diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4409d4f33afc..fcdde9c91e8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,6 +94,10 @@ jobs: CACHE_DOMAIN: ci-caches.rust-lang.org continue-on-error: ${{ matrix.continue_on_error || false }} strategy: + # If the user starts multiple jobs in a try build, let them all finish. + # Try builds are sometimes used to test several jobs at once, and it is useful to know which + # of them would succeed or not. + fail-fast: ${{ needs.calculate_matrix.outputs.run_type != 'try' }} matrix: # Check the `calculate_matrix` job to see how is the matrix defined. include: ${{ fromJSON(needs.calculate_matrix.outputs.jobs) }} diff --git a/.mailmap b/.mailmap index 948f1ab14fde..c0333b49f53b 100644 --- a/.mailmap +++ b/.mailmap @@ -83,7 +83,8 @@ Ben Sago Ben Striegel Benjamin Jackman Benoît Cortier -binarycat lolbinarycat +binarycat lolbinarycat +binarycat lolbinarycat Bheesham Persaud Bheesham Persaud bjorn3 <17426603+bjorn3@users.noreply.github.com> bjorn3 <17426603+bjorn3@users.noreply.github.com> @@ -579,9 +580,14 @@ Ralph Giles Ralph Giles Ramkumar Ramachandra Raphaël Huchet rChaser53 +Redddy +Redddy +Redddy <78539407+reddevilmidzy@users.noreply.github.com> Rémy Rakic +Rémy Rakic Rémy Rakic Rémy Rakic +Rémy Rakic Renato Riccieri Santos Zannon Richard Diamond Ricky Hosfelt diff --git a/Cargo.lock b/Cargo.lock index 4307639a5099..1e6d8f682d04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,16 +52,6 @@ dependencies = [ "libc", ] -[[package]] -name = "annotate-snippets" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e" -dependencies = [ - "unicode-width 0.1.14", - "yansi-term", -] - [[package]] name = "annotate-snippets" version = "0.11.5" @@ -630,7 +620,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "clippy" -version = "0.1.95" +version = "0.1.96" dependencies = [ "anstream", "askama", @@ -657,7 +647,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.95" +version = "0.1.96" dependencies = [ "clippy_utils", "itertools", @@ -681,7 +671,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.95" +version = "0.1.96" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -713,7 +703,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.95" +version = "0.1.96" dependencies = [ "arrayvec", "itertools", @@ -1117,7 +1107,7 @@ dependencies = [ [[package]] name = "declare_clippy_lint" -version = "0.1.95" +version = "0.1.96" [[package]] name = "derive-where" @@ -1213,16 +1203,6 @@ dependencies = [ "dirs-sys", ] -[[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" version = "0.5.0" @@ -1231,26 +1211,15 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users 0.5.2", + "redox_users", "windows-sys 0.61.2", ] -[[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 0.4.6", - "winapi", -] - [[package]] name = "dispatch2" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ "bitflags", "block2", @@ -1665,7 +1634,6 @@ dependencies = [ "allocator-api2", "equivalent", "foldhash 0.1.5", - "serde", ] [[package]] @@ -1675,6 +1643,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "foldhash 0.2.0", + "serde", + "serde_core", ] [[package]] @@ -1898,9 +1868,9 @@ dependencies = [ [[package]] name = "id-arena" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" [[package]] name = "ident_case" @@ -2652,9 +2622,9 @@ dependencies = [ [[package]] name = "objc2-core-foundation" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ "bitflags", ] @@ -2667,9 +2637,9 @@ checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] name = "objc2-io-kit" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" dependencies = [ "libc", "objc2-core-foundation", @@ -3228,17 +3198,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror 1.0.69", -] - [[package]] name = "redox_users" version = "0.5.2" @@ -3514,6 +3473,7 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_feature", + "rustc_hir", "rustc_macros", "rustc_session", "rustc_span", @@ -3546,6 +3506,7 @@ dependencies = [ "rustc_lexer", "rustc_macros", "rustc_parse", + "rustc_parse_format", "rustc_session", "rustc_span", "rustc_target", @@ -3640,7 +3601,6 @@ dependencies = [ "rustc_macros", "rustc_metadata", "rustc_middle", - "rustc_query_system", "rustc_sanitizers", "rustc_session", "rustc_span", @@ -3808,7 +3768,6 @@ name = "rustc_error_messages" version = "0.0.0" dependencies = [ "fluent-bundle", - "fluent-syntax", "icu_list", "icu_locale", "intl-memoizer", @@ -3819,7 +3778,6 @@ dependencies = [ "rustc_macros", "rustc_serialize", "rustc_span", - "tracing", "unic-langid", ] @@ -4163,6 +4121,7 @@ version = "0.0.0" dependencies = [ "cc", "libc", + "shlex", ] [[package]] @@ -4243,7 +4202,6 @@ dependencies = [ "rustc_index", "rustc_lint_defs", "rustc_macros", - "rustc_query_system", "rustc_serialize", "rustc_session", "rustc_span", @@ -4495,12 +4453,10 @@ dependencies = [ "rustc_abi", "rustc_data_structures", "rustc_errors", - "rustc_hashes", "rustc_hir", "rustc_index", "rustc_macros", "rustc_middle", - "rustc_query_system", "rustc_serialize", "rustc_session", "rustc_span", @@ -4508,23 +4464,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "rustc_query_system" -version = "0.0.0" -dependencies = [ - "rustc_abi", - "rustc_ast", - "rustc_data_structures", - "rustc_errors", - "rustc_feature", - "rustc_hir", - "rustc_macros", - "rustc_serialize", - "rustc_session", - "rustc_span", - "smallvec", -] - [[package]] name = "rustc_resolve" version = "0.0.0" @@ -4703,7 +4642,6 @@ dependencies = [ "rustc_macros", "rustc_middle", "rustc_next_trait_solver", - "rustc_parse_format", "rustc_session", "rustc_span", "rustc_transmute", @@ -4769,6 +4707,7 @@ dependencies = [ "ena", "indexmap", "rustc-hash 2.1.1", + "rustc_abi", "rustc_ast_ir", "rustc_data_structures", "rustc_error_messages", @@ -4892,9 +4831,9 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.8.0" +version = "1.9.0" dependencies = [ - "annotate-snippets 0.9.2", + "annotate-snippets 0.11.5", "anyhow", "bytecount", "cargo_metadata 0.18.1", @@ -4907,11 +4846,13 @@ dependencies = [ "itertools", "regex", "rustfmt-config_proc_macro", + "semver", "serde", "serde_json", + "tempfile", "term", "thiserror 1.0.69", - "toml 0.7.8", + "toml 0.9.8", "tracing", "tracing-subscriber", "unicode-properties", @@ -5374,9 +5315,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.38.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe840c5b1afe259a5657392a4dbb74473a14c8db999c3ec2f4ae812e028a94da" +checksum = "1efc19935b4b66baa6f654ac7924c192f55b175c00a7ab72410fc24284dacda8" dependencies = [ "libc", "objc2-core-foundation", @@ -5431,13 +5372,11 @@ dependencies = [ [[package]] name = "term" -version = "0.7.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "dirs-next", - "rustversion", - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -5772,6 +5711,16 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.20" @@ -5783,12 +5732,15 @@ dependencies = [ "once_cell", "parking_lot", "regex-automata", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] @@ -6174,9 +6126,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.20" +version = "0.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "846d20ed66ae37b7a237e36dfcd2fdc979eae82a46cdb0586f9bba80782fd789" +checksum = "59dcd765f510df84d1677a502c49057761486597a95950b4c92153e5707af091" dependencies = [ "anyhow", "clap", @@ -6185,7 +6137,7 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.243.0", + "wasmparser 0.245.1", "wat", "windows-sys 0.61.2", "winsplit", @@ -6212,24 +6164,24 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.243.0" +version = "0.245.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55db9c896d70bd9fa535ce83cd4e1f2ec3726b0edd2142079f594fc3be1cb35" +checksum = "3f9dca005e69bf015e45577e415b9af8c67e8ee3c0e38b5b0add5aa92581ed5c" dependencies = [ "leb128fmt", - "wasmparser 0.243.0", + "wasmparser 0.245.1", ] [[package]] name = "wasm-metadata" -version = "0.243.0" +version = "0.245.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae05bf9579f45a62e8d0a4e3f52eaa8da518883ac5afa482ec8256c329ecd56" +checksum = "da55e60097e8b37b475a0fa35c3420dd71d9eb7bd66109978ab55faf56a57efb" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.243.0", - "wasmparser 0.243.0", + "wasm-encoder 0.245.1", + "wasmparser 0.245.1", ] [[package]] @@ -6254,12 +6206,12 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.243.0" +version = "0.245.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d8db401b0528ec316dfbe579e6ab4152d61739cfe076706d2009127970159d" +checksum = "4f08c9adee0428b7bddf3890fc27e015ac4b761cc608c822667102b8bfd6995e" dependencies = [ "bitflags", - "hashbrown 0.15.5", + "hashbrown 0.16.1", "indexmap", "semver", "serde", @@ -6267,22 +6219,22 @@ dependencies = [ [[package]] name = "wast" -version = "243.0.0" +version = "245.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df21d01c2d91e46cb7a221d79e58a2d210ea02020d57c092e79255cc2999ca7f" +checksum = "28cf1149285569120b8ce39db8b465e8a2b55c34cbb586bd977e43e2bc7300bf" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.2", - "wasm-encoder 0.243.0", + "wasm-encoder 0.245.1", ] [[package]] name = "wat" -version = "1.243.0" +version = "1.245.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226a9a91cd80a50449312fef0c75c23478fcecfcc4092bdebe1dc8e760ef521b" +checksum = "cd48d1679b6858988cb96b154dda0ec5bbb09275b71db46057be37332d5477be" dependencies = [ "wast", ] @@ -6297,22 +6249,6 @@ dependencies = [ "wasm-bindgen", ] -[[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-util" version = "0.1.11" @@ -6322,12 +6258,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[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" version = "0.61.3" @@ -6729,9 +6659,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.243.0" +version = "0.245.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f9fc53513e461ce51dcf17a3e331752cb829f1d187069e54af5608fc998fe4" +checksum = "4894f10d2d5cbc17c77e91f86a1e48e191a788da4425293b55c98b44ba3fcac9" dependencies = [ "anyhow", "bitflags", @@ -6740,19 +6670,20 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.243.0", + "wasm-encoder 0.245.1", "wasm-metadata", - "wasmparser 0.243.0", + "wasmparser 0.245.1", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.243.0" +version = "0.245.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df983a8608e513d8997f435bb74207bf0933d0e49ca97aa9d8a6157164b9b7fc" +checksum = "330698718e82983499419494dd1e3d7811a457a9bf9f69734e8c5f07a2547929" dependencies = [ "anyhow", + "hashbrown 0.16.1", "id-arena", "indexmap", "log", @@ -6761,7 +6692,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.243.0", + "wasmparser 0.245.1", ] [[package]] @@ -6793,15 +6724,6 @@ dependencies = [ "lzma-sys", ] -[[package]] -name = "yansi-term" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1" -dependencies = [ - "winapi", -] - [[package]] name = "yoke" version = "0.8.1" diff --git a/README.md b/README.md index 611260470f12..ed35016eb3a2 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ See https://www.rust-lang.org/community for a list of chat platforms and forums. See [CONTRIBUTING.md](CONTRIBUTING.md). +For a detailed explanation of the compiler's architecture and how to begin contributing, see the [rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/). + ## License Rust is primarily distributed under the terms of both the MIT license and the diff --git a/RELEASES.md b/RELEASES.md index 29c787f4e14c..f180d740a3d1 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,118 @@ +Version 1.94.0 (2026-03-05) +========================== + + + +Language +-------- +- [Impls and impl items inherit `dead_code` lint level of the corresponding traits and trait items](https://github.com/rust-lang/rust/pull/144113) +- [Stabilize additional 29 RISC-V target features including large portions of the RVA22U64 / RVA23U64 profiles](https://github.com/rust-lang/rust/pull/145948) +- [Add warn-by-default `unused_visibilities` lint for visibility on `const _` declarations](https://github.com/rust-lang/rust/pull/147136) +- [Update to Unicode 17](https://github.com/rust-lang/rust/pull/148321) +- [Avoid incorrect lifetime errors for closures](https://github.com/rust-lang/rust/pull/148329) + + + +Platform Support +---------------- + +- [Add `riscv64im-unknown-none-elf` as a tier 3 target](https://github.com/rust-lang/rust/pull/148790) + +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 +--------- + +- [Relax `T: Ord` bound for some `BinaryHeap` methods.](https://github.com/rust-lang/rust/pull/149408) + + + +Stabilized APIs +--------------- + +- [`<[T]>::array_windows`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.array_windows) +- [`<[T]>::element_offset`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.element_offset) +- [`LazyCell::get`](https://doc.rust-lang.org/stable/std/cell/struct.LazyCell.html#method.get) +- [`LazyCell::get_mut`](https://doc.rust-lang.org/stable/std/cell/struct.LazyCell.html#method.get_mut) +- [`LazyCell::force_mut`](https://doc.rust-lang.org/stable/std/cell/struct.LazyCell.html#method.force_mut) +- [`LazyLock::get`](https://doc.rust-lang.org/stable/std/sync/struct.LazyLock.html#method.get) +- [`LazyLock::get_mut`](https://doc.rust-lang.org/stable/std/sync/struct.LazyLock.html#method.get_mut) +- [`LazyLock::force_mut`](https://doc.rust-lang.org/stable/std/sync/struct.LazyLock.html#method.force_mut) +- [`impl TryFrom for usize`](https://doc.rust-lang.org/stable/std/convert/trait.TryFrom.html#impl-TryFrom%3Cchar%3E-for-usize) +- [`std::iter::Peekable::next_if_map`](https://doc.rust-lang.org/stable/std/iter/struct.Peekable.html#method.next_if_map) +- [`std::iter::Peekable::next_if_map_mut`](https://doc.rust-lang.org/stable/std/iter/struct.Peekable.html#method.next_if_map_mut) +- [x86 `avx512fp16` intrinsics](https://github.com/rust-lang/rust/issues/127213) + (excluding those that depend directly on the unstable `f16` type) +- [AArch64 NEON fp16 intrinsics](https://github.com/rust-lang/rust/issues/136306) + (excluding those that depend directly on the unstable `f16` type) +- [`f32::consts::EULER_GAMMA`](https://doc.rust-lang.org/stable/std/f32/consts/constant.EULER_GAMMA.html) +- [`f64::consts::EULER_GAMMA`](https://doc.rust-lang.org/stable/std/f64/consts/constant.EULER_GAMMA.html) +- [`f32::consts::GOLDEN_RATIO`](https://doc.rust-lang.org/stable/std/f32/consts/constant.GOLDEN_RATIO.html) +- [`f64::consts::GOLDEN_RATIO`](https://doc.rust-lang.org/stable/std/f64/consts/constant.GOLDEN_RATIO.html) + + +These previously stable APIs are now stable in const contexts: + +- [`f32::mul_add`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.mul_add) +- [`f64::mul_add`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.mul_add) + + + + +Cargo +----- + +- Stabilize the config include key. The top-level include config key allows loading additional config files, enabling better organization, sharing, and management of Cargo configurations across projects and environments. [docs](https://doc.rust-lang.org/nightly/cargo/reference/config.html#including-extra-configuration-files) [#16284](https://github.com/rust-lang/cargo/pull/16284) +- Stabilize the pubtime field in registry index. This records when a crate version was published and enables time-based dependency resolution in the future. Note that crates.io will gradually backfill existing packages when a new version is published. Not all crates have pubtime yet. [#16369](https://github.com/rust-lang/cargo/pull/16369) [#16372](https://github.com/rust-lang/cargo/pull/16372) +- Cargo now parses [TOML v1.1](https://toml.io/en/v1.1.0) for manifests and configuration files. Note that using these features in Cargo.toml will raise your development MSRV, but the published manifest remains compatible with older parsers. [#16415](https://github.com/rust-lang/cargo/pull/16415) +- [Make `CARGO_BIN_EXE_` available at runtime ](https://github.com/rust-lang/cargo/pull/16421/) + + + +Compatibility Notes +------------------- +- [Forbid freely casting lifetime bounds of `dyn`-types](https://github.com/rust-lang/rust/pull/136776) +- [Make closure capturing have consistent and correct behaviour around patterns](https://github.com/rust-lang/rust/pull/138961) + Some finer details of how precise closure captures get affected by pattern matching have been changed. In some cases, this can cause a non-move closure that was previously capturing an entire variable by move, to now capture only part of that variable by move, and other parts by borrow. This can cause the borrow checker to complain where it previously didn't, or cause `Drop` to run at a different point in time. +- [Standard library macros are now imported via prelude, not via injected `#[macro_use]`](https://github.com/rust-lang/rust/pull/139493) + This will raise an error if macros of the same name are glob imported. + For example if a crate defines their own `matches` macro and then glob imports that, + it's now ambiguous whether the custom or standard library `matches` is meant and + an explicit import of the name is required to resolve the ambiguity. + One exception is `core::panic` and `std::panic`, if their import is ambiguous + a new warning ([`ambiguous_panic_imports`](https://github.com/rust-lang/rust/issues/147319)) is raised. + This may raise a new warning ([`ambiguous_panic_imports`](https://github.com/rust-lang/rust/issues/147319)) on `#![no_std]` code glob importing the std crate. + Both `core::panic!` and `std::panic!` are then in scope and which is used is ambiguous. +- [Don't strip shebang in expression-context `include!(…)`s](https://github.com/rust-lang/rust/pull/146377) + This can cause previously working includes to no longer compile if they included files which started with a shebang. +- [Ambiguous glob reexports are now also visible cross-crate](https://github.com/rust-lang/rust/pull/147984) + This unifies behavior between local and cross-crate errors on these exports, which may introduce new ambiguity errors. +- [Don't normalize where-clauses before checking well-formedness](https://github.com/rust-lang/rust/pull/148477) +- [Introduce a future compatibility warning on codegen attributes on body-free trait methods](https://github.com/rust-lang/rust/pull/148756) + These attributes currently have no effect in this position. +- [On Windows `std::time::SystemTime::checked_sub_duration` will return `None` for times before the Windows epoch (1/1/1601)](https://github.com/rust-lang/rust/pull/148825) +- [Lifetime identifiers such as `'a` are now NFC normalized](https://github.com/rust-lang/rust/pull/149192). +- [Overhaul filename handling for cross-compiler consistency](https://github.com/rust-lang/rust/pull/149709) + Any paths emitted by compiler now always respect the relative-ness of the paths and `--remap-path-prefix` given originally. + One side-effect of this change is that paths emitted for local crates in Cargo (path dependencies and workspace members) are no longer absolute but relative when emitted as part of a diagnostic in a downstream crate. + + + +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. + +- [Switch to `annotate-snippets` for error emission](https://github.com/rust-lang/rust/pull/150032) + This should preserve mostly the same outputs in rustc error messages. + Version 1.93.1 (2026-02-12) =========================== diff --git a/bootstrap.example.toml b/bootstrap.example.toml index e0cbb0c0e747..662bcc5d61e7 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -302,6 +302,11 @@ # If you set this, you likely want to set `cargo` as well. #build.rustc = "/path/to/rustc" +# Use this rustdoc binary as the stage0 snapshot rustdoc. +# If unspecified, then the binary "rustdoc" (with platform-specific extension, e.g. ".exe") +# in the same directory as "rustc" will be used. +#build.rustdoc = "/path/to/rustdoc" + # Instead of downloading the src/stage0 version of rustfmt specified, # use this rustfmt binary instead as the stage0 snapshot rustfmt. #build.rustfmt = "/path/to/rustfmt" diff --git a/compiler/rustc_abi/src/extern_abi/tests.rs b/compiler/rustc_abi/src/extern_abi/tests.rs index 8b9353ccae97..d84d6217f691 100644 --- a/compiler/rustc_abi/src/extern_abi/tests.rs +++ b/compiler/rustc_abi/src/extern_abi/tests.rs @@ -1,7 +1,6 @@ +use std::assert_matches; use std::str::FromStr; -use rustc_data_structures::assert_matches; - use super::*; #[allow(non_snake_case)] diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 4f1594d02a82..2351d58d8e82 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -21,7 +21,64 @@ mod ty; #[cfg(feature = "nightly")] -pub use ty::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx}; +pub use ty::{Layout, TyAbiInterface, TyAndLayout}; + +rustc_index::newtype_index! { + /// The *source-order* index of a field in a variant. + /// + /// This is how most code after type checking refers to fields, rather than + /// using names (as names have hygiene complications and more complex lookup). + /// + /// Particularly for `repr(Rust)` types, this may not be the same as *layout* order. + /// (It is for `repr(C)` `struct`s, however.) + /// + /// For example, in the following types, + /// ```rust + /// # enum Never {} + /// # #[repr(u16)] + /// enum Demo1 { + /// Variant0 { a: Never, b: i32 } = 100, + /// Variant1 { c: u8, d: u64 } = 10, + /// } + /// struct Demo2 { e: u8, f: u16, g: u8 } + /// ``` + /// `b` is `FieldIdx(1)` in `VariantIdx(0)`, + /// `d` is `FieldIdx(1)` in `VariantIdx(1)`, and + /// `f` is `FieldIdx(1)` in `VariantIdx(0)`. + #[cfg_attr(feature = "nightly", derive(rustc_macros::HashStable_Generic))] + #[encodable] + #[orderable] + #[gate_rustc_only] + pub struct FieldIdx {} +} + +impl FieldIdx { + /// The second field, at index 1. + /// + /// For use alongside [`FieldIdx::ZERO`], particularly with scalar pairs. + pub const ONE: FieldIdx = FieldIdx::from_u32(1); +} + +rustc_index::newtype_index! { + /// The *source-order* index of a variant in a type. + /// + /// For enums, these are always `0..variant_count`, regardless of any + /// custom discriminants that may have been defined, and including any + /// variants that may end up uninhabited due to field types. (Some of the + /// variants may not be present in a monomorphized ABI [`Variants`], but + /// those skipped variants are always counted when determining the *index*.) + /// + /// `struct`s, `tuples`, and `unions`s are considered to have a single variant + /// with variant index zero, aka [`FIRST_VARIANT`]. + #[cfg_attr(feature = "nightly", derive(rustc_macros::HashStable_Generic))] + #[encodable] + #[orderable] + #[gate_rustc_only] + pub struct VariantIdx { + /// Equivalent to `VariantIdx(0)`. + const FIRST_VARIANT = 0; + } +} // A variant is absent if it's uninhabited and only has ZST fields. // Present uninhabited variants only require space for their fields, @@ -291,9 +348,9 @@ pub fn layout_of_struct_or_enum< always_sized: bool, ) -> LayoutCalculatorResult { let (present_first, present_second) = { - let mut present_variants = variants - .iter_enumerated() - .filter_map(|(i, v)| if !repr.c() && absent(v) { None } else { Some(i) }); + let mut present_variants = variants.iter_enumerated().filter_map(|(i, v)| { + if !repr.inhibit_enum_layout_opt() && absent(v) { None } else { Some(i) } + }); (present_variants.next(), present_variants.next()) }; let present_first = match present_first { diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index aafb124986e1..7698a40629da 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -4,6 +4,7 @@ use rustc_data_structures::intern::Interned; use rustc_macros::HashStable_Generic; +use crate::layout::{FieldIdx, VariantIdx}; use crate::{ AbiAlign, Align, BackendRepr, FieldsShape, Float, HasDataLayout, LayoutData, Niche, PointeeInfo, Primitive, Size, Variants, @@ -11,60 +12,6 @@ // Explicitly import `Float` to avoid ambiguity with `Primitive::Float`. -rustc_index::newtype_index! { - /// The *source-order* index of a field in a variant. - /// - /// This is how most code after type checking refers to fields, rather than - /// using names (as names have hygiene complications and more complex lookup). - /// - /// Particularly for `repr(Rust)` types, this may not be the same as *layout* order. - /// (It is for `repr(C)` `struct`s, however.) - /// - /// For example, in the following types, - /// ```rust - /// # enum Never {} - /// # #[repr(u16)] - /// enum Demo1 { - /// Variant0 { a: Never, b: i32 } = 100, - /// Variant1 { c: u8, d: u64 } = 10, - /// } - /// struct Demo2 { e: u8, f: u16, g: u8 } - /// ``` - /// `b` is `FieldIdx(1)` in `VariantIdx(0)`, - /// `d` is `FieldIdx(1)` in `VariantIdx(1)`, and - /// `f` is `FieldIdx(1)` in `VariantIdx(0)`. - #[derive(HashStable_Generic)] - #[encodable] - #[orderable] - pub struct FieldIdx {} -} - -impl FieldIdx { - /// The second field, at index 1. - /// - /// For use alongside [`FieldIdx::ZERO`], particularly with scalar pairs. - pub const ONE: FieldIdx = FieldIdx::from_u32(1); -} - -rustc_index::newtype_index! { - /// The *source-order* index of a variant in a type. - /// - /// For enums, these are always `0..variant_count`, regardless of any - /// custom discriminants that may have been defined, and including any - /// variants that may end up uninhabited due to field types. (Some of the - /// variants may not be present in a monomorphized ABI [`Variants`], but - /// those skipped variants are always counted when determining the *index*.) - /// - /// `struct`s, `tuples`, and `unions`s are considered to have a single variant - /// with variant index zero, aka [`FIRST_VARIANT`]. - #[derive(HashStable_Generic)] - #[encodable] - #[orderable] - pub struct VariantIdx { - /// Equivalent to `VariantIdx(0)`. - const FIRST_VARIANT = 0; - } -} #[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)] #[rustc_pass_by_value] pub struct Layout<'a>(pub Interned<'a, LayoutData>); diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 88f8b7cc5170..ce5f257abae9 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1,5 +1,4 @@ // tidy-alphabetical-start -#![cfg_attr(all(feature = "nightly", bootstrap, test), feature(assert_matches))] #![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", feature(rustc_attrs))] #![cfg_attr(feature = "nightly", feature(step_trait))] @@ -64,9 +63,9 @@ #[cfg(feature = "nightly")] pub use extern_abi::CVariadicStatus; pub use extern_abi::{ExternAbi, all_names}; +pub use layout::{FIRST_VARIANT, FieldIdx, LayoutCalculator, LayoutCalculatorError, VariantIdx}; #[cfg(feature = "nightly")] -pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx}; -pub use layout::{LayoutCalculator, LayoutCalculatorError}; +pub use layout::{Layout, TyAbiInterface, TyAndLayout}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro @@ -1036,6 +1035,19 @@ impl Align { // LLVM has a maximal supported alignment of 2^29, we inherit that. pub const MAX: Align = Align { pow2: 29 }; + /// Either `1 << (pointer_bits - 1)` or [`Align::MAX`], whichever is smaller. + #[inline] + pub fn max_for_target(tdl: &TargetDataLayout) -> Align { + let pointer_bits = tdl.pointer_size().bits(); + if let Ok(pointer_bits) = u8::try_from(pointer_bits) + && pointer_bits <= Align::MAX.pow2 + { + Align { pow2: pointer_bits - 1 } + } else { + Align::MAX + } + } + #[inline] pub fn from_bits(bits: u64) -> Result { Align::from_bytes(Size::from_bits(bits).bytes()) @@ -2132,21 +2144,22 @@ pub enum PointerKind { } /// Encodes extra information we have about a pointer. +/// /// Note that this information is advisory only, and backends are free to ignore it: /// if the information is wrong, that can cause UB, but if the information is absent, /// that must always be okay. #[derive(Copy, Clone, Debug)] pub struct PointeeInfo { - /// If this is `None`, then this is a raw pointer, so size and alignment are not guaranteed to - /// be reliable. + /// If this is `None`, then this is a raw pointer. pub safe: Option, - /// If `safe` is `Some`, then the pointer is either null or dereferenceable for this many bytes. + /// 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. - /// The size can be zero if the pointer is not dereferenceable. pub size: Size, - /// If `safe` is `Some`, then the pointer is aligned as indicated. + /// The pointer is guaranteed to be aligned this much (independent of `safe`). pub align: Align, } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fa323b7cf581..5258f179d95d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1724,6 +1724,12 @@ pub enum StructRest { Rest(Span), /// No trailing `..` or expression. None, + /// No trailing `..` or expression, and also, a parse error occurred inside the struct braces. + /// + /// This struct should be treated similarly to as if it had an `..` in it, + /// in particular rather than reporting missing fields, because the parse error + /// makes which fields the struct was intended to have not fully known. + NoneWithError(ErrorGuaranteed), } #[derive(Clone, Encodable, Decodable, Debug, Walkable)] @@ -2553,6 +2559,11 @@ pub enum TyKind { /// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZero`, /// just as part of the type system. Pat(Box, Box), + /// A `field_of` expression (e.g., `builtin # field_of(Struct, field)`). + /// + /// Usually not written directly in user code but indirectly via the macro + /// `core::field::field_of!(...)`. + FieldOf(Box, Option, Ident), /// Sometimes we need a dummy value when no error has occurred. Dummy, /// Placeholder for a kind that has failed to be defined. @@ -3131,8 +3142,16 @@ pub enum Const { /// For details see the [RFC #2532](https://github.com/rust-lang/rfcs/pull/2532). #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)] pub enum Defaultness { + /// Item is unmarked. Implicitly determined based off of position. + /// For impls, this is `final`; for traits, this is `default`. + /// + /// If you're expanding an item in a built-in macro or parsing an item + /// by hand, you probably want to use this. + Implicit, + /// `default` Default(Span), - Final, + /// `final`; per RFC 3678, only trait items may be *explicitly* marked final. + Final(Span), } #[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)] @@ -3534,6 +3553,19 @@ pub fn is_pub(&self) -> bool { } } +#[derive(Clone, Encodable, Decodable, Debug, Walkable)] +pub struct ImplRestriction { + pub kind: RestrictionKind, + pub span: Span, + pub tokens: Option, +} + +#[derive(Clone, Encodable, Decodable, Debug, Walkable)] +pub enum RestrictionKind { + Unrestricted, + Restricted { path: Box, id: NodeId, shorthand: bool }, +} + /// Field definition in a struct, variant or union. /// /// E.g., `bar: usize` as in `struct Foo { bar: usize }`. @@ -3733,6 +3765,7 @@ pub struct Trait { pub constness: Const, pub safety: Safety, pub is_auto: IsAuto, + pub impl_restriction: ImplRestriction, pub ident: Ident, pub generics: Generics, #[visitable(extra = BoundKind::SuperTraits)] @@ -4140,7 +4173,7 @@ pub fn defaultness(&self) -> Defaultness { | Self::Fn(box Fn { defaultness, .. }) | Self::Type(box TyAlias { defaultness, .. }) => defaultness, Self::MacCall(..) | Self::Delegation(..) | Self::DelegationMac(..) => { - Defaultness::Final + Defaultness::Implicit } } } @@ -4203,9 +4236,7 @@ pub fn ident(&self) -> Option { impl From for ItemKind { fn from(foreign_item_kind: ForeignItemKind) -> ItemKind { match foreign_item_kind { - ForeignItemKind::Static(box static_foreign_item) => { - ItemKind::Static(Box::new(static_foreign_item)) - } + ForeignItemKind::Static(static_foreign_item) => ItemKind::Static(static_foreign_item), ForeignItemKind::Fn(fn_kind) => ItemKind::Fn(fn_kind), ForeignItemKind::TyAlias(ty_alias_kind) => ItemKind::TyAlias(ty_alias_kind), ForeignItemKind::MacCall(a) => ItemKind::MacCall(a), @@ -4218,7 +4249,7 @@ impl TryFrom for ForeignItemKind { fn try_from(item_kind: ItemKind) -> Result { Ok(match item_kind { - ItemKind::Static(box static_item) => ForeignItemKind::Static(Box::new(static_item)), + ItemKind::Static(static_item) => ForeignItemKind::Static(static_item), ItemKind::Fn(fn_kind) => ForeignItemKind::Fn(fn_kind), ItemKind::TyAlias(ty_alias_kind) => ForeignItemKind::TyAlias(ty_alias_kind), ItemKind::MacCall(a) => ForeignItemKind::MacCall(a), diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 3d2477e5f033..73bfa0ba7ab6 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -8,8 +8,8 @@ use crate::tokenstream::LazyAttrTokenStream; use crate::{ Arm, AssocItem, AttrItem, AttrKind, AttrVec, Attribute, Block, Crate, Expr, ExprField, - FieldDef, ForeignItem, GenericParam, Item, NodeId, Param, Pat, PatField, Path, Stmt, StmtKind, - Ty, Variant, Visibility, WherePredicate, + FieldDef, ForeignItem, GenericParam, ImplRestriction, Item, NodeId, Param, Pat, PatField, Path, + Stmt, StmtKind, Ty, Variant, Visibility, WherePredicate, }; /// A trait for AST nodes having an ID. @@ -97,7 +97,19 @@ fn tokens_mut(&mut self) -> Option<&mut Option> { }; } -impl_has_tokens!(AssocItem, AttrItem, Block, Expr, ForeignItem, Item, Pat, Path, Ty, Visibility); +impl_has_tokens!( + AssocItem, + AttrItem, + Block, + Expr, + ForeignItem, + Item, + Pat, + Path, + Ty, + Visibility, + ImplRestriction +); impl_has_tokens_none!( Arm, ExprField, @@ -242,7 +254,7 @@ fn visit_attrs(&mut self, _f: impl FnOnce(&mut AttrVec)) {} Variant, WherePredicate, ); -impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility); +impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility, ImplRestriction); impl HasAttrs for Box { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index b9411a3269a5..72e7b27a1f97 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -296,6 +296,10 @@ fn is_doc_keyword_or_attribute(&self) -> bool { } false } + + fn is_rustc_doc_primitive(&self) -> bool { + self.has_name(sym::rustc_doc_primitive) + } } impl Attribute { @@ -935,6 +939,9 @@ fn is_proc_macro_attr(&self) -> bool { /// Returns `true` is this attribute contains `doc(keyword)` or `doc(attribute)`. fn is_doc_keyword_or_attribute(&self) -> bool; + + /// Returns `true` if this is a `#[rustc_doc_primitive]` attribute. + fn is_rustc_doc_primitive(&self) -> bool; } // FIXME(fn_delegation): use function delegation instead of manually forwarding diff --git a/compiler/rustc_ast/src/expand/autodiff_attrs.rs b/compiler/rustc_ast/src/expand/autodiff_attrs.rs index 90f15753e99c..a3c913436ac7 100644 --- a/compiler/rustc_ast/src/expand/autodiff_attrs.rs +++ b/compiler/rustc_ast/src/expand/autodiff_attrs.rs @@ -1,12 +1,13 @@ //! This crate handles the user facing autodiff macro. For each `#[autodiff(...)]` attribute, -//! we create an [`AutoDiffItem`] which contains the source and target function names. The source +//! we create an `RustcAutodiff` which contains the source and target function names. The source //! is the function to which the autodiff attribute is applied, and the target is the function //! getting generated by us (with a name given by the user as the first autodiff arg). use std::fmt::{self, Display, Formatter}; use std::str::FromStr; -use crate::expand::typetree::TypeTree; +use rustc_span::{Symbol, sym}; + use crate::expand::{Decodable, Encodable, HashStable_Generic}; use crate::{Ty, TyKind}; @@ -31,6 +32,12 @@ pub enum DiffMode { Reverse, } +impl DiffMode { + pub fn all_modes() -> &'static [Symbol] { + &[sym::Source, sym::Forward, sym::Reverse] + } +} + /// Dual and Duplicated (and their Only variants) are getting lowered to the same Enzyme Activity. /// However, under forward mode we overwrite the previous shadow value, while for reverse mode /// we add to the previous shadow value. To not surprise users, we picked different names. @@ -76,43 +83,20 @@ pub fn is_dual_or_const(&self) -> bool { use DiffActivity::*; matches!(self, |Dual| DualOnly | Dualv | DualvOnly | Const) } -} -/// We generate one of these structs for each `#[autodiff(...)]` attribute. -#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] -pub struct AutoDiffItem { - /// The name of the function getting differentiated - pub source: String, - /// The name of the function being generated - pub target: String, - pub attrs: AutoDiffAttrs, - pub inputs: Vec, - pub output: TypeTree, -} -#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] -pub struct AutoDiffAttrs { - /// Conceptually either forward or reverse mode AD, as described in various autodiff papers and - /// e.g. in the [JAX - /// Documentation](https://jax.readthedocs.io/en/latest/_tutorials/advanced-autodiff.html#how-it-s-made-two-foundational-autodiff-functions). - pub mode: DiffMode, - /// A user-provided, batching width. If not given, we will default to 1 (no batching). - /// Calling a differentiated, non-batched function through a loop 100 times is equivalent to: - /// - Calling the function 50 times with a batch size of 2 - /// - Calling the function 25 times with a batch size of 4, - /// etc. A batched function takes more (or longer) arguments, and might be able to benefit from - /// cache locality, better re-usal of primal values, and other optimizations. - /// We will (before LLVM's vectorizer runs) just generate most LLVM-IR instructions `width` - /// times, so this massively increases code size. As such, values like 1024 are unlikely to - /// work. We should consider limiting this to u8 or u16, but will leave it at u32 for - /// experiments for now and focus on documenting the implications of a large width. - pub width: u32, - pub ret_activity: DiffActivity, - pub input_activity: Vec, -} - -impl AutoDiffAttrs { - pub fn has_primal_ret(&self) -> bool { - matches!(self.ret_activity, DiffActivity::Active | DiffActivity::Dual) + pub fn all_activities() -> &'static [Symbol] { + &[ + sym::None, + sym::Active, + sym::ActiveOnly, + sym::Const, + sym::Dual, + sym::Dualv, + sym::DualOnly, + sym::DualvOnly, + sym::Duplicated, + sym::DuplicatedOnly, + ] } } @@ -241,59 +225,3 @@ fn from_str(s: &str) -> Result { } } } - -impl AutoDiffAttrs { - pub fn has_ret_activity(&self) -> bool { - self.ret_activity != DiffActivity::None - } - pub fn has_active_only_ret(&self) -> bool { - self.ret_activity == DiffActivity::ActiveOnly - } - - pub const fn error() -> Self { - AutoDiffAttrs { - mode: DiffMode::Error, - width: 0, - ret_activity: DiffActivity::None, - input_activity: Vec::new(), - } - } - pub fn source() -> Self { - AutoDiffAttrs { - mode: DiffMode::Source, - width: 0, - ret_activity: DiffActivity::None, - input_activity: Vec::new(), - } - } - - pub fn is_active(&self) -> bool { - self.mode != DiffMode::Error - } - - pub fn is_source(&self) -> bool { - self.mode == DiffMode::Source - } - pub fn apply_autodiff(&self) -> bool { - !matches!(self.mode, DiffMode::Error | DiffMode::Source) - } - - pub fn into_item( - self, - source: String, - target: String, - inputs: Vec, - output: TypeTree, - ) -> AutoDiffItem { - AutoDiffItem { source, target, inputs, output, attrs: self } - } -} - -impl fmt::Display for AutoDiffItem { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Differentiating {} -> {}", self.source, self.target)?; - write!(f, " with attributes: {:?}", self.attrs)?; - write!(f, " with inputs: {:?}", self.inputs)?; - write!(f, " with output: {:?}", self.output) - } -} diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 0e3ab0a4a4c3..ac3e77b0b5d6 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -8,7 +8,6 @@ #![doc(test(attr(deny(warnings), allow(internal_features))))] #![feature(associated_type_defaults)] #![feature(box_patterns)] -#![feature(if_let_guard)] #![feature(iter_order_by)] #![feature(macro_metavar_expr)] #![recursion_limit = "256"] diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 2e494b968b6b..43ef6897b79c 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -298,6 +298,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { | ast::TyKind::ImplicitSelf | ast::TyKind::CVarArgs | ast::TyKind::Pat(..) + | ast::TyKind::FieldOf(..) | ast::TyKind::Dummy | ast::TyKind::Err(..) => break None, } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 8556e8288670..c3c1c518d849 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -467,6 +467,7 @@ pub fn ctxt(&self) -> Option { RangeEnd, RangeSyntax, Recovered, + RestrictionKind, Safety, StaticItem, StrLit, @@ -595,6 +596,7 @@ fn visit_ident(&mut self, Ident { name: _, span }: &$($lt)? $($mut)? Ident) -> S fn visit_poly_trait_ref(PolyTraitRef); fn visit_precise_capturing_arg(PreciseCapturingArg); fn visit_qself(QSelf); + fn visit_impl_restriction(ImplRestriction); fn visit_trait_ref(TraitRef); fn visit_ty_pat(TyPat); fn visit_ty(Ty); @@ -757,9 +759,10 @@ fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( ) -> V::Result; } - // this is only used by the MutVisitor. We include this symmetry here to make writing other functions easier + // This is only used by the MutVisitor. We include this symmetry here to make writing other + // functions easier. $(${ignore($lt)} - #[expect(unused, rustc::pass_by_value)] + #[expect(unused, rustc::disallowed_pass_by_ref)] #[inline] )? fn visit_span<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, span: &$($lt)? $($mut)? Span) -> V::Result { @@ -1115,6 +1118,7 @@ pub fn walk_fn<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, kind: FnKind<$($lt)? pub fn walk_poly_trait_ref(PolyTraitRef); pub fn walk_precise_capturing_arg(PreciseCapturingArg); pub fn walk_qself(QSelf); + pub fn walk_impl_restriction(ImplRestriction); pub fn walk_trait_ref(TraitRef); pub fn walk_ty_pat(TyPat); pub fn walk_ty(Ty); diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index cccfb112ec2b..68b76f7bd3dd 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -42,11 +42,12 @@ use hir::def::{DefKind, PartialRes, Res}; use hir::{BodyId, HirId}; use rustc_abi::ExternAbi; +use rustc_ast as ast; use rustc_ast::*; use rustc_attr_parsing::{AttributeParser, ShouldEmit}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::ErrorGuaranteed; -use rustc_hir::Target; +use rustc_hir as hir; use rustc_hir::attrs::{AttributeKind, InlineAttr}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::span_bug; @@ -54,11 +55,15 @@ use rustc_span::symbol::kw; use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; use smallvec::SmallVec; -use {rustc_ast as ast, rustc_hir as hir}; -use super::{GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode}; +use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults}; use crate::errors::{CycleInDelegationSignatureResolution, UnresolvedDelegationCallee}; -use crate::{AllowReturnTypeNotation, ImplTraitPosition, ResolverAstLoweringExt}; +use crate::{ + AllowReturnTypeNotation, GenericArgsMode, ImplTraitContext, ImplTraitPosition, LoweringContext, + ParamMode, ResolverAstLoweringExt, +}; + +mod generics; pub(crate) struct DelegationResults<'hir> { pub body_id: hir::BodyId, @@ -184,18 +189,48 @@ pub(crate) fn lower_delegation( // we need a function to extract this information let (param_count, c_variadic) = self.param_count(root_function_id); + let mut generics = self.lower_delegation_generics( + delegation, + ids.root_function_id(), + item_id, + span, + ); + + let body_id = self.lower_delegation_body( + delegation, + item_id, + is_method, + param_count, + &mut generics, + span, + ); + // Here we use `delegee_id`, as this id will then be used to calculate parent for generics // inheritance, and we want this id to point on a delegee, not on the original // function (see https://github.com/rust-lang/rust/issues/150152#issuecomment-3674834654) - let decl = self.lower_delegation_decl(delegee_id, param_count, c_variadic, span); + let decl = self.lower_delegation_decl( + delegee_id, + param_count, + c_variadic, + span, + &generics, + ); // Here we pass `root_function_id` as we want to inherit signature (including consts, async) // from the root function that started delegation let sig = self.lower_delegation_sig(root_function_id, decl, span); - - let body_id = self.lower_delegation_body(delegation, is_method, param_count, span); let ident = self.lower_ident(delegation.ident); - let generics = self.lower_delegation_generics(span); + + let generics = self.arena.alloc(hir::Generics { + has_where_clause_predicates: false, + params: self.arena.alloc_from_iter(generics.all_params(item_id, span, self)), + predicates: self + .arena + .alloc_from_iter(generics.all_predicates(item_id, span, self)), + span, + where_clause_span: span, + }); + DelegationResults { body_id, sig, ident, generics } } Err(err) => self.generate_delegation_error(err, span, delegation), @@ -264,11 +299,14 @@ fn create_new_attrs( .flatten() }) .flatten(), - None => self - .tcx - .get_all_attrs(*def_id) - .iter() - .find(|base_attr| (addition_info.equals)(base_attr)), + None => + { + #[allow(deprecated)] + self.tcx + .get_all_attrs(*def_id) + .iter() + .find(|base_attr| (addition_info.equals)(base_attr)) + } }; if let Some(original_attr) = original_attr { @@ -292,7 +330,7 @@ fn parse_local_original_attrs(&self, def_id: DefId) -> Option Option { self.resolver.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id()) } - fn lower_delegation_generics(&mut self, span: Span) -> &'hir hir::Generics<'hir> { - self.arena.alloc(hir::Generics { - params: &[], - predicates: &[], - has_where_clause_predicates: false, - where_clause_span: span, - span, - }) - } - // Function parameter count, including C variadic `...` if present. fn param_count(&self, def_id: DefId) -> (usize, bool /*c_variadic*/) { if let Some(local_sig_id) = def_id.as_local() { @@ -390,6 +418,7 @@ fn lower_delegation_decl( param_count: usize, c_variadic: bool, span: Span, + generics: &GenericsGenerationResults<'hir>, ) -> &'hir hir::FnDecl<'hir> { // The last parameter in C variadic functions is skipped in the signature, // like during regular lowering. @@ -402,7 +431,13 @@ fn lower_delegation_decl( let output = self.arena.alloc(hir::Ty { hir_id: self.next_id(), - kind: hir::TyKind::InferDelegation(sig_id, hir::InferDelegationKind::Output), + kind: hir::TyKind::InferDelegation( + sig_id, + hir::InferDelegationKind::Output(self.arena.alloc(hir::DelegationGenerics { + child_args_segment_id: generics.child.args_segment_id, + parent_args_segment_id: generics.parent.args_segment_id, + })), + ), span, }); @@ -457,6 +492,7 @@ fn lower_delegation_sig( abi: sig.abi, } }; + hir::FnSig { decl, header, span } } @@ -498,6 +534,7 @@ fn generate_arg( } else { Symbol::intern(&format!("arg{idx}")) }; + let segments = self.arena.alloc_from_iter(iter::once(hir::PathSegment { ident: Ident::with_dummy_span(name), hir_id: self.next_id(), @@ -513,8 +550,10 @@ fn generate_arg( fn lower_delegation_body( &mut self, delegation: &Delegation, + item_id: NodeId, is_method: bool, param_count: usize, + generics: &mut GenericsGenerationResults<'hir>, span: Span, ) -> BodyId { let block = delegation.body.as_deref(); @@ -545,7 +584,8 @@ fn lower_delegation_body( args.push(arg); } - let final_expr = this.finalize_body_lowering(delegation, args, span); + let final_expr = this.finalize_body_lowering(delegation, item_id, args, generics, span); + (this.arena.alloc_from_iter(parameters), final_expr) }) } @@ -581,7 +621,9 @@ fn lower_target_expr(&mut self, block: &Block) -> hir::Expr<'hir> { fn finalize_body_lowering( &mut self, delegation: &Delegation, + item_id: NodeId, args: Vec>, + generics: &mut GenericsGenerationResults<'hir>, span: Span, ) -> hir::Expr<'hir> { let args = self.arena.alloc_from_iter(args); @@ -606,6 +648,10 @@ fn finalize_body_lowering( ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); + + // FIXME(fn_delegation): proper support for parent generics propagation + // in method call scenario. + let segment = self.process_segment(item_id, span, &segment, &mut generics.child, false); let segment = self.arena.alloc(segment); self.arena.alloc(hir::Expr { @@ -624,9 +670,41 @@ fn finalize_body_lowering( None, ); - let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span)); + let new_path = match path { + hir::QPath::Resolved(ty, path) => { + let mut new_path = path.clone(); + let len = new_path.segments.len(); + + new_path.segments = self.arena.alloc_from_iter( + new_path.segments.iter().enumerate().map(|(idx, segment)| { + let mut process_segment = |result, add_lifetimes| { + self.process_segment(item_id, span, segment, result, add_lifetimes) + }; + + if idx + 2 == len { + process_segment(&mut generics.parent, true) + } else if idx + 1 == len { + process_segment(&mut generics.child, false) + } else { + segment.clone() + } + }), + ); + + hir::QPath::Resolved(ty, self.arena.alloc(new_path)) + } + hir::QPath::TypeRelative(ty, segment) => { + let segment = + self.process_segment(item_id, span, segment, &mut generics.child, false); + + hir::QPath::TypeRelative(ty, self.arena.alloc(segment)) + } + }; + + let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(new_path), span)); self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span)) }; + let block = self.arena.alloc(hir::Block { stmts: &[], expr: Some(call), @@ -639,14 +717,40 @@ fn finalize_body_lowering( self.mk_expr(hir::ExprKind::Block(block, None), span) } + fn process_segment( + &mut self, + item_id: NodeId, + span: Span, + segment: &hir::PathSegment<'hir>, + result: &mut GenericsGenerationResult<'hir>, + add_lifetimes: bool, + ) -> hir::PathSegment<'hir> { + // The first condition is needed when there is SelfAndUserSpecified case, + // we don't want to propagate generics params in this situation. + let segment = if !result.generics.is_user_specified() + && let Some(args) = result + .generics + .into_hir_generics(self, item_id, span) + .into_generic_args(self, add_lifetimes, span) + { + hir::PathSegment { args: Some(args), ..segment.clone() } + } else { + segment.clone() + }; + + if result.generics.is_user_specified() { + result.args_segment_id = Some(segment.hir_id); + } + + segment + } + fn generate_delegation_error( &mut self, err: ErrorGuaranteed, span: Span, delegation: &Delegation, ) -> DelegationResults<'hir> { - let generics = self.lower_delegation_generics(span); - let decl = self.arena.alloc(hir::FnDecl { inputs: &[], output: hir::FnRetTy::DefaultReturn(span), @@ -693,6 +797,7 @@ fn generate_delegation_error( (&[], this.mk_expr(body_expr, span)) }); + let generics = hir::Generics::empty(); DelegationResults { ident, generics, body_id, sig } } @@ -711,13 +816,13 @@ fn mk_expr(&mut self, kind: hir::ExprKind<'hir>, span: Span) -> hir::Expr<'hir> } } -struct SelfResolver<'a> { - resolver: &'a mut ResolverAstLowering, +struct SelfResolver<'a, 'tcx> { + resolver: &'a mut ResolverAstLowering<'tcx>, path_id: NodeId, self_param_id: NodeId, } -impl<'a> SelfResolver<'a> { +impl SelfResolver<'_, '_> { fn try_replace_id(&mut self, id: NodeId) { if let Some(res) = self.resolver.partial_res_map.get(&id) && let Some(Res::Local(sig_id)) = res.full_res() @@ -729,7 +834,7 @@ fn try_replace_id(&mut self, id: NodeId) { } } -impl<'ast, 'a> Visitor<'ast> for SelfResolver<'a> { +impl<'ast, 'a> Visitor<'ast> for SelfResolver<'a, '_> { fn visit_id(&mut self, id: NodeId) { self.try_replace_id(id); } diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs new file mode 100644 index 000000000000..9e7ec04d38fb --- /dev/null +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -0,0 +1,578 @@ +use hir::HirId; +use hir::def::{DefKind, Res}; +use rustc_ast::*; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::GenericParamDefKind; +use rustc_middle::{bug, ty}; +use rustc_span::sym::{self}; +use rustc_span::symbol::kw; +use rustc_span::{DUMMY_SP, Ident, Span}; +use thin_vec::{ThinVec, thin_vec}; + +use crate::{AstOwner, LoweringContext}; + +pub(super) enum DelegationGenerics { + /// User-specified args are present: `reuse foo::;`. + UserSpecified, + /// The default case when no user-specified args are present: `reuse Trait::foo;`. + Default(Option), + /// In free-to-trait reuse, when user specified args for trait `reuse Trait::::foo;` + /// in this case we need to both generate `Self` and process user args. + SelfAndUserSpecified(Option), +} + +/// Used for storing either AST generics or their lowered HIR version. Firstly we obtain +/// AST generics either from local function from AST index or from external function +/// through `tcx`. Next, at some point of generics processing we need to lower those +/// generics to HIR, for this purpose we use `into_hir_generics` that lowers AST generics +/// and replaces Ast variant with Hir. Such approach is useful as we can call this method +/// at any time knowing that lowering will occur at most only once. Then, in order to obtain generic +/// params or args we use `hir_generics_or_empty` or `into_generic_args` functions. +/// There also may be situations when we obtained AST generics but never lowered them to HIR, +/// meaning we did not propagate them and thus we do not need to generate generic params +/// (i.e., method call scenarios), in such a case this approach helps +/// a lot as if `into_hir_generics` will not be called then lowering will not happen. +pub(super) enum HirOrAstGenerics<'hir> { + Ast(DelegationGenerics), + Hir(DelegationGenerics<&'hir hir::Generics<'hir>>), +} + +pub(super) struct GenericsGenerationResult<'hir> { + pub(super) generics: HirOrAstGenerics<'hir>, + pub(super) args_segment_id: Option, +} + +pub(super) struct GenericsGenerationResults<'hir> { + pub(super) parent: GenericsGenerationResult<'hir>, + pub(super) child: GenericsGenerationResult<'hir>, +} + +impl DelegationGenerics { + fn is_user_specified(&self) -> bool { + matches!( + self, + DelegationGenerics::UserSpecified | DelegationGenerics::SelfAndUserSpecified { .. } + ) + } +} + +impl<'hir> HirOrAstGenerics<'hir> { + pub(super) fn into_hir_generics( + &mut self, + ctx: &mut LoweringContext<'_, 'hir>, + item_id: NodeId, + span: Span, + ) -> &mut HirOrAstGenerics<'hir> { + if let HirOrAstGenerics::Ast(generics) = self { + let process_params = |generics: &mut Generics| { + ctx.lower_delegation_generic_params(item_id, span, &mut generics.params) + }; + + let hir_generics = match generics { + DelegationGenerics::UserSpecified => DelegationGenerics::UserSpecified, + DelegationGenerics::Default(generics) => { + DelegationGenerics::Default(generics.as_mut().map(process_params)) + } + DelegationGenerics::SelfAndUserSpecified(generics) => { + DelegationGenerics::SelfAndUserSpecified(generics.as_mut().map(process_params)) + } + }; + + *self = HirOrAstGenerics::Hir(hir_generics); + } + + self + } + + fn hir_generics_or_empty(&self) -> &'hir hir::Generics<'hir> { + match self { + HirOrAstGenerics::Ast(_) => hir::Generics::empty(), + HirOrAstGenerics::Hir(hir_generics) => match hir_generics { + DelegationGenerics::UserSpecified => hir::Generics::empty(), + DelegationGenerics::Default(generics) + | DelegationGenerics::SelfAndUserSpecified(generics) => { + generics.unwrap_or(hir::Generics::empty()) + } + }, + } + } + + pub(super) fn into_generic_args( + &self, + ctx: &mut LoweringContext<'_, 'hir>, + add_lifetimes: bool, + span: Span, + ) -> Option<&'hir hir::GenericArgs<'hir>> { + match self { + HirOrAstGenerics::Ast(_) => { + bug!("Attempting to get generic args before lowering to HIR") + } + HirOrAstGenerics::Hir(hir_generics) => match hir_generics { + DelegationGenerics::UserSpecified => None, + DelegationGenerics::Default(generics) + | DelegationGenerics::SelfAndUserSpecified(generics) => generics.map(|generics| { + ctx.create_generics_args_from_params(generics.params, add_lifetimes, span) + }), + }, + } + } + + pub(super) fn is_user_specified(&self) -> bool { + match self { + HirOrAstGenerics::Ast(ast_generics) => ast_generics.is_user_specified(), + HirOrAstGenerics::Hir(hir_generics) => hir_generics.is_user_specified(), + } + } +} + +impl<'a> GenericsGenerationResult<'a> { + fn new(generics: DelegationGenerics) -> GenericsGenerationResult<'a> { + GenericsGenerationResult { + generics: HirOrAstGenerics::Ast(generics), + args_segment_id: None, + } + } +} + +impl<'hir> GenericsGenerationResults<'hir> { + pub(super) fn all_params( + &mut self, + item_id: NodeId, + span: Span, + ctx: &mut LoweringContext<'_, 'hir>, + ) -> impl Iterator> { + // Now we always call `into_hir_generics` both on child and parent, + // however in future we would not do that, when scenarios like + // method call will be supported (if HIR generics were not obtained + // then it means that we did not propagated them, thus we do not need + // to generate params). + let parent = self + .parent + .generics + .into_hir_generics(ctx, item_id, span) + .hir_generics_or_empty() + .params; + + let child = self + .child + .generics + .into_hir_generics(ctx, item_id, span) + .hir_generics_or_empty() + .params; + + // Order generics, firstly we have parent and child lifetimes, + // then parent and child types and consts. + // `generics_of` in `rustc_hir_analysis` will order them anyway, + // however we want the order to be consistent in HIR too. + parent + .iter() + .filter(|p| p.is_lifetime()) + .chain(child.iter().filter(|p| p.is_lifetime())) + .chain(parent.iter().filter(|p| !p.is_lifetime())) + .chain(child.iter().filter(|p| !p.is_lifetime())) + .copied() + } + + /// As we add hack predicates(`'a: 'a`) for all lifetimes (see `lower_delegation_generic_params` + /// and `generate_lifetime_predicate` functions) we need to add them to delegation generics. + /// Those predicates will not affect resulting predicate inheritance and folding + /// in `rustc_hir_analysis`, as we inherit all predicates from delegation signature. + pub(super) fn all_predicates( + &mut self, + item_id: NodeId, + span: Span, + ctx: &mut LoweringContext<'_, 'hir>, + ) -> impl Iterator> { + // Now we always call `into_hir_generics` both on child and parent, + // however in future we would not do that, when scenarios like + // method call will be supported (if HIR generics were not obtained + // then it means that we did not propagated them, thus we do not need + // to generate predicates). + self.parent + .generics + .into_hir_generics(ctx, item_id, span) + .hir_generics_or_empty() + .predicates + .into_iter() + .chain( + self.child + .generics + .into_hir_generics(ctx, item_id, span) + .hir_generics_or_empty() + .predicates + .into_iter(), + ) + .copied() + } +} + +impl<'hir> LoweringContext<'_, 'hir> { + pub(super) fn lower_delegation_generics( + &mut self, + delegation: &Delegation, + root_fn_id: DefId, + item_id: NodeId, + span: Span, + ) -> GenericsGenerationResults<'hir> { + let delegation_in_free_ctx = !matches!( + self.tcx.def_kind(self.tcx.local_parent(self.local_def_id(item_id))), + DefKind::Trait | DefKind::Impl { .. } + ); + + let root_function_in_trait = + matches!(self.tcx.def_kind(self.tcx.parent(root_fn_id)), DefKind::Trait); + + let generate_self = delegation_in_free_ctx && root_function_in_trait; + + let parent_generics_factory = |this: &mut Self, user_specified: bool| { + this.get_parent_generics( + this.tcx.parent(root_fn_id), + generate_self, + user_specified, + span, + ) + }; + + let segments = &delegation.path.segments; + let len = segments.len(); + + let can_add_generics_to_parent = len >= 2 + && self.get_resolution_id(segments[len - 2].id).is_some_and(|def_id| { + matches!(self.tcx.def_kind(def_id), DefKind::Trait | DefKind::TraitAlias) + }); + + let parent_generics = if can_add_generics_to_parent { + if segments[len - 2].args.is_some() { + if generate_self { + DelegationGenerics::SelfAndUserSpecified(parent_generics_factory(self, true)) + } else { + DelegationGenerics::UserSpecified + } + } else { + DelegationGenerics::Default(parent_generics_factory(self, false)) + } + } else { + DelegationGenerics::Default(None) + }; + + let child_generics = if segments[len - 1].args.is_some() { + DelegationGenerics::UserSpecified + } else { + DelegationGenerics::Default(self.get_fn_like_generics(root_fn_id, span)) + }; + + GenericsGenerationResults { + parent: GenericsGenerationResult::new(parent_generics), + child: GenericsGenerationResult::new(child_generics), + } + } + + fn lower_delegation_generic_params( + &mut self, + item_id: NodeId, + span: Span, + params: &mut ThinVec, + ) -> &'hir hir::Generics<'hir> { + for p in params.iter_mut() { + // We want to create completely new params, so we generate + // a new id, otherwise assertions will be triggered. + p.id = self.next_node_id(); + + // Remove default params, as they are not supported on functions + // and there will duplicate DefId when we try to lower them later. + match &mut p.kind { + GenericParamKind::Lifetime => {} + GenericParamKind::Type { default } => *default = None, + GenericParamKind::Const { default, .. } => *default = None, + } + + // Note that we use self.disambiguator here, if we will create new every time + // we will get ICE if params have the same name. + self.resolver.node_id_to_def_id.insert( + p.id, + self.tcx + .create_def( + self.resolver.node_id_to_def_id[&item_id], + Some(p.ident.name), + match p.kind { + GenericParamKind::Lifetime => DefKind::LifetimeParam, + GenericParamKind::Type { .. } => DefKind::TyParam, + GenericParamKind::Const { .. } => DefKind::ConstParam, + }, + None, + &mut self.disambiguator, + ) + .def_id(), + ); + } + + // Fallback to default generic param lowering, we modified them in the loop above. + let params = self.arena.alloc_from_iter( + params.iter().map(|p| self.lower_generic_param(p, hir::GenericParamSource::Generics)), + ); + + // HACK: for now we generate predicates such that all lifetimes are early bound, + // we can not not generate early-bound lifetimes, but we can't know which of them + // are late-bound at this level of compilation. + // FIXME(fn_delegation): proper support for late bound lifetimes. + self.arena.alloc(hir::Generics { + params, + predicates: self.arena.alloc_from_iter( + params + .iter() + .filter_map(|p| p.is_lifetime().then(|| self.generate_lifetime_predicate(p))), + ), + has_where_clause_predicates: false, + where_clause_span: span, + span, + }) + } + + fn generate_lifetime_predicate( + &mut self, + p: &hir::GenericParam<'hir>, + ) -> hir::WherePredicate<'hir> { + let create_lifetime = |this: &mut Self| -> &'hir hir::Lifetime { + this.arena.alloc(hir::Lifetime { + hir_id: this.next_id(), + ident: p.name.ident(), + kind: rustc_hir::LifetimeKind::Param(p.def_id), + source: rustc_hir::LifetimeSource::Path { + angle_brackets: rustc_hir::AngleBrackets::Full, + }, + syntax: rustc_hir::LifetimeSyntax::ExplicitBound, + }) + }; + + hir::WherePredicate { + hir_id: self.next_id(), + span: DUMMY_SP, + kind: self.arena.alloc(hir::WherePredicateKind::RegionPredicate( + hir::WhereRegionPredicate { + in_where_clause: true, + lifetime: create_lifetime(self), + bounds: self + .arena + .alloc_slice(&[hir::GenericBound::Outlives(create_lifetime(self))]), + }, + )), + } + } + + fn create_generics_args_from_params( + &mut self, + params: &[hir::GenericParam<'hir>], + add_lifetimes: bool, + span: Span, + ) -> &'hir hir::GenericArgs<'hir> { + self.arena.alloc(hir::GenericArgs { + args: self.arena.alloc_from_iter(params.iter().filter_map(|p| { + // Skip self generic arg, we do not need to propagate it. + if p.name.ident().name == kw::SelfUpper { + return None; + } + + let create_path = |this: &mut Self| { + let res = Res::Def( + match p.kind { + hir::GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam, + hir::GenericParamKind::Type { .. } => DefKind::TyParam, + hir::GenericParamKind::Const { .. } => DefKind::ConstParam, + }, + p.def_id.to_def_id(), + ); + + hir::QPath::Resolved( + None, + self.arena.alloc(hir::Path { + segments: this.arena.alloc_slice(&[hir::PathSegment { + args: None, + hir_id: this.next_id(), + ident: p.name.ident(), + infer_args: false, + res, + }]), + res, + span: p.span, + }), + ) + }; + + match p.kind { + hir::GenericParamKind::Lifetime { .. } => match add_lifetimes { + true => Some(hir::GenericArg::Lifetime(self.arena.alloc(hir::Lifetime { + hir_id: self.next_id(), + ident: p.name.ident(), + kind: hir::LifetimeKind::Param(p.def_id), + source: hir::LifetimeSource::Path { + angle_brackets: hir::AngleBrackets::Full, + }, + syntax: hir::LifetimeSyntax::ExplicitBound, + }))), + false => None, + }, + hir::GenericParamKind::Type { .. } => { + Some(hir::GenericArg::Type(self.arena.alloc(hir::Ty { + hir_id: self.next_id(), + span: p.span, + kind: hir::TyKind::Path(create_path(self)), + }))) + } + hir::GenericParamKind::Const { .. } => { + Some(hir::GenericArg::Const(self.arena.alloc(hir::ConstArg { + hir_id: self.next_id(), + kind: hir::ConstArgKind::Path(create_path(self)), + span: p.span, + }))) + } + } + })), + constraints: &[], + parenthesized: hir::GenericArgsParentheses::No, + span_ext: span, + }) + } + + fn get_fn_like_generics(&mut self, id: DefId, span: Span) -> Option { + if let Some(local_id) = id.as_local() { + match self.ast_index.get(local_id) { + Some(AstOwner::Item(item)) if let ItemKind::Fn(f) = &item.kind => { + Some(f.generics.clone()) + } + Some(AstOwner::AssocItem(item, _)) if let AssocItemKind::Fn(f) = &item.kind => { + Some(f.generics.clone()) + } + _ => None, + } + } else { + self.get_external_generics(id, false, span) + } + } + + fn get_external_generics( + &mut self, + id: DefId, + processing_parent: bool, + span: Span, + ) -> Option { + let generics = self.tcx.generics_of(id); + if generics.own_params.is_empty() { + return None; + } + + // Skip first Self parameter if we are in trait, it will be added later. + let to_skip = (processing_parent && generics.has_self) as usize; + + Some(Generics { + params: generics + .own_params + .iter() + .skip(to_skip) + .map(|p| GenericParam { + attrs: Default::default(), + bounds: Default::default(), + colon_span: None, + id: self.next_node_id(), + ident: Ident::with_dummy_span(p.name), + is_placeholder: false, + kind: match p.kind { + GenericParamDefKind::Lifetime => GenericParamKind::Lifetime, + GenericParamDefKind::Type { .. } => { + GenericParamKind::Type { default: None } + } + GenericParamDefKind::Const { .. } => self.map_const_kind(p, span), + }, + }) + .collect(), + where_clause: Default::default(), + span: DUMMY_SP, + }) + } + + fn map_const_kind(&mut self, p: &ty::GenericParamDef, span: Span) -> GenericParamKind { + let const_type = self.tcx.type_of(p.def_id).instantiate_identity(); + + let (type_symbol, res) = match const_type.kind() { + ty::Bool => (sym::bool, Res::PrimTy(hir::PrimTy::Bool)), + ty::Uint(uint) => (uint.name(), Res::PrimTy(hir::PrimTy::Uint(*uint))), + ty::Int(int) => (int.name(), Res::PrimTy(hir::PrimTy::Int(*int))), + ty::Char => (sym::char, Res::PrimTy(hir::PrimTy::Char)), + _ => { + self.tcx + .dcx() + .span_delayed_bug(span, format!("Unexpected const type: {}", const_type)); + + (sym::dummy, Res::Err) + } + }; + + let node_id = self.next_node_id(); + + self.resolver.partial_res_map.insert(node_id, hir::def::PartialRes::new(res)); + + GenericParamKind::Const { + ty: Box::new(Ty { + id: node_id, + kind: TyKind::Path( + None, + Path { + segments: thin_vec![PathSegment { + ident: Ident::with_dummy_span(type_symbol), + id: self.next_node_id(), + args: None + }], + span: DUMMY_SP, + tokens: None, + }, + ), + span: DUMMY_SP, + tokens: None, + }), + span: DUMMY_SP, + default: None, + } + } + + fn get_parent_generics( + &mut self, + id: DefId, + add_self: bool, + user_specified: bool, + span: Span, + ) -> Option { + // If args are user-specified we still maybe need to add self. + let mut generics = if user_specified { + None + } else { + if let Some(local_id) = id.as_local() { + if let Some(AstOwner::Item(item)) = self.ast_index.get(local_id) + && matches!(item.kind, ItemKind::Trait(..)) + { + item.opt_generics().cloned() + } else { + None + } + } else { + self.get_external_generics(id, true, span) + } + }; + + if add_self { + generics.get_or_insert_default().params.insert( + 0, + GenericParam { + id: self.next_node_id(), + ident: Ident::new(kw::SelfUpper, DUMMY_SP), + attrs: Default::default(), + bounds: vec![], + is_placeholder: false, + kind: GenericParamKind::Type { default: None }, + colon_span: None, + }, + ); + } + + generics + } +} diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index b034e250b58d..4a2992038003 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -7,7 +7,6 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::msg; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::definitions::DefPathData; use rustc_hir::{HirId, Target, find_attr}; @@ -341,12 +340,13 @@ pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> { self.arena.alloc_from_iter(fields.iter().map(|&ident| self.lower_ident(ident))), ), ExprKind::Struct(se) => { - let rest = match &se.rest { - StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)), + let rest = match se.rest { + StructRest::Base(ref e) => hir::StructTailExpr::Base(self.lower_expr(e)), StructRest::Rest(sp) => { - hir::StructTailExpr::DefaultFields(self.lower_span(*sp)) + hir::StructTailExpr::DefaultFields(self.lower_span(sp)) } StructRest::None => hir::StructTailExpr::None, + StructRest::NoneWithError(guar) => hir::StructTailExpr::NoneWithError(guar), }; hir::ExprKind::Struct( self.arena.alloc(self.lower_qpath( @@ -805,7 +805,7 @@ pub(super) fn maybe_forward_track_caller( ) { if self.tcx.features().async_fn_track_caller() && let Some(attrs) = self.attrs.get(&outer_hir_id.local_id) - && find_attr!(*attrs, AttributeKind::TrackCaller(_)) + && find_attr!(*attrs, TrackCaller(_)) { let unstable_span = self.mark_span_with_reason( DesugaringKind::Async, @@ -1072,8 +1072,7 @@ fn lower_expr_closure( let (binder_clause, generic_params) = self.lower_closure_binder(binder); let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| { - - let mut coroutine_kind = find_attr!(attrs, AttributeKind::Coroutine(_) => hir::CoroutineKind::Coroutine(Movability::Movable)); + let mut coroutine_kind = find_attr!(attrs, Coroutine(_) => hir::CoroutineKind::Coroutine(Movability::Movable)); // FIXME(contracts): Support contracts on closures? let body_id = this.lower_fn_body(decl, None, |this| { @@ -1437,7 +1436,7 @@ fn destructure_assign_mut( Some(self.lower_span(e.span)) } StructRest::Rest(span) => Some(self.lower_span(*span)), - StructRest::None => None, + StructRest::None | StructRest::NoneWithError(_) => None, }; let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted); return self.pat_without_dbm(lhs.span, struct_pat); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 9922ed8a5c58..ed78b77a704f 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -27,7 +27,7 @@ pub(super) struct ItemLowerer<'a, 'hir> { pub(super) tcx: TyCtxt<'hir>, - pub(super) resolver: &'a mut ResolverAstLowering, + pub(super) resolver: &'a mut ResolverAstLowering<'hir>, pub(super) ast_index: &'a IndexSlice>, pub(super) owners: &'a mut IndexVec>, } @@ -57,7 +57,7 @@ fn with_lctx( owner: NodeId, f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>, ) { - let mut lctx = LoweringContext::new(self.tcx, self.resolver); + let mut lctx = LoweringContext::new(self.tcx, self.ast_index, self.resolver); lctx.with_hir_id_owner(owner, |lctx| f(lctx)); for (def_id, info) in lctx.children { @@ -243,7 +243,7 @@ fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> { vis_span, span: self.lower_span(i.span), has_delayed_lints: !self.delayed_lints.is_empty(), - eii: find_attr!(attrs, AttributeKind::EiiImpls(..) | AttributeKind::EiiDeclaration(..)), + eii: find_attr!(attrs, EiiImpls(..) | EiiDeclaration(..)), }; self.arena.alloc(item) } @@ -512,6 +512,8 @@ fn lower_item_kind( constness, is_auto, safety, + // FIXME(impl_restrictions): lower to HIR + impl_restriction: _, ident, generics, bounds, @@ -707,10 +709,7 @@ fn lower_use_tree( vis_span, span: this.lower_span(use_tree.span), has_delayed_lints: !this.delayed_lints.is_empty(), - eii: find_attr!( - attrs, - AttributeKind::EiiImpls(..) | AttributeKind::EiiDeclaration(..) - ), + eii: find_attr!(attrs, EiiImpls(..) | EiiDeclaration(..)), }; hir::OwnerNode::Item(this.arena.alloc(item)) }); @@ -939,7 +938,7 @@ fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> { ); let trait_item_def_id = hir_id.expect_owner(); - let (ident, generics, kind, has_default) = match &i.kind { + let (ident, generics, kind, has_value) = match &i.kind { AssocItemKind::Const(box ConstItem { ident, generics, @@ -1088,13 +1087,26 @@ fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> { } }; + let defaultness = match i.kind.defaultness() { + // We do not yet support `final` on trait associated items other than functions. + // Even though we reject `final` on non-functions during AST validation, we still + // need to stop propagating it here because later compiler passes do not expect + // and cannot handle such items. + Defaultness::Final(..) if !matches!(i.kind, AssocItemKind::Fn(..)) => { + Defaultness::Implicit + } + defaultness => defaultness, + }; + let (defaultness, _) = self + .lower_defaultness(defaultness, has_value, || hir::Defaultness::Default { has_value }); + let item = hir::TraitItem { owner_id: trait_item_def_id, ident: self.lower_ident(ident), generics, kind, span: self.lower_span(i.span), - defaultness: hir::Defaultness::Default { has_value: has_default }, + defaultness, has_delayed_lints: !self.delayed_lints.is_empty(), }; self.arena.alloc(item) @@ -1122,7 +1134,8 @@ fn lower_trait_impl_header( // `defaultness.has_value()` is never called for an `impl`, always `true` in order // to not cause an assertion failure inside the `lower_defaultness` function. let has_val = true; - let (defaultness, defaultness_span) = self.lower_defaultness(defaultness, has_val); + let (defaultness, defaultness_span) = + self.lower_defaultness(defaultness, has_val, || hir::Defaultness::Final); let modifiers = TraitBoundModifiers { constness: BoundConstness::Never, asyncness: BoundAsyncness::Normal, @@ -1151,7 +1164,8 @@ fn lower_impl_item( ) -> &'hir hir::ImplItem<'hir> { // Since `default impl` is not yet implemented, this is always true in impls. let has_value = true; - let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); + let (defaultness, _) = + self.lower_defaultness(i.kind.defaultness(), has_value, || hir::Defaultness::Final); let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); let attrs = self.lower_attrs( hir_id, @@ -1304,15 +1318,14 @@ fn lower_defaultness( &self, d: Defaultness, has_value: bool, + implicit: impl FnOnce() -> hir::Defaultness, ) -> (hir::Defaultness, Option) { match d { + Defaultness::Implicit => (implicit(), None), Defaultness::Default(sp) => { (hir::Defaultness::Default { has_value }, Some(self.lower_span(sp))) } - Defaultness::Final => { - assert!(has_value); - (hir::Defaultness::Final, None) - } + Defaultness::Final(sp) => (hir::Defaultness::Final, Some(self.lower_span(sp))), } } @@ -1410,9 +1423,7 @@ fn lower_maybe_coroutine_body( // create a fake body so that the entire rest of the compiler doesn't have to deal with // this as a special case. return self.lower_fn_body(decl, contract, |this| { - if find_attr!(attrs, AttributeKind::RustcIntrinsic) - || this.tcx.is_sdylib_interface_build() - { + if find_attr!(attrs, RustcIntrinsic) || this.tcx.is_sdylib_interface_build() { let span = this.lower_span(span); let empty_block = hir::Block { hir_id: this.next_id(), @@ -1690,7 +1701,7 @@ pub(super) fn lower_fn_header( let safety = self.lower_safety(h.safety, default_safety); // Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so. - let safety = if find_attr!(attrs, AttributeKind::TargetFeature { was_forced: false, .. }) + let safety = if find_attr!(attrs, TargetFeature { was_forced: false, .. }) && safety.is_safe() && !self.tcx.sess.target.is_like_wasm { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 6265a06410d5..c3525d124670 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -32,7 +32,7 @@ // tidy-alphabetical-start #![feature(box_patterns)] -#![feature(if_let_guard)] +#![recursion_limit = "256"] // tidy-alphabetical-end use std::mem; @@ -47,7 +47,6 @@ use rustc_data_structures::sync::spawn; use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::{DefPathData, DisambiguatorState}; @@ -90,7 +89,16 @@ macro_rules! arena_vec { struct LoweringContext<'a, 'hir> { tcx: TyCtxt<'hir>, - resolver: &'a mut ResolverAstLowering, + + // During lowering of delegation we need to access AST of other functions + // in order to properly propagate generics, we could have done it at resolve + // stage, however it will require either to firstly identify functions that + // are being reused and store their generics, or to store generics of all functions + // in resolver. This approach helps with those problems, as functions that are reused + // will be in AST index. + ast_index: &'a IndexSlice>, + + resolver: &'a mut ResolverAstLowering<'hir>, disambiguator: DisambiguatorState, /// Used to allocate HIR nodes. @@ -124,7 +132,7 @@ struct LoweringContext<'a, 'hir> { current_hir_id_owner: hir::OwnerId, item_local_id_counter: hir::ItemLocalId, - trait_map: ItemLocalMap>, + trait_map: ItemLocalMap<&'hir [TraitCandidate<'hir>]>, impl_trait_defs: Vec>, impl_trait_bounds: Vec>, @@ -150,11 +158,16 @@ struct LoweringContext<'a, 'hir> { } impl<'a, 'hir> LoweringContext<'a, 'hir> { - fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self { + fn new( + tcx: TyCtxt<'hir>, + ast_index: &'a IndexSlice>, + resolver: &'a mut ResolverAstLowering<'hir>, + ) -> Self { let registered_tools = tcx.registered_tools(()).iter().map(|x| x.name).collect(); Self { // Pseudo-globals. tcx, + ast_index, resolver, disambiguator: DisambiguatorState::new(), arena: tcx.hir_arena, @@ -234,7 +247,7 @@ fn lower(&self, span: Span) -> Span { } #[extension(trait ResolverAstLoweringExt)] -impl ResolverAstLowering { +impl ResolverAstLowering<'_> { fn legacy_const_generic_args(&self, expr: &Expr, tcx: TyCtxt<'_>) -> Option> { let ExprKind::Path(None, path) = &expr.kind else { return None; @@ -255,10 +268,10 @@ fn legacy_const_generic_args(&self, expr: &Expr, tcx: TyCtxt<'_>) -> Option fn_indexes + tcx, def_id, + RustcLegacyConstGenerics{fn_indexes,..} => fn_indexes ) .map(|fn_indexes| fn_indexes.iter().map(|(num, _)| *num).collect()) } @@ -734,8 +747,8 @@ fn lower_node_id(&mut self, ast_node_id: NodeId) -> HirId { self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id))); } - if let Some(traits) = self.resolver.trait_map.remove(&ast_node_id) { - self.trait_map.insert(hir_id.local_id, traits.into_boxed_slice()); + if let Some(&traits) = self.resolver.trait_map.get(&ast_node_id) { + self.trait_map.insert(hir_id.local_id, traits); } // Check whether the same `NodeId` is lowered more than once. @@ -1497,6 +1510,13 @@ fn lower_ty(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> { TyKind::Pat(ty, pat) => { hir::TyKind::Pat(self.lower_ty_alloc(ty, itctx), self.lower_ty_pat(pat, ty.span)) } + TyKind::FieldOf(ty, variant, field) => hir::TyKind::FieldOf( + self.lower_ty_alloc(ty, itctx), + self.arena.alloc(hir::TyFieldPath { + variant: variant.map(|variant| self.lower_ident(variant)), + field: self.lower_ident(*field), + }), + ), TyKind::MacCall(_) => { span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now") } @@ -2522,16 +2542,6 @@ fn lower_expr_to_const_arg_direct(&mut self, expr: &Expr) -> hir::ConstArg<'hir> ExprKind::Block(block, _) => { if let [stmt] = block.stmts.as_slice() && let StmtKind::Expr(expr) = &stmt.kind - && matches!( - expr.kind, - ExprKind::Block(..) - | ExprKind::Path(..) - | ExprKind::Struct(..) - | ExprKind::Call(..) - | ExprKind::Tup(..) - | ExprKind::Array(..) - | ExprKind::ConstBlock(..) - ) { return self.lower_expr_to_const_arg_direct(expr); } @@ -2554,6 +2564,17 @@ fn lower_expr_to_const_arg_direct(&mut self, expr: &Expr) -> hir::ConstArg<'hir> let span = expr.span; let literal = self.lower_lit(literal, span); + if !matches!(literal.node, LitKind::Int(..)) { + let err = + self.dcx().struct_span_err(expr.span, "negated literal must be an integer"); + + return ConstArg { + hir_id: self.next_id(), + kind: hir::ConstArgKind::Error(err.emit()), + span, + }; + } + ConstArg { hir_id: self.lower_node_id(expr.id), kind: hir::ConstArgKind::Literal { lit: literal.node, negated: true }, diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index bfb42efb684f..ec57720387c0 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -112,7 +112,7 @@ pub(crate) fn lower_qpath( } // `a::b::Trait(Args)::TraitItem` Res::Def(DefKind::AssocFn, _) - | Res::Def(DefKind::AssocConst, _) + | Res::Def(DefKind::AssocConst { .. }, _) | Res::Def(DefKind::AssocTy, _) if i + 2 == proj_start => { diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index fdc735fa8d4f..c9def6246d1b 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -13,6 +13,7 @@ rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } +rustc_hir = { path = "../rustc_hir" } rustc_macros = { path = "../rustc_macros" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index b9fb20b68971..c8dbba006f6d 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -65,6 +65,28 @@ fn constness(&self) -> Option { } } +enum AllowDefault { + Yes, + No, +} + +impl AllowDefault { + fn when(b: bool) -> Self { + if b { Self::Yes } else { Self::No } + } +} + +enum AllowFinal { + Yes, + No, +} + +impl AllowFinal { + fn when(b: bool) -> Self { + if b { Self::Yes } else { Self::No } + } +} + struct AstValidator<'a> { sess: &'a Session, features: &'a Features, @@ -563,10 +585,32 @@ fn check_fn_ptr_safety(&self, span: Span, safety: Safety) { } } - fn check_defaultness(&self, span: Span, defaultness: Defaultness) { - if let Defaultness::Default(def_span) = defaultness { - let span = self.sess.source_map().guess_head_span(span); - self.dcx().emit_err(errors::ForbiddenDefault { span, def_span }); + fn check_defaultness( + &self, + span: Span, + defaultness: Defaultness, + allow_default: AllowDefault, + allow_final: AllowFinal, + ) { + match defaultness { + Defaultness::Default(def_span) if matches!(allow_default, AllowDefault::No) => { + let span = self.sess.source_map().guess_head_span(span); + self.dcx().emit_err(errors::ForbiddenDefault { span, def_span }); + } + Defaultness::Final(def_span) if matches!(allow_final, AllowFinal::No) => { + let span = self.sess.source_map().guess_head_span(span); + self.dcx().emit_err(errors::ForbiddenFinal { span, def_span }); + } + _ => (), + } + } + + fn check_final_has_body(&self, item: &Item, defaultness: Defaultness) { + if let AssocItemKind::Fn(box Fn { body: None, .. }) = &item.kind + && let Defaultness::Final(def_span) = defaultness + { + let span = self.sess.source_map().guess_head_span(item.span); + self.dcx().emit_err(errors::ForbiddenFinalWithoutBody { span, def_span }); } } @@ -698,13 +742,11 @@ fn check_c_variadic_type(&self, fk: FnKind<'a>, attrs: &'a AttrVec) { unreachable!("C variable argument list cannot be used in closures") }; - // C-variadics are not yet implemented in const evaluation. - if let Const::Yes(const_span) = sig.header.constness { - self.dcx().emit_err(errors::ConstAndCVariadic { - spans: vec![const_span, variadic_param.span], - const_span, - variadic_span: variadic_param.span, - }); + if let Const::Yes(_) = sig.header.constness + && !self.features.enabled(sym::const_c_variadic) + { + let msg = format!("c-variadic const function definitions are unstable"); + feature_err(&self.sess, sym::const_c_variadic, sig.span, msg).emit(); } if let Some(coroutine_kind) = sig.header.coroutine_kind { @@ -1192,7 +1234,7 @@ fn visit_item(&mut self, item: &'a Item) { }, ) => { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); - self.check_defaultness(item.span, *defaultness); + self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); for EiiImpl { eii_macro_path, .. } in eii_impls { self.visit_path(eii_macro_path); @@ -1362,7 +1404,7 @@ fn visit_item(&mut self, item: &'a Item) { }); } ItemKind::Const(box ConstItem { defaultness, ident, rhs_kind, .. }) => { - self.check_defaultness(item.span, *defaultness); + self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); if !rhs_kind.has_expr() { self.dcx().emit_err(errors::ConstWithoutBody { span: item.span, @@ -1400,7 +1442,7 @@ fn visit_item(&mut self, item: &'a Item) { ItemKind::TyAlias( ty_alias @ box TyAlias { defaultness, bounds, after_where_clause, ty, .. }, ) => { - self.check_defaultness(item.span, *defaultness); + self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); if ty.is_none() { self.dcx().emit_err(errors::TyAliasWithoutBody { span: item.span, @@ -1430,7 +1472,7 @@ fn visit_item(&mut self, item: &'a Item) { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { match &fi.kind { ForeignItemKind::Fn(box Fn { defaultness, ident, sig, body, .. }) => { - self.check_defaultness(fi.span, *defaultness); + self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No); self.check_foreign_fn_bodyless(*ident, body.as_deref()); self.check_foreign_fn_headerless(sig.header); self.check_foreign_item_ascii_only(*ident); @@ -1450,7 +1492,7 @@ fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { ty, .. }) => { - self.check_defaultness(fi.span, *defaultness); + self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No); self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span)); self.check_type_no_bounds(bounds, "`extern` blocks"); self.check_foreign_ty_genericless(generics, after_where_clause); @@ -1709,9 +1751,19 @@ fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) { self.check_nomangle_item_asciionly(ident, item.span); } - if ctxt == AssocCtxt::Trait || self.outer_trait_or_trait_impl.is_none() { - self.check_defaultness(item.span, item.kind.defaultness()); - } + let defaultness = item.kind.defaultness(); + self.check_defaultness( + item.span, + defaultness, + // `default` is allowed on all associated items in impls. + AllowDefault::when(matches!(ctxt, AssocCtxt::Impl { .. })), + // `final` is allowed on all associated *functions* in traits. + AllowFinal::when( + ctxt == AssocCtxt::Trait && matches!(item.kind, AssocItemKind::Fn(..)), + ), + ); + + self.check_final_has_body(item, defaultness); if let AssocCtxt::Impl { .. } = ctxt { match &item.kind { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index dd260aede489..edc175b99088 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -4,7 +4,7 @@ use rustc_ast::ParamKindOrd; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic}; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; #[derive(Diagnostic)] @@ -159,6 +159,24 @@ pub(crate) struct ForbiddenDefault { pub def_span: Span, } +#[derive(Diagnostic)] +#[diag("`final` is only allowed on associated functions in traits")] +pub(crate) struct ForbiddenFinal { + #[primary_span] + pub span: Span, + #[label("`final` because of this")] + pub def_span: Span, +} + +#[derive(Diagnostic)] +#[diag("`final` is only allowed on associated functions if they have a body")] +pub(crate) struct ForbiddenFinalWithoutBody { + #[primary_span] + pub span: Span, + #[label("`final` because of this")] + pub def_span: Span, +} + #[derive(Diagnostic)] #[diag("associated constant in `impl` without body")] pub(crate) struct AssocConstWithoutBody { @@ -653,7 +671,7 @@ pub(crate) struct MissingUnsafeOnExtern { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("extern blocks should be unsafe")] pub(crate) struct MissingUnsafeOnExternLint { #[suggestion( @@ -823,17 +841,6 @@ pub(crate) struct ConstAndCoroutine { pub coroutine_kind: &'static str, } -#[derive(Diagnostic)] -#[diag("functions cannot be both `const` and C-variadic")] -pub(crate) struct ConstAndCVariadic { - #[primary_span] - pub spans: Vec, - #[label("`const` because of this")] - pub const_span: Span, - #[label("C-variadic because of this")] - pub variadic_span: Span, -} - #[derive(Diagnostic)] #[diag("functions cannot be both `{$coroutine_kind}` and C-variadic")] pub(crate) struct CoroutineAndCVariadic { @@ -1020,7 +1027,7 @@ pub(crate) struct MissingAbi { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`extern` declarations without an explicit ABI are deprecated")] pub(crate) struct MissingAbiSugg { #[suggestion( diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 4523b4c14163..72679d745665 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,11 +1,14 @@ use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{self as ast, AttrVec, NodeId, PatKind, attr, token}; +use rustc_attr_parsing::AttributeParser; use rustc_errors::msg; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features}; +use rustc_hir::Attribute; +use rustc_hir::attrs::AttributeKind; use rustc_session::Session; use rustc_session::parse::{feature_err, feature_warn}; use rustc_span::source_map::Spanned; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use thin_vec::ThinVec; use crate::errors; @@ -427,7 +430,7 @@ fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) { false } ast::AssocItemKind::Const(box ast::ConstItem { - rhs_kind: ast::ConstItemRhsKind::TypeConst { .. }, + rhs_kind: ast::ConstItemRhsKind::TypeConst { rhs }, .. }) => { // Make sure this is only allowed if the feature gate is enabled. @@ -438,6 +441,17 @@ fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) { i.span, "associated `type const` are unstable" ); + // Make sure associated `type const` defaults in traits are only allowed + // if the feature gate is enabled. + // #![feature(associated_type_defaults)] + if ctxt == AssocCtxt::Trait && rhs.is_some() { + gate!( + &self, + associated_type_defaults, + i.span, + "associated type defaults are unstable" + ); + } false } _ => false, @@ -481,11 +495,6 @@ macro_rules! gate_all { } }; } - gate_all!( - if_let_guard, - "`if let` guards are experimental", - "you can write `if matches!(, )` instead of `if let = `" - ); gate_all!( async_trait_bounds, "`async` trait bounds are unstable", @@ -580,6 +589,8 @@ macro_rules! gate_all { gate_all!(frontmatter, "frontmatters are experimental"); gate_all!(coroutines, "coroutine syntax is experimental"); gate_all!(const_block_items, "const block items are experimental"); + gate_all!(final_associated_functions, "`final` on trait functions is experimental"); + gate_all!(impl_restriction, "`impl` restrictions are experimental"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { @@ -640,17 +651,27 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) return; } let mut errored = false; - for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) { + + if let Some(Attribute::Parsed(AttributeKind::Feature(feature_idents, first_span))) = + AttributeParser::parse_limited( + sess, + &krate.attrs, + sym::feature, + DUMMY_SP, + krate.id, + Some(&features), + ) + { // `feature(...)` used on non-nightly. This is definitely an error. let mut err = errors::FeatureOnNonNightly { - span: attr.span, + span: first_span, channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"), stable_features: vec![], sugg: None, }; let mut all_stable = true; - for ident in attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident()) { + for ident in feature_idents { let name = ident.name; let stable_since = features .enabled_lang_features() @@ -665,7 +686,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) } } if all_stable { - err.sugg = Some(attr.span); + err.sugg = Some(first_span); } sess.dcx().emit_err(err); errored = true; diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 7793f786cefe..677e44a79ac4 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -4,7 +4,6 @@ // tidy-alphabetical-start #![feature(box_patterns)] -#![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iter_is_partitioned)] // tidy-alphabetical-end diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs index 74ed0405498d..25b398b84924 100644 --- a/compiler/rustc_ast_pretty/src/pprust/mod.rs +++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs @@ -80,6 +80,10 @@ pub fn vis_to_string(v: &ast::Visibility) -> String { State::new().vis_to_string(v) } +pub fn impl_restriction_to_string(r: &ast::ImplRestriction) -> String { + State::new().impl_restriction_to_string(r) +} + pub fn meta_list_item_to_string(li: &ast::MetaItemInner) -> String { State::new().meta_list_item_to_string(li) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index c8874ed99dca..c85d6f454321 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1110,6 +1110,10 @@ fn vis_to_string(&self, v: &ast::Visibility) -> String { Self::to_string(|s| s.print_visibility(v)) } + fn impl_restriction_to_string(&self, r: &ast::ImplRestriction) -> String { + Self::to_string(|s| s.print_impl_restriction(r)) + } + fn block_to_string(&self, blk: &ast::Block) -> String { Self::to_string(|s| { let (cb, ib) = s.head(""); @@ -1377,6 +1381,23 @@ pub fn print_type(&mut self, ty: &ast::Ty) { self.word(" is "); self.print_ty_pat(pat); } + ast::TyKind::FieldOf(ty, variant, field) => { + self.word("builtin # field_of"); + self.popen(); + let ib = self.ibox(0); + self.print_type(ty); + self.word(","); + self.space(); + + if let Some(variant) = variant { + self.print_ident(*variant); + self.word("."); + } + self.print_ident(*field); + + self.end(ib); + self.pclose(); + } } self.end(ib); } @@ -1394,6 +1415,9 @@ fn print_formal_generic_params(&mut self, generic_params: &[ast::GenericParam]) } fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) { + if let ast::Parens::Yes = t.parens { + self.popen(); + } self.print_formal_generic_params(&t.bound_generic_params); let ast::TraitBoundModifiers { constness, asyncness, polarity } = t.modifiers; @@ -1416,7 +1440,10 @@ fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) { } } - self.print_trait_ref(&t.trait_ref) + self.print_trait_ref(&t.trait_ref); + if let ast::Parens::Yes = t.parens { + self.pclose(); + } } fn print_stmt(&mut self, st: &ast::Stmt) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 7dce7f6d4195..9b4ff2b63bd4 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -162,7 +162,7 @@ fn print_expr_struct( self.word("{"); let has_rest = match rest { ast::StructRest::Base(_) | ast::StructRest::Rest(_) => true, - ast::StructRest::None => false, + ast::StructRest::None | ast::StructRest::NoneWithError(_) => false, }; if fields.is_empty() && !has_rest { self.word("}"); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index c7f110a2e003..3407feb3dcc3 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -51,7 +51,7 @@ pub(crate) fn print_foreign_item(&mut self, item: &ast::ForeignItem) { expr.as_deref(), vis, *safety, - ast::Defaultness::Final, + ast::Defaultness::Implicit, define_opaque.as_deref(), ), ast::ForeignItemKind::TyAlias(box ast::TyAlias { @@ -201,7 +201,7 @@ pub(crate) fn print_item(&mut self, item: &ast::Item) { body.as_deref(), &item.vis, ast::Safety::Default, - ast::Defaultness::Final, + ast::Defaultness::Implicit, define_opaque.as_deref(), ); } @@ -365,6 +365,7 @@ pub(crate) fn print_item(&mut self, item: &ast::Item) { constness, safety, is_auto, + impl_restriction, ident, generics, bounds, @@ -375,6 +376,7 @@ pub(crate) fn print_item(&mut self, item: &ast::Item) { self.print_constness(*constness); self.print_safety(*safety); self.print_is_auto(*is_auto); + self.print_impl_restriction(impl_restriction); self.word_nbsp("trait"); self.print_ident(*ident); self.print_generic_params(&generics.params); @@ -483,6 +485,20 @@ pub(crate) fn print_visibility(&mut self, vis: &ast::Visibility) { } } + pub(crate) fn print_impl_restriction(&mut self, impl_restriction: &ast::ImplRestriction) { + match &impl_restriction.kind { + ast::RestrictionKind::Restricted { path, shorthand, .. } => { + let path = Self::to_string(|s| s.print_path(path, false, 0)); + if *shorthand { + self.word_nbsp(format!("impl({path})")) + } else { + self.word_nbsp(format!("impl(in {path})")) + } + } + ast::RestrictionKind::Unrestricted => {} + } + } + fn print_defaultness(&mut self, defaultness: ast::Defaultness) { if let ast::Defaultness::Default(_) = defaultness { self.word_nbsp("default"); diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 0a11a2da0dcf..886df58e8d6f 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_parse = { path = "../rustc_parse" } +rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 4f1a8cd8b403..c2511ac75d5d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -52,8 +52,8 @@ fn extend( } } -pub(crate) struct AllowConstFnUnstableParser; -impl CombineAttributeParser for AllowConstFnUnstableParser { +pub(crate) struct RustcAllowConstFnUnstableParser; +impl CombineAttributeParser for RustcAllowConstFnUnstableParser { const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable]; type Item = Symbol; const CONVERT: ConvertFn = diff --git a/compiler/rustc_attr_parsing/src/attributes/autodiff.rs b/compiler/rustc_attr_parsing/src/attributes/autodiff.rs new file mode 100644 index 000000000000..118a4103b1a9 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/autodiff.rs @@ -0,0 +1,117 @@ +use std::str::FromStr; + +use rustc_ast::LitKind; +use rustc_ast::expand::autodiff_attrs::{DiffActivity, DiffMode}; +use rustc_feature::{AttributeTemplate, template}; +use rustc_hir::attrs::{AttributeKind, RustcAutodiff}; +use rustc_hir::{MethodKind, Target}; +use rustc_span::{Symbol, sym}; +use thin_vec::ThinVec; + +use crate::attributes::prelude::Allow; +use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::{ArgParser, MetaItemOrLitParser}; +use crate::target_checking::AllowedTargets; + +pub(crate) struct RustcAutodiffParser; + +impl SingleAttributeParser for RustcAutodiffParser { + const PATH: &[Symbol] = &[sym::rustc_autodiff]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + const TEMPLATE: AttributeTemplate = template!( + List: &["MODE", "WIDTH", "INPUT_ACTIVITIES", "OUTPUT_ACTIVITY"], + "https://doc.rust-lang.org/std/autodiff/index.html" + ); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let list = match args { + ArgParser::NoArgs => return Some(AttributeKind::RustcAutodiff(None)), + ArgParser::List(list) => list, + ArgParser::NameValue(_) => { + cx.expected_list_or_no_args(cx.attr_span); + return None; + } + }; + + let mut items = list.mixed().peekable(); + + // Parse name + let Some(mode) = items.next() else { + cx.expected_at_least_one_argument(list.span); + return None; + }; + let Some(mode) = mode.meta_item() else { + cx.expected_identifier(mode.span()); + return None; + }; + let Ok(()) = mode.args().no_args() else { + cx.expected_identifier(mode.span()); + return None; + }; + let Some(mode) = mode.path().word() else { + cx.expected_identifier(mode.span()); + return None; + }; + let Ok(mode) = DiffMode::from_str(mode.as_str()) else { + cx.expected_specific_argument(mode.span, DiffMode::all_modes()); + return None; + }; + + // Parse width + let width = if let Some(width) = items.peek() + && let MetaItemOrLitParser::Lit(width) = width + && let LitKind::Int(width, _) = width.kind + && let Ok(width) = width.0.try_into() + { + _ = items.next(); + width + } else { + 1 + }; + + // Parse activities + let mut activities = ThinVec::new(); + for activity in items { + let MetaItemOrLitParser::MetaItemParser(activity) = activity else { + cx.expected_specific_argument(activity.span(), DiffActivity::all_activities()); + return None; + }; + let Ok(()) = activity.args().no_args() else { + cx.expected_specific_argument(activity.span(), DiffActivity::all_activities()); + return None; + }; + let Some(activity) = activity.path().word() else { + cx.expected_specific_argument(activity.span(), DiffActivity::all_activities()); + return None; + }; + let Ok(activity) = DiffActivity::from_str(activity.as_str()) else { + cx.expected_specific_argument(activity.span, DiffActivity::all_activities()); + return None; + }; + + activities.push(activity); + } + let Some(ret_activity) = activities.pop() else { + cx.expected_specific_argument( + list.span.with_lo(list.span.hi()), + DiffActivity::all_activities(), + ); + return None; + }; + + Some(AttributeKind::RustcAutodiff(Some(Box::new(RustcAutodiff { + mode, + width, + input_activity: activities, + ret_activity, + })))) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index b6cb5b4504ee..7377159be370 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -128,20 +128,14 @@ pub fn parse_cfg_select( } } - if let Some(features) = features - && features.enabled(sym::cfg_select) - { - let it = branches - .reachable - .iter() - .map(|(entry, _, _)| CfgSelectPredicate::Cfg(entry.clone())) - .chain(branches.wildcard.as_ref().map(|(t, _, _)| CfgSelectPredicate::Wildcard(*t))) - .chain( - branches.unreachable.iter().map(|(entry, _, _)| CfgSelectPredicate::clone(entry)), - ); + let it = branches + .reachable + .iter() + .map(|(entry, _, _)| CfgSelectPredicate::Cfg(entry.clone())) + .chain(branches.wildcard.as_ref().map(|(t, _, _)| CfgSelectPredicate::Wildcard(*t))) + .chain(branches.unreachable.iter().map(|(entry, _, _)| CfgSelectPredicate::clone(entry))); - lint_unreachable(p, it, lint_node_id); - } + lint_unreachable(p, it, lint_node_id); Ok(branches) } diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 485307622291..4909e0d35173 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -153,9 +153,9 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option SingleAttributeParser for ObjcClassParser { +impl SingleAttributeParser for RustcObjcClassParser { const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; @@ -185,9 +185,9 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option SingleAttributeParser for ObjcSelectorParser { +impl SingleAttributeParser for RustcObjcSelectorParser { const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; @@ -709,13 +709,13 @@ impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisPa const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis; } -pub(crate) struct EiiForeignItemParser; +pub(crate) struct RustcEiiForeignItemParser; -impl NoArgsAttributeParser for EiiForeignItemParser { +impl NoArgsAttributeParser for RustcEiiForeignItemParser { const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::EiiForeignItem; + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem; } pub(crate) struct PatchableFunctionEntryParser; diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index bdfe7bfb8f1f..2d2994c02cd6 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -292,3 +292,105 @@ impl NoArgsAttributeParser for RustcNoImplicitBoundsParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitBounds; } + +pub(crate) struct DefaultLibAllocatorParser; + +impl NoArgsAttributeParser for DefaultLibAllocatorParser { + const PATH: &[Symbol] = &[sym::default_lib_allocator]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::DefaultLibAllocator; +} + +pub(crate) struct FeatureParser; + +impl CombineAttributeParser for FeatureParser { + const PATH: &[Symbol] = &[sym::feature]; + type Item = Ident; + const CONVERT: ConvertFn = AttributeKind::Feature; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const TEMPLATE: AttributeTemplate = template!(List: &["feature1, feature2, ..."]); + + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let ArgParser::List(list) = args else { + cx.expected_list(cx.attr_span, args); + return Vec::new(); + }; + + if list.is_empty() { + cx.warn_empty_attribute(cx.attr_span); + } + + let mut res = Vec::new(); + + for elem in list.mixed() { + let Some(elem) = elem.meta_item() else { + cx.expected_identifier(elem.span()); + continue; + }; + if let Err(arg_span) = elem.args().no_args() { + cx.expected_no_args(arg_span); + continue; + } + + let path = elem.path(); + let Some(ident) = path.word() else { + cx.expected_identifier(path.span()); + continue; + }; + res.push(ident); + } + + res + } +} + +pub(crate) struct RegisterToolParser; + +impl CombineAttributeParser for RegisterToolParser { + const PATH: &[Symbol] = &[sym::register_tool]; + type Item = Ident; + const CONVERT: ConvertFn = AttributeKind::RegisterTool; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const TEMPLATE: AttributeTemplate = template!(List: &["tool1, tool2, ..."]); + + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let ArgParser::List(list) = args else { + cx.expected_list(cx.attr_span, args); + return Vec::new(); + }; + + if list.is_empty() { + cx.warn_empty_attribute(cx.attr_span); + } + + let mut res = Vec::new(); + + for elem in list.mixed() { + let Some(elem) = elem.meta_item() else { + cx.expected_identifier(elem.span()); + continue; + }; + if let Err(arg_span) = elem.args().no_args() { + cx.expected_no_args(arg_span); + continue; + } + + let path = elem.path(); + let Some(ident) = path.word() else { + cx.expected_identifier(path.span()); + continue; + }; + + res.push(ident); + } + + res + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index c055c2936e95..a2c7e459e0df 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -7,8 +7,6 @@ DeprecatedItemSuggestion, InvalidSince, MissingNote, MissingSince, }; -pub(crate) struct DeprecationParser; - fn get( cx: &AcceptContext<'_, '_, S>, name: Symbol, @@ -33,7 +31,8 @@ fn get( } } -impl SingleAttributeParser for DeprecationParser { +pub(crate) struct DeprecatedParser; +impl SingleAttributeParser for DeprecatedParser { const PATH: &[Symbol] = &[sym::deprecated]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; @@ -164,7 +163,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option( + cx: &mut AcceptContext<'_, '_, S>, + first: &mut Option<(Span, Directive)>, + later: (Span, Directive), +) { + if let Some((_, first)) = first { + if first.is_rustc_attr || later.1.is_rustc_attr { + cx.emit_err(DupesNotAllowed); + } + + merge(cx, &mut first.message, later.1.message, sym::message); + merge(cx, &mut first.label, later.1.label, sym::label); + first.notes.extend(later.1.notes); + } else { + *first = Some(later); + } +} + +fn merge( + cx: &mut AcceptContext<'_, '_, S>, + first: &mut Option<(Span, T)>, + later: Option<(Span, T)>, + option_name: Symbol, +) { + match (first, later) { + (Some(_) | None, None) => {} + (Some((first_span, _)), Some((later_span, _))) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::IgnoredDiagnosticOption { + first_span: *first_span, + later_span, + option_name, + }, + later_span, + ); + } + (first @ None, Some(later)) => { + first.get_or_insert(later); + } + } +} + +fn parse_directive_items<'p, S: Stage>( + cx: &mut AcceptContext<'_, '_, S>, + mode: Mode, + items: impl Iterator, + is_root: bool, +) -> Option { + let condition = None; + let mut message: Option<(Span, _)> = None; + let mut label: Option<(Span, _)> = None; + let mut notes = ThinVec::new(); + let mut parent_label = None; + let mut subcommands = ThinVec::new(); + let mut append_const_msg = None; + + for item in items { + let span = item.span(); + + macro malformed() {{ + match mode { + Mode::RustcOnUnimplemented => { + cx.emit_err(NoValueInOnUnimplemented { span: item.span() }); + } + Mode::DiagnosticOnUnimplemented => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnUnimplementedAttr { span }, + span, + ); + } + Mode::DiagnosticOnConst => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnConstAttr { span }, + span, + ); + } + } + continue; + }} + + macro or_malformed($($code:tt)*) {{ + let Some(ret) = (||{ + Some($($code)*) + })() else { + + malformed!() + }; + ret + }} + + macro duplicate($name: ident, $($first_span:tt)*) {{ + match mode { + Mode::RustcOnUnimplemented => { + cx.emit_err(NoValueInOnUnimplemented { span: item.span() }); + } + Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::IgnoredDiagnosticOption { + first_span: $($first_span)*, + later_span: span, + option_name: $name, + }, + span, + ); + } + } + }} + + let item: &MetaItemParser = or_malformed!(item.meta_item()?); + let name = or_malformed!(item.ident()?).name; + + // Some things like `message = "message"` must have a value. + // But with things like `append_const_msg` that is optional. + let value: Option = match item.args().name_value() { + Some(nv) => Some(or_malformed!(nv.value_as_ident()?)), + None => None, + }; + + let mut parse_format = |input: Ident| { + let snippet = cx.sess.source_map().span_to_snippet(input.span).ok(); + let is_snippet = snippet.is_some(); + match parse_format_string(input.name, snippet, input.span, mode) { + Ok((f, warnings)) => { + for warning in warnings { + let (FormatWarning::InvalidSpecifier { span, .. } + | FormatWarning::PositionalArgument { span, .. }) = warning; + cx.emit_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + AttributeLintKind::MalformedDiagnosticFormat { warning }, + span, + ); + } + + f + } + Err(e) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + AttributeLintKind::DiagnosticWrappedParserError { + description: e.description, + label: e.label, + span: slice_span(input.span, e.span, is_snippet), + }, + input.span, + ); + // We could not parse the input, just use it as-is. + FormatString { + input: input.name, + span: input.span, + pieces: thin_vec![Piece::Lit(input.name)], + } + } + } + }; + match (mode, name) { + (_, sym::message) => { + let value = or_malformed!(value?); + if let Some(message) = &message { + duplicate!(name, message.0) + } else { + message = Some((item.span(), parse_format(value))); + } + } + (_, sym::label) => { + let value = or_malformed!(value?); + if let Some(label) = &label { + duplicate!(name, label.0) + } else { + label = Some((item.span(), parse_format(value))); + } + } + (_, sym::note) => { + let value = or_malformed!(value?); + notes.push(parse_format(value)) + } + + (Mode::RustcOnUnimplemented, sym::append_const_msg) => { + append_const_msg = if let Some(msg) = value { + Some(AppendConstMessage::Custom(msg.name, item.span())) + } else { + Some(AppendConstMessage::Default) + } + } + (Mode::RustcOnUnimplemented, sym::parent_label) => { + let value = or_malformed!(value?); + if parent_label.is_none() { + parent_label = Some(parse_format(value)); + } else { + duplicate!(name, span) + } + } + (Mode::RustcOnUnimplemented, sym::on) => { + if is_root { + let items = or_malformed!(item.args().list()?); + let mut iter = items.mixed(); + let condition: &MetaItemOrLitParser = match iter.next() { + Some(c) => c, + None => { + cx.emit_err(InvalidOnClause::Empty { span }); + continue; + } + }; + + let condition = parse_condition(condition); + + if items.len() < 2 { + // Something like `#[rustc_on_unimplemented(on(.., /* nothing */))]` + // There's a condition but no directive behind it, this is a mistake. + malformed!(); + } + + let mut directive = + or_malformed!(parse_directive_items(cx, mode, iter, false)?); + + match condition { + Ok(c) => { + directive.condition = Some(c); + subcommands.push(directive); + } + Err(e) => { + cx.emit_err(e); + } + } + } else { + malformed!(); + } + } + + _other => { + malformed!(); + } + } + } + + Some(Directive { + is_rustc_attr: matches!(mode, Mode::RustcOnUnimplemented), + condition, + subcommands, + message, + label, + notes, + parent_label, + append_const_msg, + }) +} + +pub(crate) fn parse_format_string( + input: Symbol, + snippet: Option, + span: Span, + mode: Mode, +) -> Result<(FormatString, Vec), ParseError> { + let s = input.as_str(); + let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic); + let pieces: Vec<_> = parser.by_ref().collect(); + + if let Some(err) = parser.errors.into_iter().next() { + return Err(err); + } + let mut warnings = Vec::new(); + + let pieces = pieces + .into_iter() + .map(|piece| match piece { + RpfPiece::Lit(lit) => Piece::Lit(Symbol::intern(lit)), + RpfPiece::NextArgument(arg) => { + warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal); + let arg = parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal); + Piece::Arg(arg) + } + }) + .collect(); + + Ok((FormatString { input, pieces, span }, warnings)) +} + +fn parse_arg( + arg: &Argument<'_>, + mode: Mode, + warnings: &mut Vec, + input_span: Span, + is_source_literal: bool, +) -> FormatArg { + let span = slice_span(input_span, arg.position_span.clone(), is_source_literal); + + match arg.position { + // Something like "hello {name}" + Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) { + // Only `#[rustc_on_unimplemented]` can use these + (Mode::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext, + (Mode::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This, + (Mode::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait, + // Any attribute can use these + (_, kw::SelfUpper) => FormatArg::SelfUpper, + (_, generic_param) => FormatArg::GenericParam { generic_param, span }, + }, + + // `{:1}` and `{}` are ignored + Position::ArgumentIs(idx) => { + warnings.push(FormatWarning::PositionalArgument { + span, + help: format!("use `{{{idx}}}` to print a number in braces"), + }); + FormatArg::AsIs(Symbol::intern(&format!("{{{idx}}}"))) + } + Position::ArgumentImplicitlyIs(_) => { + warnings.push(FormatWarning::PositionalArgument { + span, + help: String::from("use `{{}}` to print empty braces"), + }); + FormatArg::AsIs(sym::empty_braces) + } + } +} + +/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything +/// with specifiers, so emit a warning if they are used. +fn warn_on_format_spec( + spec: &FormatSpec<'_>, + warnings: &mut Vec, + input_span: Span, + is_source_literal: bool, +) { + if spec.ty != "" { + let span = spec + .ty_span + .as_ref() + .map(|inner| slice_span(input_span, inner.clone(), is_source_literal)) + .unwrap_or(input_span); + warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() }) + } +} + +fn slice_span(input: Span, Range { start, end }: Range, is_source_literal: bool) -> Span { + if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input } +} + +pub(crate) fn parse_condition( + input: &MetaItemOrLitParser, +) -> Result { + let span = input.span(); + let pred = parse_predicate(input)?; + Ok(OnUnimplementedCondition { span, pred }) +} + +fn parse_predicate(input: &MetaItemOrLitParser) -> Result { + let Some(meta_item) = input.meta_item() else { + return Err(InvalidOnClause::UnsupportedLiteral { span: input.span() }); + }; + + let Some(predicate) = meta_item.ident() else { + return Err(InvalidOnClause::ExpectedIdentifier { + span: meta_item.path().span(), + path: meta_item.path().get_attribute_path(), + }); + }; + + match meta_item.args() { + ArgParser::List(mis) => match predicate.name { + sym::any => Ok(Predicate::Any(parse_predicate_sequence(mis)?)), + sym::all => Ok(Predicate::All(parse_predicate_sequence(mis)?)), + sym::not => { + if let Some(single) = mis.single() { + Ok(Predicate::Not(Box::new(parse_predicate(single)?))) + } else { + Err(InvalidOnClause::ExpectedOnePredInNot { span: mis.span }) + } + } + invalid_pred => { + Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred }) + } + }, + ArgParser::NameValue(p) => { + let Some(value) = p.value_as_ident() else { + return Err(InvalidOnClause::UnsupportedLiteral { span: p.args_span() }); + }; + let name = parse_name(predicate.name); + let value = parse_filter(value.name); + let kv = NameValue { name, value }; + Ok(Predicate::Match(kv)) + } + ArgParser::NoArgs => { + let flag = parse_flag(predicate)?; + Ok(Predicate::Flag(flag)) + } + } +} + +fn parse_predicate_sequence( + sequence: &MetaItemListParser, +) -> Result, InvalidOnClause> { + sequence.mixed().map(parse_predicate).collect() +} + +fn parse_flag(Ident { name, span }: Ident) -> Result { + match name { + sym::crate_local => Ok(Flag::CrateLocal), + sym::direct => Ok(Flag::Direct), + sym::from_desugaring => Ok(Flag::FromDesugaring), + invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }), + } +} + +fn parse_name(name: Symbol) -> Name { + match name { + kw::SelfUpper => Name::SelfUpper, + sym::from_desugaring => Name::FromDesugaring, + sym::cause => Name::Cause, + generic => Name::GenericArg(generic), + } +} + +fn parse_filter(input: Symbol) -> FilterFormatString { + let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic) + .map(|p| match p { + RpfPiece::Lit(s) => LitOrArg::Lit(Symbol::intern(s)), + // We just ignore formatspecs here + RpfPiece::NextArgument(a) => match a.position { + // In `TypeErrCtxt::on_unimplemented_note` we substitute `"{integral}"` even + // if the integer type has been resolved, to allow targeting all integers. + // `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet, + // from the `Display` impl of `InferTy` to be precise. + // + // Don't try to format these later! + Position::ArgumentNamed(arg @ ("integer" | "integral" | "float")) => { + LitOrArg::Lit(Symbol::intern(&format!("{{{arg}}}"))) + } + + Position::ArgumentNamed(arg) => LitOrArg::Arg(Symbol::intern(arg)), + Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(sym::empty_braces), + Position::ArgumentIs(idx) => LitOrArg::Lit(Symbol::intern(&format!("{{{idx}}}"))), + }, + }) + .collect(); + FilterFormatString { pieces } +} + +#[derive(Diagnostic)] +pub(crate) enum InvalidOnClause { + #[diag("empty `on`-clause in `#[rustc_on_unimplemented]`", code = E0232)] + Empty { + #[primary_span] + #[label("empty `on`-clause here")] + span: Span, + }, + #[diag("expected a single predicate in `not(..)`", code = E0232)] + ExpectedOnePredInNot { + #[primary_span] + #[label("unexpected quantity of predicates here")] + span: Span, + }, + #[diag("literals inside `on`-clauses are not supported", code = E0232)] + UnsupportedLiteral { + #[primary_span] + #[label("unexpected literal here")] + span: Span, + }, + #[diag("expected an identifier inside this `on`-clause", code = E0232)] + ExpectedIdentifier { + #[primary_span] + #[label("expected an identifier here, not `{$path}`")] + span: Span, + path: AttrPath, + }, + #[diag("this predicate is invalid", code = E0232)] + InvalidPredicate { + #[primary_span] + #[label("expected one of `any`, `all` or `not` here, not `{$invalid_pred}`")] + span: Span, + invalid_pred: Symbol, + }, + #[diag("invalid flag in `on`-clause", code = E0232)] + InvalidFlag { + #[primary_span] + #[label( + "expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}`" + )] + span: Span, + invalid_flag: Symbol, + }, +} + +#[derive(Diagnostic)] +#[diag("this attribute must have a value", code = E0232)] +#[note("e.g. `#[rustc_on_unimplemented(message=\"foo\")]`")] +pub(crate) struct NoValueInOnUnimplemented { + #[primary_span] + #[label("expected value here")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag( + "using multiple `rustc_on_unimplemented` (or mixing it with `diagnostic::on_unimplemented`) is not supported" +)] +pub(crate) struct DupesNotAllowed; diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs new file mode 100644 index 000000000000..def4069f6b47 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs @@ -0,0 +1,65 @@ +use rustc_hir::attrs::diagnostic::Directive; +use rustc_hir::lints::AttributeLintKind; +use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES; + +use crate::attributes::diagnostic::*; +use crate::attributes::prelude::*; + +#[derive(Default)] +pub(crate) struct OnConstParser { + span: Option, + directive: Option<(Span, Directive)>, +} + +impl AttributeParser for OnConstParser { + const ATTRIBUTES: AcceptMapping = &[( + &[sym::diagnostic, sym::on_const], + template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), + |this, cx, args| { + if !cx.features().diagnostic_on_const() { + return; + } + + let span = cx.attr_span; + this.span = Some(span); + + let items = match args { + ArgParser::List(items) if items.len() != 0 => items, + ArgParser::NoArgs | ArgParser::List(_) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MissingOptionsForOnConst, + span, + ); + return; + } + ArgParser::NameValue(_) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnConstAttr { span }, + span, + ); + return; + } + }; + + let Some(directive) = + parse_directive_items(cx, Mode::DiagnosticOnConst, items.mixed(), true) + else { + return; + }; + merge_directives(cx, &mut this.directive, (span, directive)); + }, + )]; + + //FIXME Still checked in `check_attr.rs` + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + if let Some(span) = self.span { + Some(AttributeKind::OnConst { span, directive: self.directive.map(|d| Box::new(d.1)) }) + } else { + None + } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs new file mode 100644 index 000000000000..12028059b7d4 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs @@ -0,0 +1,88 @@ +use rustc_hir::attrs::diagnostic::Directive; +use rustc_hir::lints::AttributeLintKind; +use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES; + +use crate::attributes::diagnostic::*; +use crate::attributes::prelude::*; + +#[derive(Default)] +pub(crate) struct OnUnimplementedParser { + span: Option, + directive: Option<(Span, Directive)>, +} + +impl OnUnimplementedParser { + fn parse<'sess, S: Stage>( + &mut self, + cx: &mut AcceptContext<'_, 'sess, S>, + args: &ArgParser, + mode: Mode, + ) { + let span = cx.attr_span; + self.span = Some(span); + + // If target is not a trait, returning early will make `finalize` emit a + // `AttributeKind::OnUnimplemented {span, directive: None }`, to prevent it being + // accidentally used on non-trait items like trait aliases. + if !matches!(cx.target, Target::Trait) { + // Lint later emitted in check_attr + return; + } + + let items = match args { + ArgParser::List(items) if items.len() != 0 => items, + ArgParser::NoArgs | ArgParser::List(_) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MissingOptionsForOnUnimplemented, + span, + ); + return; + } + ArgParser::NameValue(_) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnUnimplementedAttr { span }, + span, + ); + return; + } + }; + + if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) { + merge_directives(cx, &mut self.directive, (span, directive)); + }; + } +} + +impl AttributeParser for OnUnimplementedParser { + const ATTRIBUTES: AcceptMapping = &[ + ( + &[sym::diagnostic, sym::on_unimplemented], + template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), + |this, cx, args| { + this.parse(cx, args, Mode::DiagnosticOnUnimplemented); + }, + ), + ( + &[sym::rustc_on_unimplemented], + template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), + |this, cx, args| { + this.parse(cx, args, Mode::RustcOnUnimplemented); + }, + ), + ]; + //FIXME attribute is not parsed for non-traits but diagnostics are issued in `check_attr.rs` + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + if let Some(span) = self.span { + Some(AttributeKind::OnUnimplemented { + span, + directive: self.directive.map(|d| Box::new(d.1)), + }) + } else { + None + } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/dummy.rs b/compiler/rustc_attr_parsing/src/attributes/dummy.rs index 9f97af48afa0..71d10b23a37f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/dummy.rs +++ b/compiler/rustc_attr_parsing/src/attributes/dummy.rs @@ -7,8 +7,8 @@ use crate::parser::ArgParser; use crate::target_checking::{ALL_TARGETS, AllowedTargets}; -pub(crate) struct DummyParser; -impl SingleAttributeParser for DummyParser { +pub(crate) struct RustcDummyParser; +impl SingleAttributeParser for RustcDummyParser { const PATH: &[Symbol] = &[sym::rustc_dummy]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 21c05611ce29..c4a483157a19 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -524,8 +524,8 @@ impl NoArgsAttributeParser for FfiPureParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure; } -pub(crate) struct StdInternalSymbolParser; -impl NoArgsAttributeParser for StdInternalSymbolParser { +pub(crate) struct RustcStdInternalSymbolParser; +impl NoArgsAttributeParser for RustcStdInternalSymbolParser { const PATH: &[Symbol] = &[sym::rustc_std_internal_symbol]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 60e83a6083ed..76bddacd20bf 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -1,7 +1,7 @@ use super::prelude::*; -pub(crate) struct AsPtrParser; -impl NoArgsAttributeParser for AsPtrParser { +pub(crate) struct RustcAsPtrParser; +impl NoArgsAttributeParser for RustcAsPtrParser { const PATH: &[Symbol] = &[sym::rustc_as_ptr]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ @@ -14,8 +14,8 @@ impl NoArgsAttributeParser for AsPtrParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAsPtr; } -pub(crate) struct PubTransparentParser; -impl NoArgsAttributeParser for PubTransparentParser { +pub(crate) struct RustcPubTransparentParser; +impl NoArgsAttributeParser for RustcPubTransparentParser { const PATH: &[Symbol] = &[sym::rustc_pub_transparent]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ @@ -26,8 +26,8 @@ impl NoArgsAttributeParser for PubTransparentParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPubTransparent; } -pub(crate) struct PassByValueParser; -impl NoArgsAttributeParser for PassByValueParser { +pub(crate) struct RustcPassByValueParser; +impl NoArgsAttributeParser for RustcPassByValueParser { const PATH: &[Symbol] = &[sym::rustc_pass_by_value]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ @@ -38,8 +38,8 @@ impl NoArgsAttributeParser for PassByValueParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassByValue; } -pub(crate) struct RustcShouldNotBeCalledOnConstItems; -impl NoArgsAttributeParser for RustcShouldNotBeCalledOnConstItems { +pub(crate) struct RustcShouldNotBeCalledOnConstItemsParser; +impl NoArgsAttributeParser for RustcShouldNotBeCalledOnConstItemsParser { const PATH: &[Symbol] = &[sym::rustc_should_not_be_called_on_const_items]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 0d328d5cc6a7..223c88972d75 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -30,6 +30,7 @@ mod prelude; pub(crate) mod allow_unstable; +pub(crate) mod autodiff; pub(crate) mod body; pub(crate) mod cfg; pub(crate) mod cfg_select; @@ -39,7 +40,7 @@ pub(crate) mod crate_level; pub(crate) mod debugger; pub(crate) mod deprecation; -pub(crate) mod do_not_recommend; +pub(crate) mod diagnostic; pub(crate) mod doc; pub(crate) mod dummy; pub(crate) mod inline; diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index fb0b8df65284..f8ccc8594e33 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -1,4 +1,4 @@ -use rustc_abi::Align; +use rustc_abi::{Align, Size}; use rustc_ast::{IntTy, LitIntType, LitKind, UintTy}; use rustc_hir::attrs::{IntType, ReprAttr}; @@ -229,7 +229,7 @@ fn parse_repr_align( return None; }; - match parse_alignment(&lit.kind) { + match parse_alignment(&lit.kind, cx) { Ok(literal) => Some(match align_kind { AlignKind::Packed => ReprAttr::ReprPacked(literal), AlignKind::Align => ReprAttr::ReprAlign(literal), @@ -248,30 +248,42 @@ fn parse_repr_align( } } -fn parse_alignment(node: &LitKind) -> Result { - if let LitKind::Int(literal, LitIntType::Unsuffixed) = node { - // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first - if literal.get().is_power_of_two() { - // Only possible error is larger than 2^29 - literal - .get() - .try_into() - .ok() - .and_then(|v| Align::from_bytes(v).ok()) - .ok_or("larger than 2^29") - } else { - Err("not a power of two") - } - } else { - Err("not an unsuffixed integer") +fn parse_alignment( + node: &LitKind, + cx: &AcceptContext<'_, '_, S>, +) -> Result { + let LitKind::Int(literal, LitIntType::Unsuffixed) = node else { + return Err("not an unsuffixed integer".to_string()); + }; + + // `Align::from_bytes` accepts 0 as a valid input, + // so we check if its a power of two first + if !literal.get().is_power_of_two() { + return Err("not a power of two".to_string()); } + // lit must be < 2^29 + let align = literal + .get() + .try_into() + .ok() + .and_then(|a| Align::from_bytes(a).ok()) + .ok_or("larger than 2^29".to_string())?; + + // alignment must not be larger than the pointer width (`isize::MAX`) + let max = Size::from_bits(cx.sess.target.pointer_width).signed_int_max() as u64; + if align.bytes() > max { + return Err(format!( + "alignment larger than `isize::MAX` bytes ({max} for the current target)" + )); + } + Ok(align) } /// Parse #[align(N)]. #[derive(Default)] -pub(crate) struct AlignParser(Option<(Align, Span)>); +pub(crate) struct RustcAlignParser(Option<(Align, Span)>); -impl AlignParser { +impl RustcAlignParser { const PATH: &[Symbol] = &[sym::rustc_align]; const TEMPLATE: AttributeTemplate = template!(List: &[""]); @@ -294,7 +306,7 @@ fn parse(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParse return; }; - match parse_alignment(&lit.kind) { + match parse_alignment(&lit.kind, cx) { Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))), Err(message) => { cx.emit_err(session_diagnostics::InvalidAlignmentValue { @@ -308,7 +320,7 @@ fn parse(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParse } } -impl AttributeParser for AlignParser { +impl AttributeParser for RustcAlignParser { const ATTRIBUTES: AcceptMapping = &[(Self::PATH, Self::TEMPLATE, Self::parse)]; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::Fn), @@ -321,29 +333,29 @@ impl AttributeParser for AlignParser { fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { let (align, span) = self.0?; - Some(AttributeKind::Align { align, span }) + Some(AttributeKind::RustcAlign { align, span }) } } #[derive(Default)] -pub(crate) struct AlignStaticParser(AlignParser); +pub(crate) struct RustcAlignStaticParser(RustcAlignParser); -impl AlignStaticParser { +impl RustcAlignStaticParser { const PATH: &[Symbol] = &[sym::rustc_align_static]; - const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE; + const TEMPLATE: AttributeTemplate = RustcAlignParser::TEMPLATE; fn parse(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) { self.0.parse(cx, args) } } -impl AttributeParser for AlignStaticParser { +impl AttributeParser for RustcAlignStaticParser { const ATTRIBUTES: AcceptMapping = &[(Self::PATH, Self::TEMPLATE, Self::parse)]; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]); fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { let (align, span) = self.0.0?; - Some(AttributeKind::Align { align, span }) + Some(AttributeKind::RustcAlign { align, span }) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs index 71a8fb0dd47d..7c771a71bf63 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -1,5 +1,5 @@ -use rustc_hir::Target; use rustc_hir::attrs::AttributeKind; +use rustc_hir::{MethodKind, Target}; use rustc_span::{Span, Symbol, sym}; use crate::attributes::prelude::Allow; @@ -25,6 +25,20 @@ impl NoArgsAttributeParser for RustcDumpDefParentsParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpDefParents; } +pub(crate) struct RustcDumpInferredOutlivesParser; + +impl NoArgsAttributeParser for RustcDumpInferredOutlivesParser { + const PATH: &[Symbol] = &[sym::rustc_dump_inferred_outlives]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Struct), + Allow(Target::Enum), + Allow(Target::Union), + Allow(Target::TyAlias), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpInferredOutlives; +} + pub(crate) struct RustcDumpItemBoundsParser; impl NoArgsAttributeParser for RustcDumpItemBoundsParser { @@ -34,21 +48,88 @@ impl NoArgsAttributeParser for RustcDumpItemBoundsParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpItemBounds; } +pub(crate) struct RustcDumpObjectLifetimeDefaultsParser; + +impl NoArgsAttributeParser for RustcDumpObjectLifetimeDefaultsParser { + const PATH: &[Symbol] = &[sym::rustc_dump_object_lifetime_defaults]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::AssocConst), + Allow(Target::AssocTy), + Allow(Target::Const), + Allow(Target::Enum), + Allow(Target::Fn), + Allow(Target::ForeignFn), + Allow(Target::Impl { of_trait: false }), + Allow(Target::Impl { of_trait: true }), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Struct), + Allow(Target::Trait), + Allow(Target::TraitAlias), + Allow(Target::TyAlias), + Allow(Target::Union), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpObjectLifetimeDefaults; +} + pub(crate) struct RustcDumpPredicatesParser; impl NoArgsAttributeParser for RustcDumpPredicatesParser { const PATH: &[Symbol] = &[sym::rustc_dump_predicates]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ - Allow(Target::Struct), - Allow(Target::Enum), - Allow(Target::Union), - Allow(Target::Trait), + Allow(Target::AssocConst), Allow(Target::AssocTy), + Allow(Target::Const), + Allow(Target::Delegation { mac: false }), + Allow(Target::Delegation { mac: true }), + Allow(Target::Enum), + Allow(Target::Fn), + Allow(Target::Impl { of_trait: false }), + Allow(Target::Impl { of_trait: true }), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Struct), + Allow(Target::Trait), + Allow(Target::TraitAlias), + Allow(Target::TyAlias), + Allow(Target::Union), ]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpPredicates; } +pub(crate) struct RustcDumpVariancesParser; + +impl NoArgsAttributeParser for RustcDumpVariancesParser { + const PATH: &[Symbol] = &[sym::rustc_dump_variances]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Enum), + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Struct), + Allow(Target::Union), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpVariances; +} + +pub(crate) struct RustcDumpVariancesOfOpaquesParser; + +impl NoArgsAttributeParser for RustcDumpVariancesOfOpaquesParser { + const PATH: &[Symbol] = &[sym::rustc_dump_variances_of_opaques]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpVariancesOfOpaques; +} + pub(crate) struct RustcDumpVtableParser; impl NoArgsAttributeParser for RustcDumpVtableParser { diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 2c94a708a90c..e8b4cb343794 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -71,9 +71,9 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option NoArgsAttributeParser for RustcNeverReturnsNullPointerParser { +impl NoArgsAttributeParser for RustcNeverReturnsNullPtrParser { const PATH: &[Symbol] = &[sym::rustc_never_returns_null_ptr]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ @@ -83,7 +83,7 @@ impl NoArgsAttributeParser for RustcNeverReturnsNullPointerParser { Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), ]); - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNeverReturnsNullPointer; + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNeverReturnsNullPtr; } pub(crate) struct RustcNoImplicitAutorefsParser; @@ -175,6 +175,20 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option NoArgsAttributeParser for RustcInheritOverflowChecksParser { + const PATH: &[Symbol] = &[sym::rustc_inherit_overflow_checks]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Closure), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcInheritOverflowChecks; +} + pub(crate) struct RustcLintOptDenyFieldAccessParser; impl SingleAttributeParser for RustcLintOptDenyFieldAccessParser { @@ -574,15 +588,6 @@ impl NoArgsAttributeParser for RustcLintUntrackedQueryInformationPa const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintUntrackedQueryInformation; } -pub(crate) struct RustcObjectLifetimeDefaultParser; - -impl NoArgsAttributeParser for RustcObjectLifetimeDefaultParser { - const PATH: &[Symbol] = &[sym::rustc_object_lifetime_default]; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcObjectLifetimeDefault; -} - pub(crate) struct RustcSimdMonomorphizeLaneLimitParser; impl SingleAttributeParser for RustcSimdMonomorphizeLaneLimitParser { @@ -854,7 +859,6 @@ fn extend( .collect() } } - pub(crate) struct RustcNonConstTraitMethodParser; impl NoArgsAttributeParser for RustcNonConstTraitMethodParser { @@ -1178,9 +1182,34 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option SingleAttributeParser for RustcSymbolName { +impl NoArgsAttributeParser for RustcDoNotConstCheckParser { + const PATH: &[Symbol] = &[sym::rustc_do_not_const_check]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDoNotConstCheck; +} + +pub(crate) struct RustcNonnullOptimizationGuaranteedParser; + +impl NoArgsAttributeParser for RustcNonnullOptimizationGuaranteedParser { + const PATH: &[Symbol] = &[sym::rustc_nonnull_optimization_guaranteed]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNonnullOptimizationGuaranteed; +} + +pub(crate) struct RustcSymbolNameParser; + +impl SingleAttributeParser for RustcSymbolNameParser { + const PATH: &[Symbol] = &[sym::rustc_symbol_name]; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::TraitImpl)), @@ -1191,7 +1220,6 @@ impl SingleAttributeParser for RustcSymbolName { Allow(Target::Impl { of_trait: false }), ]); const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const PATH: &[Symbol] = &[sym::rustc_symbol_name]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const TEMPLATE: AttributeTemplate = template!(Word); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { @@ -1203,9 +1231,10 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option SingleAttributeParser for RustcDefPath { +impl SingleAttributeParser for RustcDefPathParser { + const PATH: &[Symbol] = &[sym::rustc_def_path]; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::TraitImpl)), @@ -1216,7 +1245,6 @@ impl SingleAttributeParser for RustcDefPath { Allow(Target::Impl { of_trait: false }), ]); const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const PATH: &[Symbol] = &[sym::rustc_def_path]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const TEMPLATE: AttributeTemplate = template!(Word); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { @@ -1228,24 +1256,6 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option NoArgsAttributeParser for RustcIntrinsicParser { - const PATH: &[Symbol] = &[sym::rustc_intrinsic]; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsic; -} - -pub(crate) struct RustcIntrinsicConstStableIndirectParser; - -impl NoArgsAttributeParser for RustcIntrinsicConstStableIndirectParser { - const PATH: &'static [Symbol] = &[sym::rustc_intrinsic_const_stable_indirect]; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsicConstStableIndirect; -} - pub(crate) struct RustcStrictCoherenceParser; impl NoArgsAttributeParser for RustcStrictCoherenceParser { @@ -1295,3 +1305,45 @@ impl NoArgsAttributeParser for PreludeImportParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Use)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::PreludeImport; } + +pub(crate) struct RustcDocPrimitiveParser; + +impl SingleAttributeParser for RustcDocPrimitiveParser { + const PATH: &[Symbol] = &[sym::rustc_doc_primitive]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Mod)]); + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "primitive name"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(nv) = args.name_value() else { + cx.expected_name_value(args.span().unwrap_or(cx.attr_span), None); + return None; + }; + + let Some(value_str) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return None; + }; + + Some(AttributeKind::RustcDocPrimitive(cx.attr_span, value_str)) + } +} + +pub(crate) struct RustcIntrinsicParser; + +impl NoArgsAttributeParser for RustcIntrinsicParser { + const PATH: &[Symbol] = &[sym::rustc_intrinsic]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsic; +} + +pub(crate) struct RustcIntrinsicConstStableIndirectParser; + +impl NoArgsAttributeParser for RustcIntrinsicConstStableIndirectParser { + const PATH: &'static [Symbol] = &[sym::rustc_intrinsic_const_stable_indirect]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsicConstStableIndirect; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index a2be2d42b3e1..e35c10996ceb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -9,7 +9,7 @@ use super::prelude::*; use super::util::parse_version; -use crate::session_diagnostics::{self}; +use crate::session_diagnostics; macro_rules! reject_outside_std { ($cx: ident) => { @@ -177,15 +177,15 @@ fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { } } -pub(crate) struct ConstStabilityIndirectParser; -impl NoArgsAttributeParser for ConstStabilityIndirectParser { +pub(crate) struct RustcConstStableIndirectParser; +impl NoArgsAttributeParser for RustcConstStableIndirectParser { const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), ]); - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConstStabilityIndirect; + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConstStableIndirect; } #[derive(Default)] diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index ac1d360c6280..2775eab1b5d9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -93,28 +93,6 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option NoArgsAttributeParser for RustcVarianceParser { - const PATH: &[Symbol] = &[sym::rustc_variance]; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ - Allow(Target::Struct), - Allow(Target::Enum), - Allow(Target::Union), - ]); - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcVariance; -} - -pub(crate) struct RustcVarianceOfOpaquesParser; - -impl NoArgsAttributeParser for RustcVarianceOfOpaquesParser { - const PATH: &[Symbol] = &[sym::rustc_variance_of_opaques]; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcVarianceOfOpaques; -} - pub(crate) struct ReexportTestHarnessMainParser; impl SingleAttributeParser for ReexportTestHarnessMainParser { @@ -215,20 +193,6 @@ impl NoArgsAttributeParser for RustcEvaluateWhereClausesParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEvaluateWhereClauses; } -pub(crate) struct RustcOutlivesParser; - -impl NoArgsAttributeParser for RustcOutlivesParser { - const PATH: &[Symbol] = &[sym::rustc_outlives]; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ - Allow(Target::Struct), - Allow(Target::Enum), - Allow(Target::Union), - Allow(Target::TyAlias), - ]); - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcOutlives; -} - pub(crate) struct TestRunnerParser; impl SingleAttributeParser for TestRunnerParser { diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index ceaa43948d67..c09e151fc70c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -9,8 +9,8 @@ use crate::target_checking::Policy::{Allow, Warn}; use crate::target_checking::{ALL_TARGETS, AllowedTargets}; -pub(crate) struct SkipDuringMethodDispatchParser; -impl SingleAttributeParser for SkipDuringMethodDispatchParser { +pub(crate) struct RustcSkipDuringMethodDispatchParser; +impl SingleAttributeParser for RustcSkipDuringMethodDispatchParser { const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; @@ -58,8 +58,8 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option NoArgsAttributeParser for ParenSugarParser { +pub(crate) struct RustcParenSugarParser; +impl NoArgsAttributeParser for RustcParenSugarParser { const PATH: &[Symbol] = &[sym::rustc_paren_sugar]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); @@ -81,16 +81,16 @@ impl NoArgsAttributeParser for MarkerParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::Marker; } -pub(crate) struct DenyExplicitImplParser; -impl NoArgsAttributeParser for DenyExplicitImplParser { +pub(crate) struct RustcDenyExplicitImplParser; +impl NoArgsAttributeParser for RustcDenyExplicitImplParser { const PATH: &[Symbol] = &[sym::rustc_deny_explicit_impl]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDenyExplicitImpl; } -pub(crate) struct DynIncompatibleTraitParser; -impl NoArgsAttributeParser for DynIncompatibleTraitParser { +pub(crate) struct RustcDynIncompatibleTraitParser; +impl NoArgsAttributeParser for RustcDynIncompatibleTraitParser { const PATH: &[Symbol] = &[sym::rustc_dyn_incompatible_trait]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); @@ -99,16 +99,16 @@ impl NoArgsAttributeParser for DynIncompatibleTraitParser { // Specialization -pub(crate) struct SpecializationTraitParser; -impl NoArgsAttributeParser for SpecializationTraitParser { +pub(crate) struct RustcSpecializationTraitParser; +impl NoArgsAttributeParser for RustcSpecializationTraitParser { const PATH: &[Symbol] = &[sym::rustc_specialization_trait]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcSpecializationTrait; } -pub(crate) struct UnsafeSpecializationMarkerParser; -impl NoArgsAttributeParser for UnsafeSpecializationMarkerParser { +pub(crate) struct RustcUnsafeSpecializationMarkerParser; +impl NoArgsAttributeParser for RustcUnsafeSpecializationMarkerParser { const PATH: &[Symbol] = &[sym::rustc_unsafe_specialization_marker]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); @@ -117,16 +117,16 @@ impl NoArgsAttributeParser for UnsafeSpecializationMarkerParser { // Coherence -pub(crate) struct CoinductiveParser; -impl NoArgsAttributeParser for CoinductiveParser { +pub(crate) struct RustcCoinductiveParser; +impl NoArgsAttributeParser for RustcCoinductiveParser { const PATH: &[Symbol] = &[sym::rustc_coinductive]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoinductive; } -pub(crate) struct AllowIncoherentImplParser; -impl NoArgsAttributeParser for AllowIncoherentImplParser { +pub(crate) struct RustcAllowIncoherentImplParser; +impl NoArgsAttributeParser for RustcAllowIncoherentImplParser { const PATH: &[Symbol] = &[sym::rustc_allow_incoherent_impl]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index 7fa4487b7c69..58b4a0b2fb1a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -2,9 +2,9 @@ use super::prelude::*; -pub(crate) struct TransparencyParser; +pub(crate) struct RustcMacroTransparencyParser; -impl SingleAttributeParser for TransparencyParser { +impl SingleAttributeParser for RustcMacroTransparencyParser { const PATH: &[Symbol] = &[sym::rustc_macro_transparency]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Custom(|cx, used, unused| { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 17a92641f10f..190568bed508 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -19,6 +19,7 @@ use crate::AttributeParser; // Glob imports to avoid big, bitrotty import lists use crate::attributes::allow_unstable::*; +use crate::attributes::autodiff::*; use crate::attributes::body::*; use crate::attributes::cfi_encoding::*; use crate::attributes::codegen_attrs::*; @@ -26,7 +27,9 @@ use crate::attributes::crate_level::*; use crate::attributes::debugger::*; use crate::attributes::deprecation::*; -use crate::attributes::do_not_recommend::*; +use crate::attributes::diagnostic::do_not_recommend::*; +use crate::attributes::diagnostic::on_const::*; +use crate::attributes::diagnostic::on_unimplemented::*; use crate::attributes::doc::*; use crate::attributes::dummy::*; use crate::attributes::inline::*; @@ -139,27 +142,31 @@ mod late { attribute_parsers!( pub(crate) static ATTRIBUTE_PARSERS = [ // tidy-alphabetical-start - AlignParser, - AlignStaticParser, BodyStabilityParser, ConfusablesParser, ConstStabilityParser, DocParser, MacroUseParser, NakedParser, + OnConstParser, + OnUnimplementedParser, + RustcAlignParser, + RustcAlignStaticParser, RustcCguTestAttributeParser, StabilityParser, UsedParser, // tidy-alphabetical-end // tidy-alphabetical-start - Combine, Combine, Combine, Combine, + Combine, Combine, Combine, + Combine, Combine, + Combine, Combine, Combine, Combine, @@ -174,9 +181,8 @@ mod late { Single, Single, Single, - Single, + Single, Single, - Single, Single, Single, Single, @@ -190,8 +196,6 @@ mod late { Single, Single, Single, - Single, - Single, Single, Single, Single, @@ -201,43 +205,42 @@ mod late { Single, Single, Single, + Single, Single, - Single, + Single, Single, Single, + Single, + Single, Single, Single, Single, Single, Single, Single, + Single, Single, Single, + Single, + Single, Single, Single, Single, - Single, + Single, + Single, Single, Single, Single, - Single, Single, - Single, Single, Single, - Single>, Single>, - Single>, Single>, - Single>, Single>, Single>, Single>, - Single>, Single>, - Single>, - Single>, - Single>, + Single>, Single>, Single>, Single>, @@ -258,31 +261,41 @@ mod late { Single>, Single>, Single>, - Single>, - Single>, Single>, Single>, Single>, Single>, Single>, Single>, - Single>, Single>, Single>, + Single>, + Single>, Single>, Single>, + Single>, + Single>, Single>, Single>, Single>, + Single>, + Single>, Single>, + Single>, Single>, + Single>, Single>, Single>, + Single>, + Single>, Single>, + Single>, Single>, + Single>, Single>, Single>, Single>, + Single>, Single>, Single>, Single>, @@ -290,30 +303,30 @@ mod late { Single>, Single>, Single>, - Single>, + Single>, Single>, Single>, Single>, Single>, + Single>, Single>, - Single>, Single>, - Single>, + Single>, + Single>, Single>, Single>, Single>, + Single>, Single>, Single>, - Single>, + Single>, + Single>, + Single>, Single>, Single>, - Single>, - Single>, - Single>, - Single>, + Single>, Single>, Single>, - Single>, // tidy-alphabetical-end ]; ); diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index fe050250e354..db09572cc56b 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -78,7 +78,6 @@ // tidy-alphabetical-start #![feature(decl_macro)] -#![feature(if_let_guard)] #![feature(iter_intersperse)] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 973635f432e8..354fbab9cfcf 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -522,6 +522,13 @@ fn expected_lit(&mut self) -> Diag<'sess> { return self.parser.dcx().create_err(err); } + if let ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden } = self.should_emit { + // Do not attempt to suggest anything in `Recovery::Forbidden` mode. + // Malformed diagnostic-attr arguments that start with an `if` expression can lead to + // an ICE (https://github.com/rust-lang/rust/issues/152744), because callers may cancel the `InvalidMetaItem` error. + return self.parser.dcx().create_err(err); + } + // Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and // don't `uninterpolate` the token to avoid suggesting anything butchered or questionable // when macro metavariables are involved. diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index e98969dda300..7c2044ec235a 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -1,6 +1,6 @@ use std::num::IntErrorKind; -use rustc_ast::{self as ast}; +use rustc_ast as ast; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, @@ -227,12 +227,12 @@ pub(crate) struct InvalidReprAlignNeedArg { #[derive(Diagnostic)] #[diag("invalid `repr({$repr_arg})` attribute: {$error_part}", code = E0589)] -pub(crate) struct InvalidReprGeneric<'a> { +pub(crate) struct InvalidReprGeneric { #[primary_span] pub span: Span, pub repr_arg: String, - pub error_part: &'a str, + pub error_part: String, } #[derive(Diagnostic)] @@ -479,7 +479,7 @@ pub(crate) struct InvalidTarget { pub(crate) struct InvalidAlignmentValue { #[primary_span] pub span: Span, - pub error_part: &'static str, + pub error_part: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 1582833bf15d..641121597848 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -28,7 +28,7 @@ use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::hygiene::DesugaringKind; -use rustc_span::{BytePos, Ident, Span, Symbol, kw, sym}; +use rustc_span::{BytePos, ExpnKind, Ident, MacroKind, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::error_reporting::traits::call_kind::CallKind; @@ -535,7 +535,7 @@ fn visit_pat(&mut self, p: &'hir hir::Pat<'hir>) { if let Some(pat) = finder.parent_pat { sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string())); } - err.multipart_suggestion_verbose( + err.multipart_suggestion( "borrow this binding in the pattern to avoid moving the value", sugg, Applicability::MachineApplicable, @@ -1157,7 +1157,10 @@ fn suggest_cloning_on_functional_record_update( let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = base.kind else { return }; let (hir::def::Res::Local(_) | hir::def::Res::Def( - DefKind::Const | DefKind::ConstParam | DefKind::Static { .. } | DefKind::AssocConst, + DefKind::Const { .. } + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::AssocConst { .. }, _, )) = path.res else { @@ -1435,6 +1438,12 @@ fn suggest_cloning_inner( expr: &hir::Expr<'_>, ) -> bool { let tcx = self.infcx.tcx; + + // Don't suggest `.clone()` in a derive macro expansion. + if let ExpnKind::Macro(MacroKind::Derive, _) = self.body.span.ctxt().outer_expn_data().kind + { + return false; + } if let Some(_) = self.clone_on_reference(expr) { // Avoid redundant clone suggestion already suggested in `explain_captures`. // See `tests/ui/moves/needs-clone-through-deref.rs` @@ -1509,7 +1518,7 @@ fn suggest_cloning_inner( } else { "consider cloning the value if the performance cost is acceptable" }; - err.multipart_suggestion_verbose(msg, sugg, Applicability::MachineApplicable); + err.multipart_suggestion(msg, sugg, Applicability::MachineApplicable); true } @@ -2759,7 +2768,7 @@ fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) { .chain(finder.closure_call_changes) .collect(); - err.multipart_suggestion_verbose( + err.multipart_suggestion( "try explicitly passing `&Self` into the closure as an argument", sugg, Applicability::MachineApplicable, @@ -3347,7 +3356,7 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { let addition = format!("let {}binding = {};\n{}", mutability, s, " ".repeat(p)); - err.multipart_suggestion_verbose( + err.multipart_suggestion( msg, vec![ (stmt.span.shrink_to_lo(), addition), diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index bc2e99b9ceb5..8ed62c92aa8b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -1,6 +1,7 @@ //! Print diagnostics to explain why values are borrowed. -use rustc_data_structures::assert_matches; +use std::assert_matches; + use rustc_errors::{Applicability, Diag, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 7c895ff63e07..af8f723ff378 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -4,6 +4,7 @@ use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{Applicability, Diag, DiagMessage, EmissionGuarantee, MultiSpan, listify, msg}; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::{ @@ -1309,12 +1310,9 @@ fn explain_captures( && !spans.is_empty() { let mut span: MultiSpan = spans.clone().into(); - err.arg("ty", param_ty.to_string()); - let msg = err.dcx.eagerly_translate_to_string( - msg!("`{$ty}` is made to be an `FnOnce` closure here"), - err.args.iter(), - ); - err.remove_arg("ty"); + let msg = msg!("`{$ty}` is made to be an `FnOnce` closure here") + .arg("ty", param_ty.to_string()) + .format(); for sp in spans { span.push_span_label(sp, msg.clone()); } @@ -1511,11 +1509,7 @@ fn explain_captures( ) } }; - err.multipart_suggestion_verbose( - msg, - sugg, - Applicability::MaybeIncorrect, - ); + err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect); for error in errors { if let FulfillmentErrorCode::Select( SelectionError::Unimplemented, diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 986ade57fb31..54253babafa7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; use rustc_span::def_id::DefId; -use rustc_span::{BytePos, ExpnKind, MacroKind, Span}; +use rustc_span::{BytePos, ExpnKind, MacroKind, Span, sym}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; @@ -294,7 +294,7 @@ fn has_ambiguous_copy(&mut self, ty: Ty<'tcx>) -> bool { // Avoid bogus move errors because of an incoherent `Copy` impl. self.infcx.type_implements_trait(copy_def_id, [ty], self.infcx.param_env).may_apply() - && self.infcx.tcx.coherent_trait(copy_def_id).is_err() + && self.infcx.tcx.ensure_result().coherent_trait(copy_def_id).is_err() } fn report_cannot_move_from_static(&mut self, place: Place<'tcx>, span: Span) -> Diag<'infcx> { @@ -375,7 +375,7 @@ pub(in crate::diagnostics) fn suggest_clone_of_captured_var_in_move_closure( .source_map() .indentation_before(stmt.span) .unwrap_or_else(|| " ".to_string()); - err.multipart_suggestion_verbose( + err.multipart_suggestion( "consider cloning the value before moving it into the closure", vec![ ( @@ -405,7 +405,7 @@ pub(in crate::diagnostics) fn suggest_clone_of_captured_var_in_move_closure( .source_map() .indentation_before(closure_expr.span) .unwrap_or_else(|| " ".to_string()); - err.multipart_suggestion_verbose( + err.multipart_suggestion( "consider cloning the value before moving it into the closure", vec![ ( @@ -626,7 +626,7 @@ fn get_closure_bound_clause_span( let predicates = match parent.kind { hir::ExprKind::Call(callee, _) => { let ty = typeck_result.node_type_opt(callee.hir_id)?; - let ty::FnDef(fn_def_id, args) = ty.kind() else { return None }; + let ty::FnDef(fn_def_id, args) = *ty.kind() else { return None }; tcx.predicates_of(fn_def_id).instantiate(tcx, args) } hir::ExprKind::MethodCall(..) => { @@ -700,14 +700,14 @@ fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span binds_to.sort(); binds_to.dedup(); - self.add_move_error_details(err, &binds_to); + self.add_move_error_details(err, &binds_to, &[]); } } GroupedMoveError::MovesFromValue { mut binds_to, .. } => { binds_to.sort(); binds_to.dedup(); - self.add_move_error_suggestions(err, &binds_to); - self.add_move_error_details(err, &binds_to); + let desugar_spans = self.add_move_error_suggestions(err, &binds_to); + self.add_move_error_details(err, &binds_to, &desugar_spans); } // No binding. Nothing to suggest. GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => { @@ -823,7 +823,7 @@ fn add_borrow_suggestions(&self, err: &mut Diag<'_>, span: Span) { } } - fn add_move_error_suggestions(&self, err: &mut Diag<'_>, binds_to: &[Local]) { + fn add_move_error_suggestions(&self, err: &mut Diag<'_>, binds_to: &[Local]) -> Vec { /// A HIR visitor to associate each binding with a `&` or `&mut` that could be removed to /// make it bind by reference instead (if possible) struct BindingFinder<'tcx> { @@ -843,6 +843,8 @@ struct BindingFinder<'tcx> { ref_pat_for_binding: Vec<(Span, Option<&'tcx hir::Pat<'tcx>>)>, /// Output: ref patterns that can't be removed straightforwardly cannot_remove: FxHashSet, + /// Output: binding spans from destructuring assignment desugaring + desugar_binding_spans: Vec, } impl<'tcx> Visitor<'tcx> for BindingFinder<'tcx> { type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies; @@ -883,16 +885,38 @@ fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { } if let hir::PatKind::Binding(_, _, ident, _) = p.kind { - // the spans in `binding_spans` encompass both the ident and binding mode - if let Some(&bind_sp) = - self.binding_spans.iter().find(|bind_sp| bind_sp.contains(ident.span)) - { - self.ref_pat_for_binding.push((bind_sp, self.ref_pat)); + // Skip synthetic bindings from destructuring assignment desugaring + // These have name `lhs` and their parent is a `LetStmt` with + // `LocalSource::AssignDesugar` + let dominated_by_desugar_assign = ident.name == sym::lhs + && self.tcx.hir_parent_iter(p.hir_id).any(|(_, node)| { + matches!( + node, + hir::Node::LetStmt(&hir::LetStmt { + source: hir::LocalSource::AssignDesugar, + .. + }) + ) + }); + + if dominated_by_desugar_assign { + if let Some(&bind_sp) = + self.binding_spans.iter().find(|bind_sp| bind_sp.contains(ident.span)) + { + self.desugar_binding_spans.push(bind_sp); + } } else { - // we've encountered a binding that we're not reporting a move error for. - // we don't want to change its type, so don't remove the surrounding `&`. - if let Some(ref_pat) = self.ref_pat { - self.cannot_remove.insert(ref_pat.hir_id); + // the spans in `binding_spans` encompass both the ident and binding mode + if let Some(&bind_sp) = + self.binding_spans.iter().find(|bind_sp| bind_sp.contains(ident.span)) + { + self.ref_pat_for_binding.push((bind_sp, self.ref_pat)); + } else { + // we've encountered a binding that we're not reporting a move error for. + // we don't want to change its type, so don't remove the surrounding `&`. + if let Some(ref_pat) = self.ref_pat { + self.cannot_remove.insert(ref_pat.hir_id); + } } } } @@ -913,10 +937,10 @@ fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { binding_spans.push(bind_to.source_info.span); } } - let Some(pat_span) = pat_span else { return }; + let Some(pat_span) = pat_span else { return Vec::new() }; let tcx = self.infcx.tcx; - let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) else { return }; + let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) else { return Vec::new() }; let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); let mut finder = BindingFinder { typeck_results, @@ -928,6 +952,7 @@ fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { has_adjustments: false, ref_pat_for_binding: Vec::new(), cannot_remove: FxHashSet::default(), + desugar_binding_spans: Vec::new(), }; finder.visit_body(body); @@ -952,9 +977,15 @@ fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { for (span, msg, suggestion) in suggestions { err.span_suggestion_verbose(span, msg, suggestion, Applicability::MachineApplicable); } + finder.desugar_binding_spans } - fn add_move_error_details(&self, err: &mut Diag<'_>, binds_to: &[Local]) { + fn add_move_error_details( + &self, + err: &mut Diag<'_>, + binds_to: &[Local], + desugar_spans: &[Span], + ) { for (j, local) in binds_to.iter().enumerate() { let bind_to = &self.body.local_decls[*local]; let binding_span = bind_to.source_info.span; @@ -968,7 +999,11 @@ fn add_move_error_details(&self, err: &mut Diag<'_>, binds_to: &[Local]) { if binds_to.len() == 1 { let place_desc = self.local_name(*local).map(|sym| format!("`{sym}`")); - if let Some(expr) = self.find_expr(binding_span) { + if !desugar_spans.contains(&binding_span) + && let Some(expr) = self.find_expr(binding_span) + { + // The binding_span doesn't correspond to a let binding desugaring + // and is an expression where calling `.clone()` would be valid. let local_place: PlaceRef<'tcx> = (*local).into(); self.suggest_cloning(err, local_place, bind_to.ty, expr, None); } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 96090e85e562..a0e53248c904 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -614,7 +614,7 @@ pub(crate) fn report_mutability_error( } _ => { let local = &self.body.local_decls[local]; - match local.local_info() { + match *local.local_info() { LocalInfo::StaticRef { def_id, .. } => { let span = self.infcx.tcx.def_span(def_id); err.span_label(span, format!("this `static` cannot be {acted_on}")); @@ -1340,7 +1340,7 @@ fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) return; } - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!( "consider changing this to be a mutable {pointer_desc}{}{extra}", if is_trait_sig { @@ -1365,7 +1365,7 @@ fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) if self.infcx.tcx.sess.source_map().is_imported(span) { return; } - err.multipart_suggestion_verbose( + err.multipart_suggestion( "consider using `get_mut`", vec![(span, suggestion)], Applicability::MaybeIncorrect, diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 35f6e26159dc..a3738689ed05 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -290,7 +290,7 @@ fn suggest_static_lifetime_for_gat_from_hrtb( }); if suggestions.len() > 0 { suggestions.dedup(); - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( msg!("consider restricting the type parameter to the `'static` lifetime"), suggestions, Applicability::MaybeIncorrect, @@ -902,7 +902,7 @@ fn add_static_impl_trait_suggestion( spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string())); } - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( format!( "to declare that the trait object {captures}, you can add a lifetime parameter `'a` in the type alias" ), @@ -926,7 +926,7 @@ fn maybe_suggest_constrain_dyn_trait_impl( let tcx = self.infcx.tcx; let ConstraintCategory::CallArgument(Some(func_ty)) = category else { return }; - let ty::FnDef(fn_did, args) = func_ty.kind() else { return }; + let ty::FnDef(fn_did, args) = *func_ty.kind() else { return }; debug!(?fn_did, ?args); // Only suggest this on function calls, not closures @@ -938,7 +938,7 @@ fn maybe_suggest_constrain_dyn_trait_impl( let Ok(Some(instance)) = ty::Instance::try_resolve( tcx, self.infcx.typing_env(self.infcx.param_env), - *fn_did, + fn_did, self.infcx.resolve_vars_if_possible(args), ) else { return; diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 2e6c1dceef98..114f9d864e73 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2,11 +2,9 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] #![feature(default_field_values)] #![feature(file_buffered)] -#![feature(if_let_guard)] #![feature(negative_impls)] #![feature(never_type)] #![feature(rustc_attrs)] @@ -27,7 +25,6 @@ use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::dominators::Dominators; -use rustc_errors::LintDiagnostic; use rustc_hir as hir; use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::LocalDefId; @@ -124,7 +121,7 @@ fn mir_borrowck( // We should eagerly check stalled coroutine obligations from HIR typeck. // Not doing so leads to silent normalization failures later, which will // fail to register opaque types in the next solver. - tcx.check_coroutine_obligations(def)?; + tcx.ensure_result().check_coroutine_obligations(def)?; let input_body: &Body<'_> = &input_body.borrow(); if let Some(guar) = input_body.tainted_by_errors { @@ -715,7 +712,7 @@ fn deref(&self) -> &Self::Target { } } -struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { +pub(crate) struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { root_cx: &'a mut BorrowCheckRootCtxt<'tcx>, infcx: &'infcx BorrowckInferCtxt<'tcx>, body: &'a Body<'tcx>, @@ -1428,13 +1425,15 @@ fn check_backward_incompatible_drop( borrow, Some((WriteKind::StorageDeadOrDrop, place)), ); - this.infcx.tcx.node_span_lint( + this.infcx.tcx.emit_node_span_lint( TAIL_EXPR_DROP_ORDER, CRATE_HIR_ID, borrowed, - |diag| { - session_diagnostics::TailExprDropOrder { borrowed }.decorate_lint(diag); - explain.add_explanation_to_diagnostic(&this, diag, "", None, None); + session_diagnostics::TailExprDropOrder { + borrowed, + callback: |diag| { + explain.add_explanation_to_diagnostic(&this, diag, "", None, None); + }, }, ); // We may stop at the first case @@ -1544,8 +1543,7 @@ fn consume_rvalue( Rvalue::Use(operand) | Rvalue::Repeat(operand, _) | Rvalue::UnaryOp(_ /*un_op*/, operand) - | Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/) - | Rvalue::ShallowInitBox(operand, _ /*ty*/) => { + | Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/) => { self.consume_operand(location, (operand, span), state) } diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index acd01be47070..dd6eb1794757 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -7,7 +7,6 @@ use polonius_engine::{Algorithm, AllFacts, Output}; use rustc_data_structures::frozen::Frozen; -use rustc_hir::attrs::AttributeKind; use rustc_hir::find_attr; use rustc_index::IndexSlice; use rustc_middle::mir::pretty::PrettyPrintMirOptions; @@ -296,7 +295,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>( ) { let tcx = infcx.tcx; let base_def_id = tcx.typeck_root_def_id(body.source.def_id()); - if !find_attr!(tcx.get_all_attrs(base_def_id), AttributeKind::RustcRegions) { + if !find_attr!(tcx, base_def_id, RustcRegions) { return; } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 99567da92ffe..439aa1a91e06 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -297,8 +297,9 @@ fn consume_rvalue(&mut self, location: Location, rvalue: &Rvalue<'tcx>) { Rvalue::Use(operand) | Rvalue::Repeat(operand, _) | Rvalue::UnaryOp(_ /*un_op*/, operand) - | Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/) - | Rvalue::ShallowInitBox(operand, _ /*ty*/) => self.consume_operand(location, operand), + | Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/) => { + self.consume_operand(location, operand) + } &Rvalue::Discriminant(place) => { self.access_place( diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 7bde534dafd2..fea5c2b99037 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -1,6 +1,6 @@ -use rustc_errors::MultiSpan; use rustc_errors::codes::*; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::{GenericArg, Ty}; use rustc_span::Span; @@ -47,7 +47,7 @@ pub(crate) struct GenericDoesNotLiveLongEnough { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("variable does not need to be mutable")] pub(crate) struct VarNeedNotMut { #[suggestion( @@ -595,9 +595,20 @@ pub(crate) struct SimdIntrinsicArgConst { pub intrinsic: String, } -#[derive(LintDiagnostic)] -#[diag("relative drop order changing in Rust 2024")] -pub(crate) struct TailExprDropOrder { - #[label("this temporary value will be dropped at the end of the block")] +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, ()> { + let Self { borrowed, callback } = self; + let mut diag = Diag::new(dcx, level, "relative drop order changing in Rust 2024") + .with_span_label( + borrowed, + "this temporary value will be dropped at the end of the block", + ); + callback(&mut diag); + diag + } } diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index c4d964441b1d..41eb5f0302f4 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -296,7 +296,8 @@ pub(crate) fn create(mut self) -> CreateResult<'tcx> { // - We must compute the normalized signature and then compute implied bounds from that // in order to connect any unconstrained region vars created during normalization to // the types of the locals corresponding to the inputs and outputs of the item. (#136547) - if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) { + if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst { .. }) + { for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { let result: Result<_, ErrorGuaranteed> = param_env .and(DeeplyNormalize { value: ty }) diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 3bce78b4e2e2..4e762b368496 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -7,8 +7,9 @@ //! `RETURN_PLACE` the MIR arguments) are always fully normalized (and //! contain revealed `impl Trait` values). +use std::assert_matches; + use itertools::Itertools; -use rustc_data_structures::assert_matches; use rustc_hir as hir; use rustc_infer::infer::{BoundRegionConversionTime, RegionVariableOrigin}; use rustc_middle::mir::*; diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 7d34d7c88e62..29d38af472c2 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1004,17 +1004,6 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { } } - Rvalue::ShallowInitBox(_operand, ty) => { - let trait_ref = - ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, span), [*ty]); - - self.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::SizedBound, - ); - } - Rvalue::Cast(cast_kind, op, ty) => { match *cast_kind { CastKind::PointerCoercion( @@ -1024,12 +1013,12 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { let is_implicit_coercion = coercion_source == CoercionSource::Implicit; let src_ty = op.ty(self.body, tcx); let mut src_sig = src_ty.fn_sig(tcx); - if let ty::FnDef(def_id, _) = src_ty.kind() + if let ty::FnDef(def_id, _) = *src_ty.kind() && let ty::FnPtr(_, target_hdr) = *ty.kind() && tcx.codegen_fn_attrs(def_id).safe_target_features && target_hdr.safety.is_safe() && let Some(safe_sig) = tcx.adjust_target_feature_sig( - *def_id, + def_id, src_sig, self.body.source.def_id(), ) @@ -2231,7 +2220,6 @@ fn rvalue_user_ty(&self, rvalue: &Rvalue<'tcx>) -> Option, handler: Ident, span: Span, sig_span: Span let body = Some(cx.block_expr(call)); let kind = ItemKind::Fn(Box::new(Fn { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, sig, ident: Ident::from_str_and_span(&global_fn_name(ALLOC_ERROR_HANDLER), span), generics: Generics::default(), diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 62a39d957b9c..a1e14b524513 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -1,3 +1,4 @@ +use rustc_ast as ast; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AsmMacro, token}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; @@ -5,11 +6,11 @@ use rustc_expand::base::*; use rustc_index::bit_set::GrowableBitSet; use rustc_parse::parser::asm::*; +use rustc_parse_format as parse; use rustc_session::lint; use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym}; use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; -use {rustc_ast as ast, rustc_parse_format as parse}; use crate::errors; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 95191b82ff3f..259ee25407a5 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -8,8 +8,7 @@ mod llvm_enzyme { use std::string::String; use rustc_ast::expand::autodiff_attrs::{ - AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity, - valid_ty_for_activity, + DiffActivity, DiffMode, valid_input_activity, valid_ret_activity, valid_ty_for_activity, }; use rustc_ast::token::{Lit, LitKind, Token, TokenKind}; use rustc_ast::tokenstream::*; @@ -20,6 +19,7 @@ mod llvm_enzyme { MetaItemInner, MgcaDisambiguation, PatKind, Path, PathSegment, TyKind, Visibility, }; use rustc_expand::base::{Annotatable, ExtCtxt}; + use rustc_hir::attrs::RustcAutodiff; use rustc_span::{Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, trace}; @@ -87,7 +87,7 @@ pub(crate) fn from_ast( meta_item: &ThinVec, has_ret: bool, mode: DiffMode, - ) -> AutoDiffAttrs { + ) -> RustcAutodiff { let dcx = ecx.sess.dcx(); // Now we check, whether the user wants autodiff in batch/vector mode, or scalar mode. @@ -105,7 +105,7 @@ pub(crate) fn from_ast( span: meta_item[1].span(), width: x, }); - return AutoDiffAttrs::error(); + return RustcAutodiff::error(); } } } else { @@ -129,7 +129,7 @@ pub(crate) fn from_ast( }; } if errors { - return AutoDiffAttrs::error(); + return RustcAutodiff::error(); } // If a return type exist, we need to split the last activity, @@ -145,11 +145,11 @@ pub(crate) fn from_ast( (&DiffActivity::None, activities.as_slice()) }; - AutoDiffAttrs { + RustcAutodiff { mode, width, ret_activity: *ret_activity, - input_activity: input_activity.to_vec(), + input_activity: input_activity.iter().cloned().collect(), } } @@ -214,7 +214,7 @@ pub(crate) fn expand_with_mode( // first get information about the annotable item: visibility, signature, name and generic // parameters. // these will be used to generate the differentiated version of the function - let Some((vis, sig, primal, generics, impl_of_trait)) = (match &item { + let Some((vis, sig, primal, generics, is_impl)) = (match &item { Annotatable::Item(iitem) => { extract_item_info(iitem).map(|(v, s, p, g)| (v, s, p, g, false)) } @@ -224,13 +224,13 @@ pub(crate) fn expand_with_mode( } _ => None, }, - Annotatable::AssocItem(assoc_item, Impl { of_trait }) => match &assoc_item.kind { + Annotatable::AssocItem(assoc_item, Impl { of_trait: _ }) => match &assoc_item.kind { ast::AssocItemKind::Fn(box ast::Fn { sig, ident, generics, .. }) => Some(( assoc_item.vis.clone(), sig.clone(), ident.clone(), generics.clone(), - *of_trait, + true, )), _ => None, }, @@ -309,7 +309,7 @@ pub(crate) fn expand_with_mode( ts.pop(); let ts: TokenStream = TokenStream::from_iter(ts); - let x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret, mode); + let x: RustcAutodiff = 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. @@ -328,13 +328,13 @@ pub(crate) fn expand_with_mode( span, &d_sig, &generics, - impl_of_trait, + is_impl, )], ); // The first element of it is the name of the function to be generated let d_fn = Box::new(ast::Fn { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, sig: d_sig, ident: first_ident(&meta_item_vec[0]), generics, @@ -603,7 +603,7 @@ fn gen_turbofish_expr( fn gen_enzyme_decl( ecx: &ExtCtxt<'_>, sig: &ast::FnSig, - x: &AutoDiffAttrs, + x: &RustcAutodiff, span: Span, ) -> ast::FnSig { let dcx = ecx.sess.dcx(); diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs index 423b6a15b646..b294328b0bb1 100644 --- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs +++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs @@ -1,6 +1,6 @@ //! Attributes injected into the crate root from command line using `-Z crate-attr`. -use rustc_ast::{self as ast}; +use rustc_ast as ast; use rustc_errors::Diag; use rustc_parse::parser::attr::InnerAttrPolicy; use rustc_parse::{parse_in, source_str_to_stream}; diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index d9b82e97cb46..7520df43bf4c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -26,15 +26,17 @@ pub(crate) fn expand_deriving_eq( additional_bounds: Vec::new(), supports_unions: true, methods: vec![MethodDef { - name: sym::assert_receiver_is_total_eq, + name: sym::assert_fields_are_eq, generics: Bounds::empty(), explicit_self: true, nonself_args: vec![], ret_ty: Unit, 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. cx.attr_word(sym::inline, span), cx.attr_nested_word(sym::doc, sym::hidden, span), - cx.attr_nested_word(sym::coverage, sym::off, span) + cx.attr_nested_word(sym::coverage, sym::off, span), ], fieldless_variants_strategy: FieldlessVariantsStrategy::Unify, combine_substructure: combine_substructure(Box::new(|a, b, c| { diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 8984e51c0844..1d9551f93a14 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -136,7 +136,7 @@ pub(crate) fn expand_deriving_coerce_pointee( of_trait: Some(Box::new(ast::TraitImplHeader { safety: ast::Safety::Default, polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, trait_ref, })), constness: ast::Const::No, @@ -159,7 +159,7 @@ pub(crate) fn expand_deriving_coerce_pointee( of_trait: Some(Box::new(ast::TraitImplHeader { safety: ast::Safety::Default, polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, trait_ref, })), constness: ast::Const::No, diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 48a2f19261f6..004b13bcc333 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -30,7 +30,7 @@ pub(crate) fn expand_deriving_debug( name: sym::fmt, generics: Bounds::empty(), explicit_self: true, - nonself_args: vec![(fmtr, sym::f)], + nonself_args: vec![(fmtr, sym::character('f'))], ret_ty: Path(path_std!(fmt::Result)), attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 86d53b23e1f1..263ba2968eab 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -67,7 +67,7 @@ fn default_struct_substructure( cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, - summary: &StaticFields, + summary: &StaticFields<'_>, ) -> BlockOrExpr { let expr = match summary { Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident), @@ -78,16 +78,16 @@ fn default_struct_substructure( Named(fields) => { let default_fields = fields .iter() - .map(|(ident, span, default_val)| { + .map(|&(ident, span, default_val)| { let value = match default_val { // We use `Default::default()`. - None => default_call(cx, *span), + None => default_call(cx, span), // We use the field default const expression. Some(val) => { cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone())) } }; - cx.field_imm(*span, *ident, value) + cx.field_imm(span, ident, value) }) .collect(); cx.expr_struct_ident(trait_span, substr.type_ident, default_fields) diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 3fe5e89ef06d..b392a9623d05 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -303,11 +303,11 @@ pub(crate) enum IsTuple { } /// Fields for a static method -pub(crate) enum StaticFields { +pub(crate) enum StaticFields<'a> { /// Tuple and unit structs/enum variants like this. Unnamed(Vec, IsTuple), /// Normal structs/struct variants. - Named(Vec<(Ident, Span, Option)>), + Named(Vec<(Ident, Span, Option<&'a AnonConst>)>), } /// A summary of the possible sets of fields. @@ -331,7 +331,7 @@ pub(crate) enum SubstructureFields<'a> { EnumDiscr(FieldInfo, Option>), /// A static method where `Self` is a struct. - StaticStruct(&'a ast::VariantData, StaticFields), + StaticStruct(&'a ast::VariantData, StaticFields<'a>), /// A static method where `Self` is an enum. StaticEnum(&'a ast::EnumDef), @@ -540,7 +540,6 @@ pub(crate) fn expand_ext( .filter(|a| { a.has_any_name(&[ sym::allow, - sym::expect, sym::warn, sym::deny, sym::forbid, @@ -596,7 +595,7 @@ fn create_derived_impl( cx: &ExtCtxt<'_>, type_ident: Ident, generics: &Generics, - field_tys: Vec>, + field_tys: Vec<&ast::Ty>, methods: Vec>, is_packed: bool, ) -> Box { @@ -614,7 +613,7 @@ fn create_derived_impl( }, attrs: ast::AttrVec::new(), kind: ast::AssocItemKind::Type(Box::new(ast::TyAlias { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, ident, generics: Generics::default(), after_where_clause: ast::WhereClause::default(), @@ -851,7 +850,7 @@ fn create_derived_impl( of_trait: Some(Box::new(ast::TraitImplHeader { safety: self.safety, polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, trait_ref, })), constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No }, @@ -870,8 +869,7 @@ fn expand_struct_def( from_scratch: bool, is_packed: bool, ) -> Box { - let field_tys: Vec> = - struct_def.fields().iter().map(|field| field.ty.clone()).collect(); + let field_tys = Vec::from_iter(struct_def.fields().iter().map(|field| &*field.ty)); let methods = self .methods @@ -923,11 +921,13 @@ fn expand_enum_def( generics: &Generics, from_scratch: bool, ) -> Box { - let mut field_tys = Vec::new(); - - for variant in &enum_def.variants { - field_tys.extend(variant.data.fields().iter().map(|field| field.ty.clone())); - } + let field_tys = Vec::from_iter( + enum_def + .variants + .iter() + .flat_map(|variant| variant.data.fields()) + .map(|field| &*field.ty), + ); let methods = self .methods @@ -1073,7 +1073,7 @@ fn create_method( let trait_lo_sp = span.shrink_to_lo(); let sig = ast::FnSig { header: ast::FnHeader::default(), decl: fn_decl, span }; - let defaultness = ast::Defaultness::Final; + let defaultness = ast::Defaultness::Implicit; // Create the method. Box::new(ast::AssocItem { @@ -1160,8 +1160,8 @@ fn expand_struct_method_body<'b>( fn expand_static_struct_method_body( &self, cx: &ExtCtxt<'_>, - trait_: &TraitDef<'_>, - struct_def: &VariantData, + trait_: &TraitDef<'a>, + struct_def: &'a VariantData, type_ident: Ident, nonselflike_args: &[Box], ) -> BlockOrExpr { @@ -1480,13 +1480,13 @@ fn expand_static_enum_method_body( // general helper methods. impl<'a> TraitDef<'a> { - fn summarise_struct(&self, cx: &ExtCtxt<'_>, struct_def: &VariantData) -> StaticFields { + fn summarise_struct(&self, cx: &ExtCtxt<'_>, struct_def: &'a VariantData) -> StaticFields<'a> { let mut named_idents = Vec::new(); let mut just_spans = Vec::new(); for field in struct_def.fields() { let sp = field.span.with_ctxt(self.span.ctxt()); match field.ident { - Some(ident) => named_idents.push((ident, sp, field.default.clone())), + Some(ident) => named_idents.push((ident, sp, field.default.as_ref())), _ => just_spans.push(sp), } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 29ab8ee505d6..39d210e14a09 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1,28 +1,29 @@ use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan, SingleLabelManySpans, Subdiagnostic, msg, }; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("avoid using `.intel_syntax`, Intel syntax is the default")] pub(crate) struct AvoidIntelSyntax; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("avoid using `.att_syntax`, prefer using `options(att_syntax)` instead")] pub(crate) struct AvoidAttSyntax; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("include macro expected single expression in source")] pub(crate) struct IncompleteInclude; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("cannot test inner items")] pub(crate) struct UnnameableTestItems; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("duplicated attribute")] pub(crate) struct DuplicateMacroAttribute; @@ -763,15 +764,17 @@ pub(crate) struct FormatUnusedArg { // form of diagnostic. impl Subdiagnostic for FormatUnusedArg { fn add_to_diag(self, diag: &mut Diag<'_, G>) { - diag.arg("named", self.named); - let msg = diag.eagerly_translate(msg!( - "{$named -> - [true] named argument - *[false] argument - } never used" - )); - diag.remove_arg("named"); - diag.span_label(self.span, msg); + diag.span_label( + self.span, + msg!( + "{$named -> + [true] named argument + *[false] argument + } never used" + ) + .arg("named", self.named) + .format(), + ); } } @@ -946,17 +949,14 @@ pub(crate) struct AsmClobberNoReg { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AsmClobberNoReg { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - // eager translation as `span_labels` takes `AsRef` - let lbl1 = dcx.eagerly_translate_to_string(msg!("clobber_abi"), [].into_iter()); - let lbl2 = dcx.eagerly_translate_to_string(msg!("generic outputs"), [].into_iter()); Diag::new( dcx, level, msg!("asm with `clobber_abi` must specify explicit registers for outputs"), ) .with_span(self.spans.clone()) - .with_span_labels(self.clobbers, &lbl1) - .with_span_labels(self.spans, &lbl2) + .with_span_labels(self.clobbers, "clobber_abi") + .with_span_labels(self.spans, "generic outputs") } } diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index d9116182eed0..1bce3c03743a 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -77,7 +77,7 @@ fn allocator_fn(&self, method: &AllocatorMethod) -> Stmt { let sig = FnSig { decl, header, span: self.span }; let body = Some(self.cx.block_expr(result)); let kind = ItemKind::Fn(Box::new(Fn { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, sig, ident: Ident::from_str_and_span(&global_fn_name(method.name), self.span), generics: Generics::default(), diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 08da5935bbea..d39e3d810354 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -3,10 +3,8 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] #![feature(decl_macro)] -#![feature(if_let_guard)] #![feature(iter_order_by)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index a9718d53ac49..5764dfc83927 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -1,12 +1,11 @@ //! The expansion from a test function to the appropriate test struct for libtest //! Ideally, this code would be in libtest but for efficiency and error messages it lives here. -use std::iter; +use std::{assert_matches, iter}; use rustc_ast::{self as ast, GenericParamKind, HasNodeId, attr, join_path_idents}; use rustc_ast_pretty::pprust; use rustc_attr_parsing::AttributeParser; -use rustc_data_structures::assert_matches; use rustc_errors::{Applicability, Diag, Level}; use rustc_expand::base::*; use rustc_hir::Attribute; @@ -283,7 +282,7 @@ pub(crate) fn expand_test_or_bench( // const $ident: test::TestDescAndFn = ast::ItemKind::Const( ast::ConstItem { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, ident: Ident::new(fn_.ident.name, sp), generics: ast::Generics::default(), ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index b5d63511fce9..1bb6d8a6bfd0 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -330,7 +330,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box { let decl = ecx.fn_decl(ThinVec::new(), ast::FnRetTy::Ty(main_ret_ty)); let sig = ast::FnSig { decl, header: ast::FnHeader::default(), span: sp }; - let defaultness = ast::Defaultness::Final; + let defaultness = ast::Defaultness::Implicit; // Honor the reexport_test_harness_main attribute let main_ident = match cx.reexport_test_harness_main { diff --git a/compiler/rustc_codegen_cranelift/.github/scripts/free-disk-space.sh b/compiler/rustc_codegen_cranelift/.github/scripts/free-disk-space.sh new file mode 100755 index 000000000000..06afdaad619f --- /dev/null +++ b/compiler/rustc_codegen_cranelift/.github/scripts/free-disk-space.sh @@ -0,0 +1,259 @@ +#!/bin/bash +# Ported from rust-lang/rust commit d29e4783dff30f9526eeba3929ebfe86c00c9dad in src/ci/scripts/free-disk-space-linux.sh +set -euo pipefail + +# Script inspired by https://github.com/jlumbroso/free-disk-space +isX86() { + local arch + arch=$(uname -m) + if [ "$arch" = "x86_64" ]; then + return 0 + else + return 1 + fi +} + +# In aws codebuild, the variable RUNNER_ENVIRONMENT is "self-hosted". +isGitHubRunner() { + # `:-` means "use the value of RUNNER_ENVIRONMENT if it exists, otherwise use an empty string". + if [[ "${RUNNER_ENVIRONMENT:-}" == "github-hosted" ]]; then + return 0 + else + return 1 + fi +} + +# print a line of the specified character +printSeparationLine() { + for ((i = 0; i < 80; i++)); do + printf "%s" "$1" + done + printf "\n" +} + +# REF: https://stackoverflow.com/a/450821/408734 +getAvailableSpace() { + df -a | awk 'NR > 1 {avail+=$4} END {print avail}' +} + +# REF: https://unix.stackexchange.com/a/44087/60849 +formatByteCount() { + numfmt --to=iec-i --suffix=B --padding=7 "${1}000" +} + +# macro to output saved space +printSavedSpace() { + # Disk space before the operation + local before=${1} + local title=${2:-} + + local after + after=$(getAvailableSpace) + local saved=$((after - before)) + + if [ "$saved" -lt 0 ]; then + echo "::warning::Saved space is negative: $saved. Using '0' as saved space." + saved=0 + fi + + echo "" + printSeparationLine "*" + if [ -n "${title}" ]; then + echo "=> ${title}: Saved $(formatByteCount "$saved")" + else + echo "=> Saved $(formatByteCount "$saved")" + fi + printSeparationLine "*" + echo "" +} + +# macro to print output of df with caption +printDF() { + local caption=${1} + + printSeparationLine "=" + echo "${caption}" + echo "" + echo "$ df -h" + echo "" + df -h + printSeparationLine "=" +} + +removeUnusedFilesAndDirs() { + local to_remove=( + "/usr/share/java" + ) + + if isGitHubRunner; then + to_remove+=( + "/usr/local/aws-sam-cli" + "/usr/local/doc/cmake" + "/usr/local/julia"* + "/usr/local/lib/android" + "/usr/local/share/chromedriver-"* + "/usr/local/share/chromium" + "/usr/local/share/cmake-"* + "/usr/local/share/edge_driver" + "/usr/local/share/emacs" + "/usr/local/share/gecko_driver" + "/usr/local/share/icons" + "/usr/local/share/powershell" + "/usr/local/share/vcpkg" + "/usr/local/share/vim" + "/usr/share/apache-maven-"* + "/usr/share/gradle-"* + "/usr/share/kotlinc" + "/usr/share/miniconda" + "/usr/share/php" + "/usr/share/ri" + "/usr/share/swift" + + # binaries + "/usr/local/bin/azcopy" + "/usr/local/bin/bicep" + "/usr/local/bin/ccmake" + "/usr/local/bin/cmake-"* + "/usr/local/bin/cmake" + "/usr/local/bin/cpack" + "/usr/local/bin/ctest" + "/usr/local/bin/helm" + "/usr/local/bin/kind" + "/usr/local/bin/kustomize" + "/usr/local/bin/minikube" + "/usr/local/bin/packer" + "/usr/local/bin/phpunit" + "/usr/local/bin/pulumi-"* + "/usr/local/bin/pulumi" + "/usr/local/bin/stack" + + # Haskell runtime + "/usr/local/.ghcup" + + # Azure + "/opt/az" + "/usr/share/az_"* + ) + + if [ -n "${AGENT_TOOLSDIRECTORY:-}" ]; then + # Environment variable set by GitHub Actions + to_remove+=( + "${AGENT_TOOLSDIRECTORY}" + ) + else + echo "::warning::AGENT_TOOLSDIRECTORY is not set. Skipping removal." + fi + else + # Remove folders and files present in AWS CodeBuild + to_remove+=( + # binaries + "/usr/local/bin/ecs-cli" + "/usr/local/bin/eksctl" + "/usr/local/bin/kubectl" + + "${HOME}/.gradle" + "${HOME}/.dotnet" + "${HOME}/.goenv" + "${HOME}/.phpenv" + + ) + fi + + for element in "${to_remove[@]}"; do + if [ ! -e "$element" ]; then + # The file or directory doesn't exist. + # Maybe it was removed in a newer version of the runner or it's not present in a + # specific architecture (e.g. ARM). + echo "::warning::Directory or file $element does not exist, skipping." + fi + done + + # Remove all files and directories at once to save time. + sudo rm -rf "${to_remove[@]}" +} + +execAndMeasureSpaceChange() { + local operation=${1} # Function to execute + local title=${2} + + local before + before=$(getAvailableSpace) + $operation + + printSavedSpace "$before" "$title" +} + +# REF: https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh +cleanPackages() { + local packages=( + '^aspnetcore-.*' + '^dotnet-.*' + '^llvm-.*' + '^mongodb-.*' + 'firefox' + 'libgl1-mesa-dri' + 'mono-devel' + 'php.*' + ) + + if isGitHubRunner; then + packages+=( + azure-cli + ) + + if isX86; then + packages+=( + 'google-chrome-stable' + 'google-cloud-cli' + 'google-cloud-sdk' + 'powershell' + ) + fi + else + packages+=( + 'google-chrome-stable' + ) + fi + + WAIT_DPKG_LOCK="-o DPkg::Lock::Timeout=60" + sudo apt-get ${WAIT_DPKG_LOCK} -qq remove -y --fix-missing "${packages[@]}" + + sudo apt-get ${WAIT_DPKG_LOCK} autoremove -y \ + || echo "::warning::The command [sudo apt-get autoremove -y] failed" + sudo apt-get ${WAIT_DPKG_LOCK} clean \ + || echo "::warning::The command [sudo apt-get clean] failed" +} + +# They aren't present in ubuntu 24 runners. +cleanDocker() { + echo "=> Removing the following docker images:" + sudo docker image ls + echo "=> Removing docker images..." + sudo docker image prune --all --force || true +} + +# Remove Swap storage +cleanSwap() { + sudo swapoff -a || true + sudo rm -rf /mnt/swapfile || true + free -h +} + +# Display initial disk space stats +AVAILABLE_INITIAL=$(getAvailableSpace) + +printDF "BEFORE CLEAN-UP:" +echo "" +execAndMeasureSpaceChange cleanPackages "Unused packages" +execAndMeasureSpaceChange cleanDocker "Docker images" +execAndMeasureSpaceChange cleanSwap "Swap storage" +execAndMeasureSpaceChange removeUnusedFilesAndDirs "Unused files and directories" + +# Output saved space statistic +echo "" +printDF "AFTER CLEAN-UP:" + +echo "" +echo "" + +printSavedSpace "$AVAILABLE_INITIAL" "Total saved" diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml index 170c7126c296..3367562f2683 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml @@ -49,6 +49,12 @@ jobs: if: matrix.os == 'ubuntu-latest' run: cat /proc/cpuinfo + - name: Free disk space + if: runner.os == 'Linux' + env: + RUNNER_ENVIRONMENT: github-hosted + run: .github/scripts/free-disk-space.sh + - name: Cache cargo target dir uses: actions/cache@v4 with: diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/audit.yml b/compiler/rustc_codegen_cranelift/.github/workflows/audit.yml index 274b9504beb0..95a4dcd3266d 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/audit.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/audit.yml @@ -13,6 +13,6 @@ jobs: - uses: actions/checkout@v4 - run: | sed -i 's/components.*/components = []/' rust-toolchain.toml - - uses: rustsec/audit-check@v1.4.1 + - uses: rustsec/audit-check@v2.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index 3d13b5540e19..afc1d0d0ab95 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -28,9 +28,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" dependencies = [ "allocator-api2", ] @@ -43,42 +43,42 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cranelift-assembler-x64" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd963a645179fa33834ba61fa63353998543b07f877e208da9eb47d4a70d1e7" +checksum = "0377b13bf002a0774fcccac4f1102a10f04893d24060cf4b7350c87e4cbb647c" dependencies = [ "cranelift-assembler-x64-meta", ] [[package]] name = "cranelift-assembler-x64-meta" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6d5739c9dc6b5553ca758d78d87d127dd19f397f776efecf817b8ba8d0bb01" +checksum = "cfa027979140d023b25bf7509fb7ede3a54c3d3871fb5ead4673c4b633f671a2" dependencies = [ "cranelift-srcgen", ] [[package]] name = "cranelift-bforest" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff402c11bb1c9652b67a3e885e84b1b8d00c13472c8fd85211e06a41a63c3e03" +checksum = "618e4da87d9179a70b3c2f664451ca8898987aa6eb9f487d16988588b5d8cc40" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "769a0d88c2f5539e9c5536a93a7bf164b0dc68d91e3d00723e5b4ffc1440afdc" +checksum = "db53764b5dad233b37b8f5dc54d3caa9900c54579195e00f17ea21f03f71aaa7" [[package]] name = "cranelift-codegen" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4351f721fb3b26add1c180f0a75c7474bab2f903c8b777c6ca65238ded59a78" +checksum = "4ae927f1d8c0abddaa863acd201471d56e7fc6c3925104f4861ed4dc3e28b421" dependencies = [ "bumpalo", "cranelift-assembler-x64", @@ -102,9 +102,9 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f86c0ba5b96713643f4dd0de0df12844de9c7bb137d6829b174b706939aa74" +checksum = "d3fcf1e3e6757834bd2584f4cbff023fcc198e9279dcb5d684b4bb27a9b19f54" dependencies = [ "cranelift-assembler-x64-meta", "cranelift-codegen-shared", @@ -114,33 +114,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f08605eee8d51fd976a970bd5b16c9529b51b624f8af68f80649ffb172eb85a4" +checksum = "205dcb9e6ccf9d368b7466be675ff6ee54a63e36da6fe20e72d45169cf6fd254" [[package]] name = "cranelift-control" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "623aab0a09e40f0cf0b5d35eb7832bae4c4f13e3768228e051a6c1a60e88ef5f" +checksum = "108eca9fcfe86026054f931eceaf57b722c1b97464bf8265323a9b5877238817" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0f066e07e3bcbe38884cc5c94c32c7a90267d69df80f187d9dfe421adaa7c4" +checksum = "a0d96496910065d3165f84ff8e1e393916f4c086f88ac8e1b407678bc78735aa" dependencies = [ "cranelift-bitset", ] [[package]] name = "cranelift-frontend" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40865b02a0e52ca8e580ad64feef530cb1d05f6bb4972b4eef05e3eaeae81701" +checksum = "e303983ad7e23c850f24d9c41fc3cb346e1b930f066d3966545e4c98dac5c9fb" dependencies = [ "cranelift-codegen", "log", @@ -150,15 +150,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "104b3c117ae513e9af1d90679842101193a5ccb96ac9f997966d85ea25be2852" +checksum = "24b0cf8d867d891245836cac7abafb0a5b0ea040a019d720702b3b8bcba40bfa" [[package]] name = "cranelift-jit" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aa5f855cfb8e4253ed2d0dfc1a0b6ebe4912e67aa8b7ee14026ff55ca17f1fe" +checksum = "dcf1e35da6eca2448395f483eb172ce71dd7842f7dc96f44bb8923beafe43c6d" dependencies = [ "anyhow", "cranelift-codegen", @@ -176,9 +176,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d01806b191b59f4fc4680293dd5f554caf2de5b62f95eff5beef7acb46c29c" +checksum = "792ba2a54100e34f8a36e3e329a5207cafd1f0918a031d34695db73c163fdcc7" dependencies = [ "anyhow", "cranelift-codegen", @@ -187,9 +187,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5c54e0a358bc05b48f2032e1c320e7f468da068604f2869b77052eab68eb0fe" +checksum = "e24b641e315443e27807b69c440fe766737d7e718c68beb665a2d69259c77bf3" dependencies = [ "cranelift-codegen", "libc", @@ -198,9 +198,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d17e0216be5daabab616647c1918e06dae0708474ba5f7b7762ac24ea5eb126" +checksum = "ecba1f219a201cf946150538e631defd620c5051b62c52ecb89a0004bab263d4" dependencies = [ "anyhow", "cranelift-codegen", @@ -213,9 +213,9 @@ dependencies = [ [[package]] name = "cranelift-srcgen" -version = "0.127.0" +version = "0.128.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6f4b039f453b66c75e9f7886e5a2af96276e151f44dc19b24b58f9a0c98009" +checksum = "a4e378a54e7168a689486d67ee1f818b7e5356e54ae51a1d7a53f4f13f7f8b7a" [[package]] name = "crc32fast" @@ -257,9 +257,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "foldhash", ] @@ -282,9 +282,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.178" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libloading" @@ -298,9 +298,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "log" @@ -337,27 +337,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] [[package]] name = "regalloc2" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e249c660440317032a71ddac302f25f1d5dff387667bcc3978d1f77aa31ac34" +checksum = "08effbc1fa53aaebff69521a5c05640523fab037b34a4a2c109506bc938246fa" dependencies = [ "allocator-api2", "bumpalo", @@ -446,9 +446,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "syn" -version = "2.0.111" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -457,9 +457,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" +checksum = "b1dd07eb858a2067e2f3c7155d54e929265c264e6f37efe3ee7a8d1b5a1dd0ba" [[package]] name = "unicode-ident" @@ -469,9 +469,9 @@ checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "wasmtime-internal-jit-icache-coherence" -version = "40.0.0" +version = "41.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0858b470463f3e7c73acd6049046049e64be17b98901c2db5047450cf83df1fe" +checksum = "bada5ca1cc47df7d14100e2254e187c2486b426df813cea2dd2553a7469f7674" dependencies = [ "anyhow", "cfg-if", @@ -481,9 +481,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-math" -version = "40.0.0" +version = "41.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222e1a590ece4e898f20af1e541b61d2cb803f2557e7eaff23e6c1db5434454a" +checksum = "cf6f615d528eda9adc6eefb062135f831b5215c348f4c3ec3e143690c730605b" dependencies = [ "libm", ] diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index ee4bde477c47..a7b4664282ed 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,12 +8,12 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.127.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] } -cranelift-frontend = { version = "0.127.0" } -cranelift-module = { version = "0.127.0" } -cranelift-native = { version = "0.127.0" } -cranelift-jit = { version = "0.127.0", optional = true } -cranelift-object = { version = "0.127.0" } +cranelift-codegen = { version = "0.128.3", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] } +cranelift-frontend = { version = "0.128.3" } +cranelift-module = { version = "0.128.3" } +cranelift-native = { version = "0.128.3" } +cranelift-jit = { version = "0.128.3", optional = true } +cranelift-object = { version = "0.128.3" } target-lexicon = "0.13" gimli = { version = "0.32", default-features = false, features = ["write"] } object = { version = "0.37.3", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } @@ -24,12 +24,12 @@ smallvec = "1.8.1" [patch.crates-io] # Uncomment to use an unreleased version of cranelift -#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-40.0.0" } -#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-40.0.0" } -#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-40.0.0" } -#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-40.0.0" } -#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-40.0.0" } -#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-40.0.0" } +#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-41.0.0" } +#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-41.0.0" } +#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-41.0.0" } +#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-41.0.0" } +#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-41.0.0" } +#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-41.0.0" } # Uncomment to use local checkout of cranelift #cranelift-codegen = { path = "../wasmtime/cranelift/codegen" } diff --git a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs index c0a8cc95614f..6b14727cd153 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs @@ -43,6 +43,8 @@ pub(crate) fn build_backend( cmd.arg("--release"); + cmd.arg("-Zno-embed-metadata"); + eprintln!("[BUILD] rustc_codegen_cranelift"); crate::utils::spawn_and_wait(cmd); diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index 7b4c604580c1..5205ec1e8aaa 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -235,17 +235,14 @@ fn build_clif_sysroot_for_triple( if let Some(prefix) = env::var_os("CG_CLIF_STDLIB_REMAP_PATH_PREFIX") { rustflags.push("--remap-path-prefix".to_owned()); - rustflags.push(format!( - "{}={}", - STDLIB_SRC.to_path(dirs).to_str().unwrap(), - prefix.to_str().unwrap() - )); + rustflags.push(format!("library/={}/library", prefix.to_str().unwrap())); } compiler.rustflags.extend(rustflags); let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs); build_cmd.arg("--release"); build_cmd.arg("--features").arg("backtrace panic-unwind"); build_cmd.arg(format!("-Zroot-dir={}", STDLIB_SRC.to_path(dirs).display())); + build_cmd.arg("-Zno-embed-metadata"); build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true"); build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif"); if compiler.triple.contains("apple") { @@ -260,7 +257,7 @@ fn build_clif_sysroot_for_triple( for entry in fs::read_dir(build_dir.join("deps")).unwrap() { let entry = entry.unwrap(); if let Some(ext) = entry.path().extension() { - if ext == "rmeta" || ext == "d" || ext == "dSYM" || ext == "clif" { + if ext == "d" || ext == "dSYM" || ext == "clif" { continue; } } else { diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index 85748a4f8a78..72631355733c 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -20,7 +20,7 @@ aot.mini_core_hello_world testsuite.base_sysroot aot.arbitrary_self_types_pointers_and_wrappers -jit.std_example +#jit.std_example # FIXME(#1619) broken for some reason aot.std_example aot.dst_field_align aot.subslice-patterns-const-eval diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 301547cadaf7..5293b458d8c4 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -622,11 +622,6 @@ fn deref(&self) -> &Self::Target { } } -#[lang = "exchange_malloc"] -unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { - unsafe { libc::malloc(size) } -} - #[lang = "drop"] pub trait Drop { fn drop(&mut self); diff --git a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch index 6ed0b17f679c..7ba4475e3145 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch @@ -17,8 +17,8 @@ index 1e336bf..35e6f54 100644 @@ -2,4 +2,3 @@ // tidy-alphabetical-start -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] - #![cfg_attr(test, feature(cfg_select))] #![feature(array_ptr_get)] + #![feature(array_try_from_fn)] diff --git a/coretests/tests/atomic.rs b/coretests/tests/atomic.rs index b735957..ea728b6 100644 --- a/coretests/tests/atomic.rs diff --git a/compiler/rustc_codegen_cranelift/patches/0028-stdlib-Ensure-va_end-doesn-t-get-emitted-unless-VaList-is-a.patch b/compiler/rustc_codegen_cranelift/patches/0028-stdlib-Ensure-va_end-doesn-t-get-emitted-unless-VaList-is-a.patch new file mode 100644 index 000000000000..2aa93164674f --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0028-stdlib-Ensure-va_end-doesn-t-get-emitted-unless-VaList-is-a.patch @@ -0,0 +1,25 @@ +From 116abc64add4d617104993a7a3011f20bcf31ef2 Mon Sep 17 00:00:00 2001 +From: bjorn3 <17426603+bjorn3@users.noreply.github.com> +Date: Mon, 26 Jan 2026 16:20:58 +0000 +Subject: [PATCH] Ensure va_end doesn't get emitted unless VaList is actually + used + +--- + library/core/src/ffi/va_list.rs | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs +index d0f1553..75129af 100644 +--- a/library/core/src/ffi/va_list.rs ++++ b/library/core/src/ffi/va_list.rs +@@ -217,6 +217,7 @@ impl Clone for VaList<'_> { + + #[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")] + impl<'f> const Drop for VaList<'f> { ++ #[inline] + fn drop(&mut self) { + // SAFETY: this variable argument list is being dropped, so won't be read from again. + unsafe { va_end(self) } +-- +2.43.0 + diff --git a/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch b/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch new file mode 100644 index 000000000000..340f6cc9b0e7 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch @@ -0,0 +1,133 @@ +From 285d5716fcfa6d43a3516d899b73bc85da322c25 Mon Sep 17 00:00:00 2001 +From: xonx <119700621+xonx4l@users.noreply.github.com> +Date: Sun, 15 Feb 2026 14:06:49 +0000 +Subject: [PATCH] Disable f16 math tests for cranelift + +--- + coretests/tests/floats/mod.rs | 26 +++++++++++++------------- + 1 file changed, 13 insertions(+), 13 deletions(-) + +diff --git a/coretests/tests/floats/mod.rs b/coretests/tests/floats/mod.rs +index c61961f8584..d7b4fa20322 100644 +--- a/coretests/tests/floats/mod.rs ++++ b/coretests/tests/floats/mod.rs +@@ -1534,7 +1534,7 @@ fn s_nan() -> Float { + name: powf, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1557,7 +1557,7 @@ fn s_nan() -> Float { + name: exp, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1578,7 +1578,7 @@ fn s_nan() -> Float { + name: exp2, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1598,7 +1598,7 @@ fn s_nan() -> Float { + name: ln, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1620,7 +1620,7 @@ fn s_nan() -> Float { + name: log, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1645,7 +1645,7 @@ fn s_nan() -> Float { + name: log2, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1668,7 +1668,7 @@ fn s_nan() -> Float { + name: log10, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1692,7 +1692,7 @@ fn s_nan() -> Float { + name: asinh, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1725,7 +1725,7 @@ fn s_nan() -> Float { + name: acosh, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1753,7 +1753,7 @@ fn s_nan() -> Float { + name: atanh, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1779,7 +1779,7 @@ fn s_nan() -> Float { + name: gamma, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1814,7 +1814,7 @@ fn s_nan() -> Float { + name: ln_gamma, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -2027,7 +2027,7 @@ fn s_nan() -> Float { + attrs: { + // FIXME(f16_f128): add math tests when available + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +-- +2.50.1 + diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain.toml b/compiler/rustc_codegen_cranelift/rust-toolchain.toml index b157c5879ba7..fe967c84352c 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain.toml +++ b/compiler/rustc_codegen_cranelift/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2025-12-23" +channel = "nightly-2026-02-18" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/jit-helpers.py b/compiler/rustc_codegen_cranelift/scripts/jit-helpers.py new file mode 100644 index 000000000000..4542aef7cb52 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/scripts/jit-helpers.py @@ -0,0 +1,57 @@ +import gdb + + +def jitmap_raw(): + pid = gdb.selected_inferior().pid + jitmap_file = open("/tmp/perf-%d.map" % (pid,), "r") + jitmap = jitmap_file.read() + jitmap_file.close() + return jitmap + + +def jit_functions(): + jitmap = jitmap_raw() + + functions = [] + for line in jitmap.strip().split("\n"): + [addr, size, name] = line.split(" ") + functions.append((int(addr, 16), int(size, 16), name)) + + return functions + + +class JitDecorator(gdb.FrameDecorator.FrameDecorator): + def __init__(self, fobj, name): + super(JitDecorator, self).__init__(fobj) + self.name = name + + def function(self): + return self.name + + +class JitFilter: + """ + A backtrace filter which reads perf map files produced by cranelift-jit. + """ + + def __init__(self): + self.name = "JitFilter" + self.enabled = True + self.priority = 0 + + gdb.current_progspace().frame_filters[self.name] = self + + # FIXME add an actual unwinder or somehow register JITed .eh_frame with gdb to avoid relying on + # gdb unwinder heuristics. + def filter(self, frame_iter): + for frame in frame_iter: + frame_addr = frame.inferior_frame().pc() + for addr, size, name in jit_functions(): + if frame_addr >= addr and frame_addr < addr + size: + yield JitDecorator(frame, name) + break + else: + yield frame + + +JitFilter() diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index c16cb4e538fe..bb9f69b5c974 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -49,25 +49,45 @@ std-features = ["panic-unwind"] EOF cat <( - ); +@@ -2249,7 +2249,7 @@ pub fn parse_download_ci_llvm<'a>( } -- #[cfg(not(test))] + #[cfg(not(test))] - if b && dwn_ctx.is_running_on_ci && CiEnv::is_rust_lang_managed_ci_job() { -- // On rust-lang CI, we must always rebuild LLVM if there were any modifications to it -- panic!( -- "\`llvm.download-ci-llvm\` cannot be set to \`true\` on CI. Use \`if-unchanged\` instead." -- ); -- } -- - // If download-ci-llvm=true we also want to check that CI llvm is available - b && llvm::is_ci_llvm_available_for_target(&dwn_ctx.host_target, asserts) - } ++ if false && dwn_ctx.is_running_on_ci && CiEnv::is_rust_lang_managed_ci_job() { + // On rust-lang CI, we must always rebuild LLVM if there were any modifications to it + panic!( + "\`llvm.download-ci-llvm\` cannot be set to \`true\` on CI. Use \`if-unchanged\` instead." +diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs +index 330fb465de..a4593ed96f 100644 +--- a/src/build_helper/src/git.rs ++++ b/src/build_helper/src/git.rs +@@ -218,7 +218,7 @@ pub fn get_closest_upstream_commit( + config: &GitConfig<'_>, + env: CiEnv, + ) -> Result, String> { +- let base = match env { ++ let base = match CiEnv::None { + CiEnv::None => "HEAD", + CiEnv::GitHubActions => { + // On CI, we should always have a non-upstream merge commit at the tip, EOF popd diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index b25269d1430a..4cad18f2a94f 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -10,11 +10,6 @@ pushd rust command -v rg >/dev/null 2>&1 || cargo install ripgrep -rm -r tests/ui/{lto/,linkage*} || true -for test in $(rg --files-with-matches "lto" tests/{codegen-units,ui,incremental}); do - rm $test -done - # should-fail tests don't work when compiletest is compiled with panic=abort for test in $(rg --files-with-matches "//@ should-fail" tests/{codegen-units,ui,incremental}); do rm $test @@ -38,6 +33,7 @@ rm tests/ui/simd/intrinsic/generic-arithmetic-pass.rs # unimplemented simd_funne rm -r tests/ui/scalable-vectors # scalable vectors are unsupported # exotic linkages +rm -r tests/ui/linkage* rm tests/incremental/hashes/function_interfaces.rs rm tests/incremental/hashes/statics.rs rm -r tests/run-make/naked-symbol-visibility @@ -45,11 +41,13 @@ rm -r tests/run-make/naked-symbol-visibility # variadic arguments rm tests/ui/abi/mir/mir_codegen_calls_variadic.rs # requires float varargs rm tests/ui/c-variadic/naked.rs # same +rm tests/ui/consts/const-eval/c-variadic.rs # same rm tests/ui/abi/variadic-ffi.rs # requires callee side vararg support rm -r tests/run-make/c-link-to-rust-va-list-fn # requires callee side vararg support rm tests/ui/c-variadic/valid.rs # same rm tests/ui/c-variadic/trait-method.rs # same rm tests/ui/c-variadic/inherent-method.rs # same +rm tests/ui/c-variadic/copy.rs # same rm tests/ui/sanitizer/kcfi-c-variadic.rs # same rm tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs # variadics for calling conventions other than C unsupported rm tests/ui/delegation/fn-header.rs @@ -79,6 +77,10 @@ rm -r tests/ui/eii # EII not yet implemented rm -r tests/run-make/forced-unwind-terminate-pof # forced unwinding doesn't take precedence # requires LTO +rm -r tests/ui/lto +for test in $(rg --files-with-matches "lto" tests/{codegen-units,ui,incremental}); do + rm $test +done rm -r tests/run-make/cdylib rm -r tests/run-make/codegen-options-parsing rm -r tests/run-make/lto-* @@ -126,6 +128,14 @@ rm -r tests/run-make/notify-all-emit-artifacts rm -r tests/run-make/reset-codegen-1 rm -r tests/run-make/inline-always-many-cgu rm -r tests/run-make/intrinsic-unreachable +rm -r tests/run-make/artifact-incr-cache +rm -r tests/run-make/artifact-incr-cache-no-obj +rm -r tests/run-make/emit +rm -r tests/run-make/llvm-outputs +rm -r tests/run-make/panic-impl-transitive +rm -r tests/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs +rm -r tests/ui/statics/issue-91050-1.rs +rm -r tests/ui/statics/issue-91050-2.rs # giving different but possibly correct results # ============================================= @@ -134,6 +144,7 @@ rm tests/ui/mir/mir_raw_fat_ptr.rs # same rm tests/ui/consts/issue-33537.rs # same rm tests/ui/consts/const-mut-refs-crate.rs # same rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift +rm -r tests/run-make/short-ice # ICE backtrace begin/end marker mismatch # doesn't work due to the way the rustc test suite is invoked. # should work when using ./x.py test the way it is intended @@ -147,20 +158,15 @@ rm -r tests/run-make-cargo/panic-immediate-abort-codegen # same rm -r tests/run-make/missing-unstable-trait-bound # This disables support for unstable features, but running cg_clif needs some unstable features 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/incr-add-rust-src-component rm tests/ui/errors/remap-path-prefix-sysroot.rs # different sysroot source path rm -r tests/run-make/export/extern-opt # something about rustc version mismatches rm -r tests/run-make/export # same rm -r tests/ui/compiletest-self-test/compile-flags-incremental.rs # needs compiletest compiled with panic=unwind -rm tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs # something going wrong with stdlib source remapping -rm tests/ui/consts/miri_unleashed/drop.rs # same -rm tests/ui/error-emitter/multiline-removal-suggestion.rs # same -rm tests/ui/lint/lint-const-item-mutation.rs # same -rm tests/ui/lint/use-redundant/use-redundant-issue-71450.rs # same -rm tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs # same -rm tests/ui/specialization/const_trait_impl.rs # same -rm tests/ui/thir-print/offset_of.rs # same -rm tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs # same +rm -r tests/ui/extern/extern-types-field-offset.rs # expects /rustc/ rather than /rustc/FAKE_PREFIX +rm -r tests/ui/process/println-with-broken-pipe.rs # same +rm tests/codegen-units/item-collection/opaque-return-impls.rs # extra mono item. possibly due to other configuration # genuine bugs # ============ @@ -170,11 +176,7 @@ rm -r tests/run-make/panic-abort-eh_frame # .eh_frame emitted with panic=abort # bugs in the test suite # ====================== rm tests/ui/process/nofile-limit.rs # TODO some AArch64 linking issue -rm tests/ui/backtrace/synchronized-panic-handler.rs # missing needs-unwind annotation -rm tests/ui/lint/non-snake-case/lint-non-snake-case-crate.rs # same -rm tests/ui/async-await/async-drop/async-drop-initial.rs # same (rust-lang/rust#140493) rm -r tests/ui/codegen/equal-pointers-unequal # make incorrect assumptions about the location of stack variables -rm -r tests/run-make-cargo/rustdoc-scrape-examples-paths # FIXME(rust-lang/rust#145580) incr comp bug rm -r tests/incremental/extern_static/issue-49153.rs # assumes reference to undefined static gets optimized away rm tests/ui/intrinsics/panic-uninitialized-zeroed.rs # really slow with unoptimized libstd diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 5a46f79e2ba0..97a19b8976d3 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -53,8 +53,7 @@ pub(crate) fn conv_to_call_conv( default_call_conv: CallConv, ) -> CallConv { match c { - CanonAbi::Rust | CanonAbi::C => default_call_conv, - CanonAbi::RustCold => CallConv::Cold, + CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::C => default_call_conv, // Cranelift doesn't currently have anything for this. CanonAbi::RustPreserveNone => default_call_conv, diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 1a916c876824..4f483cdc5d6c 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -10,7 +10,7 @@ use rustc_index::IndexVec; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::layout::FnAbiOf; +use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv as _}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_session::config::OutputFilenames; use rustc_span::Symbol; @@ -902,7 +902,6 @@ fn is_wide_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool { lval.write_cvalue_transmute(fx, operand); } Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), - Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in codegen"), } } StatementKind::StorageLive(_) @@ -925,19 +924,26 @@ fn is_wide_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool { count, }) => { let dst = codegen_operand(fx, dst); - let pointee = dst - .layout() - .pointee_info_at(fx, rustc_abi::Size::ZERO) - .expect("Expected pointer"); + + let &ty::RawPtr(pointee, _) = dst.layout().ty.kind() else { + bug!("expected pointer") + }; + let pointee_layout = fx + .tcx + .layout_of(fx.typing_env().as_query_input(pointee)) + .expect("expected pointee to have a layout"); + let elem_size: u64 = pointee_layout.layout.size().bytes(); + let dst = dst.load_scalar(fx); let src = codegen_operand(fx, src).load_scalar(fx); let count = codegen_operand(fx, count).load_scalar(fx); - let elem_size: u64 = pointee.size.bytes(); + let bytes = if elem_size != 1 { fx.bcx.ins().imul_imm(count, elem_size as i64) } else { count }; + fx.bcx.call_memcpy(fx.target_config, dst, src, bytes); } }, diff --git a/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs b/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs index 86bff32dc623..d8977657e305 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs @@ -208,7 +208,7 @@ pub(crate) fn codegen_cast( let ret_ty = if to_ty.bits() < 32 { types::I32 } else { to_ty }; let name = format!( "__fix{sign}tf{size}i", - sign = if from_signed { "" } else { "un" }, + sign = if to_signed { "" } else { "uns" }, size = match ret_ty { types::I32 => 's', types::I64 => 'd', diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index fc5c634d9570..79a321456808 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -10,9 +10,9 @@ use cranelift_object::{ObjectBuilder, ObjectModule}; use rustc_codegen_ssa::assert_module_sources::CguReuse; -use rustc_codegen_ssa::back::write::{CompiledModules, produce_final_output_artifacts}; +use rustc_codegen_ssa::back::write::produce_final_output_artifacts; use rustc_codegen_ssa::base::determine_cgu_reuse; -use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind}; +use rustc_codegen_ssa::{CompiledModule, CompiledModules, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; @@ -54,7 +54,6 @@ fn hash_stable(&self, _: &mut HCX, _: &mut StableHasher) { pub(crate) struct OngoingCodegen { modules: Vec, allocator_module: Option, - crate_info: CrateInfo, concurrency_limiter: ConcurrencyLimiter, } @@ -63,7 +62,7 @@ pub(crate) fn join( self, sess: &Session, outputs: &OutputFilenames, - ) -> (CodegenResults, FxIndexMap) { + ) -> (CompiledModules, FxIndexMap) { let mut work_products = FxIndexMap::default(); let mut modules = vec![]; let disable_incr_cache = disable_incr_cache(); @@ -126,15 +125,7 @@ pub(crate) fn join( produce_final_output_artifacts(sess, &compiled_modules, outputs); - ( - CodegenResults { - crate_info: self.crate_info, - - modules: compiled_modules.modules, - allocator_module: compiled_modules.allocator_module, - }, - work_products, - ) + (compiled_modules, work_products) } } @@ -483,13 +474,6 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { } pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box { - // FIXME handle `-Ctarget-cpu=native` - let target_cpu = match tcx.sess.opts.cg.target_cpu { - Some(ref name) => name, - None => tcx.sess.target.cpu.as_ref(), - } - .to_owned(); - let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; if tcx.dep_graph.is_fully_enabled() { @@ -549,7 +533,6 @@ pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box { Box::new(OngoingCodegen { modules, allocator_module, - crate_info: CrateInfo::new(tcx, target_cpu), concurrency_limiter: concurrency_limiter.0, }) } diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 3a8ca25a5fc0..9bbc338a8e07 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -16,13 +16,14 @@ use crate::prelude::*; use crate::unwind_module::UnwindModule; -fn create_jit_module(tcx: TyCtxt<'_>) -> (UnwindModule, Option) { - let crate_info = CrateInfo::new(tcx, "dummy_target_cpu".to_string()); - +fn create_jit_module( + tcx: TyCtxt<'_>, + crate_info: &CrateInfo, +) -> (UnwindModule, Option) { let isa = crate::build_isa(tcx.sess, true); let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); crate::compiler_builtins::register_functions_for_jit(&mut jit_builder); - jit_builder.symbol_lookup_fn(dep_symbol_lookup_fn(tcx.sess, crate_info)); + jit_builder.symbol_lookup_fn(dep_symbol_lookup_fn(tcx.sess, crate_info.clone())); let mut jit_module = UnwindModule::new(JITModule::new(jit_builder), false); let cx = DebugContext::new(tcx, jit_module.isa(), false, "dummy_cgu_name"); @@ -32,14 +33,14 @@ fn create_jit_module(tcx: TyCtxt<'_>) -> (UnwindModule, Option, jit_args: Vec) -> ! { +pub(crate) fn run_jit(tcx: TyCtxt<'_>, crate_info: &CrateInfo, jit_args: Vec) -> ! { if !tcx.crate_types().contains(&rustc_session::config::CrateType::Executable) { tcx.dcx().fatal("can't jit non-executable crate"); } let output_filenames = tcx.output_filenames(()); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); - let (mut jit_module, mut debug_context) = create_jit_module(tcx); + let (mut jit_module, mut debug_context) = create_jit_module(tcx, crate_info); let mut cached_context = Context::new(); let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 656e7b0aec5b..82c2e91b4b0f 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -40,7 +40,7 @@ use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::settings::{self, Configurable}; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_codegen_ssa::{CodegenResults, TargetConfig}; +use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig}; use rustc_log::tracing::info; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; @@ -180,6 +180,10 @@ fn target_config(&self, sess: &Session) -> TargetConfig { && sess.target.env == Env::Gnu && sess.target.abi != Abi::Llvm); + // FIXME(f128): f128 math operations need f128 math symbols, which currently aren't always + // filled in by compiler-builtins. The only libc that provides these currently is glibc. + let has_reliable_f128_math = has_reliable_f16_f128 && sess.target.env == Env::Gnu; + TargetConfig { target_features, unstable_target_features, @@ -188,7 +192,7 @@ fn target_config(&self, sess: &Session) -> TargetConfig { has_reliable_f16: has_reliable_f16_f128, has_reliable_f16_math: has_reliable_f16_f128, has_reliable_f128: has_reliable_f16_f128, - has_reliable_f128_math: has_reliable_f16_f128, + has_reliable_f128_math, } } @@ -196,12 +200,21 @@ fn print_version(&self) { println!("Cranelift version: {}", cranelift_codegen::VERSION); } - fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { + fn target_cpu(&self, sess: &Session) -> String { + // FIXME handle `-Ctarget-cpu=native` + match sess.opts.cg.target_cpu { + Some(ref name) => name, + None => sess.target.cpu.as_ref(), + } + .to_owned() + } + + fn codegen_crate(&self, tcx: TyCtxt<'_>, _crate_info: &CrateInfo) -> Box { info!("codegen crate {}", tcx.crate_name(LOCAL_CRATE)); let config = self.config.get().unwrap(); if config.jit_mode { #[cfg(feature = "jit")] - driver::jit::run_jit(tcx, config.jit_args.clone()); + driver::jit::run_jit(tcx, _crate_info, config.jit_args.clone()); #[cfg(not(feature = "jit"))] tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); @@ -215,7 +228,7 @@ fn join_codegen( ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, - ) -> (CodegenResults, FxIndexMap) { + ) -> (CompiledModules, FxIndexMap) { ongoing_codegen.downcast::().unwrap().join(sess, outputs) } } diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index 0aba44a88c5a..2e165cc3c129 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -628,11 +628,6 @@ fn deref(&self) -> &Self::Target { } } -#[lang = "exchange_malloc"] -unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { - libc::malloc(size) -} - #[lang = "drop"] pub trait Drop { fn drop(&mut self); diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index a08e3dc0df87..347a15a392af 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -26,7 +26,7 @@ use rustc_codegen_ssa::back::lto::SerializedModule; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter}; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; +use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; @@ -34,7 +34,7 @@ use rustc_session::config::Lto; use tempfile::{TempDir, tempdir}; -use crate::back::write::save_temp_bitcode; +use crate::back::write::{codegen, save_temp_bitcode}; use crate::errors::LtoBitcodeFromRlib; use crate::{GccCodegenBackend, GccContext, LtoMode, to_gcc_opt_level}; @@ -112,7 +112,7 @@ pub(crate) fn run_fat( shared_emitter: &SharedEmitter, each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, -) -> ModuleCodegen { +) -> CompiledModule { let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx); @@ -132,12 +132,12 @@ pub(crate) fn run_fat( fn fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, - _dcx: DiagCtxtHandle<'_>, + dcx: DiagCtxtHandle<'_>, modules: Vec>, mut serialized_modules: Vec<(SerializedModule, CString)>, tmp_path: TempDir, //symbols_below_threshold: &[String], -) -> ModuleCodegen { +) -> CompiledModule { let _timer = prof.generic_activity("GCC_fat_lto_build_monolithic_module"); info!("going for a fat lto"); @@ -260,7 +260,7 @@ fn fat_lto( // of now. module.module_llvm.temp_dir = Some(tmp_path); - module + codegen(cgcx, 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 ddf13558027b..24ea2b66ba7d 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -2,12 +2,10 @@ use gccjit::{Context, OutputKind}; use rustc_codegen_ssa::back::link::ensure_removed; -use rustc_codegen_ssa::back::write::{ - BitcodeSection, CodegenContext, EmitObj, ModuleConfig, SharedEmitter, -}; +use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig}; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_errors::DiagCtxt; +use rustc_errors::DiagCtxtHandle; use rustc_fs_util::link_or_copy; use rustc_log::tracing::debug; use rustc_session::config::OutputType; @@ -20,13 +18,10 @@ pub(crate) fn codegen( cgcx: &CodegenContext, prof: &SelfProfilerRef, - shared_emitter: &SharedEmitter, + dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule { - let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); - let dcx = dcx.handle(); - let _timer = prof.generic_activity_with_arg("GCC_module_codegen", &*module.name); { let context = &module.module_llvm.context; diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index e1937f5c11eb..1d5db049f7dc 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -485,13 +485,14 @@ fn deref<'b>(&'b self) -> &'a Self::Target { } impl<'gcc, 'tcx> BackendTypes for Builder<'_, 'gcc, 'tcx> { - type Value = as BackendTypes>::Value; - type Metadata = as BackendTypes>::Metadata; type Function = as BackendTypes>::Function; type BasicBlock = as BackendTypes>::BasicBlock; - type Type = as BackendTypes>::Type; type Funclet = as BackendTypes>::Funclet; + type Value = as BackendTypes>::Value; + type Type = as BackendTypes>::Type; + type FunctionSignature = as BackendTypes>::FunctionSignature; + type DIScope = as BackendTypes>::DIScope; type DILocation = as BackendTypes>::DILocation; type DIVariable = as BackendTypes>::DIVariable; @@ -1655,6 +1656,10 @@ fn catch_switch( unimplemented!(); } + fn get_funclet_cleanuppad(&self, _funclet: &Funclet) -> RValue<'gcc> { + unimplemented!(); + } + // Atomic Operations fn atomic_cmpxchg( &mut self, diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 86a4eeac89d5..79cae9e02826 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -5,9 +5,10 @@ BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods, }; use rustc_middle::mir::Mutability; -use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, PointerArithmetic, Scalar}; +use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::layout::LayoutOf; +use crate::consts::const_alloc_to_gcc; use crate::context::{CodegenCx, new_array_type}; use crate::type_of::LayoutGccExt; @@ -260,11 +261,13 @@ fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>) -> }; } - let init = self.const_data_from_alloc(alloc); - let alloc = alloc.inner(); - let value = match alloc.mutability { - Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), - _ => self.static_addr_of(init, alloc.align, None), + let value = match alloc.inner().mutability { + Mutability::Mut => self.static_addr_of_mut( + const_alloc_to_gcc(self, alloc), + alloc.inner().align, + None, + ), + _ => self.static_addr_of(alloc, None), }; if !self.sess().fewer_names() { // TODO(antoyo): set value name. @@ -282,8 +285,7 @@ fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>) -> }), ))) .unwrap_memory(); - let init = self.const_data_from_alloc(alloc); - self.static_addr_of(init, alloc.inner().align, None) + self.static_addr_of(alloc, None) } GlobalAlloc::TypeId { .. } => { let val = self.const_usize(offset.bytes()); @@ -311,22 +313,6 @@ fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>) -> } } - fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value { - // We ignore the alignment for the purpose of deduping RValues - // The alignment is not handled / used in any way by `const_alloc_to_gcc`, - // so it is OK to overwrite it here. - let mut mock_alloc = alloc.inner().clone(); - mock_alloc.align = rustc_abi::Align::MAX; - // Check if the rvalue is already in the cache - if so, just return it directly. - if let Some(res) = self.const_cache.borrow().get(&mock_alloc) { - return *res; - } - // Rvalue not in the cache - convert and add it. - let res = crate::consts::const_alloc_to_gcc_uncached(self, alloc); - self.const_cache.borrow_mut().insert(mock_alloc, res); - res - } - fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value { self.context .new_array_access(None, base_addr, self.const_usize(offset.bytes())) diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 6fb96f8832b9..8afa57bc28fc 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -22,6 +22,25 @@ use crate::context::CodegenCx; use crate::type_of::LayoutGccExt; +pub(crate) fn const_alloc_to_gcc<'gcc, 'tcx>( + cx: &CodegenCx<'gcc, 'tcx>, + alloc: ConstAllocation<'_>, +) -> RValue<'gcc> { + // We ignore the alignment for the purpose of deduping RValues + // The alignment is not handled / used in any way by `const_alloc_to_gcc`, + // so it is OK to overwrite it here. + let mut mock_alloc = alloc.inner().clone(); + mock_alloc.align = rustc_abi::Align::MAX; + // Check if the rvalue is already in the cache - if so, just return it directly. + if let Some(res) = cx.const_cache.borrow().get(&mock_alloc) { + return *res; + } + // Rvalue not in the cache - convert and add it. + let res = crate::consts::const_alloc_to_gcc_uncached(cx, alloc); + cx.const_cache.borrow_mut().insert(mock_alloc, res); + res +} + fn set_global_alignment<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, gv: LValue<'gcc>, @@ -37,7 +56,10 @@ fn set_global_alignment<'gcc, 'tcx>( } impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { - fn static_addr_of(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> { + fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> RValue<'gcc> { + let cv = const_alloc_to_gcc(self, alloc); + let align = alloc.inner().align; + if let Some(variable) = self.const_globals.borrow().get(&cv) { if let Some(global_variable) = self.global_lvalues.borrow().get(variable) { let alignment = align.bits() as i32; @@ -361,7 +383,7 @@ fn codegen_static_initializer<'gcc, 'tcx>( def_id: DefId, ) -> Result<(RValue<'gcc>, ConstAllocation<'tcx>), ErrorHandled> { let alloc = cx.tcx.eval_static_initializer(def_id)?; - Ok((cx.const_data_from_alloc(alloc), alloc)) + Ok((const_alloc_to_gcc(cx, alloc), alloc)) } fn check_and_apply_linkage<'gcc, 'tcx>( diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 03f207f45724..ada3d73f612e 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -380,14 +380,14 @@ pub fn bitcast_if_needed( } impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> { - type Value = RValue<'gcc>; - type Metadata = RValue<'gcc>; type Function = Function<'gcc>; - type BasicBlock = Block<'gcc>; - type Type = Type<'gcc>; type Funclet = (); // TODO(antoyo) + type Value = RValue<'gcc>; + type Type = Type<'gcc>; + type FunctionSignature = Type<'gcc>; + type DIScope = (); // TODO(antoyo) type DILocation = Location<'gcc>; type DIVariable = (); // TODO(antoyo) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 7e15a5eeaa5e..fb1127ab4f48 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -712,7 +712,7 @@ fn type_checked_load( &mut self, _vtable: Self::Value, _vtable_byte_offset: u64, - _typeid: Self::Value, + _typeid: &[u8], ) -> Self::Value { // Unsupported. self.context.new_rvalue_from_int(self.int_type, 0) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 5d03d2406870..592eb68ce275 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -19,6 +19,7 @@ #![warn(unused_lifetimes)] #![deny(clippy::pattern_type_mismatch)] #![expect(clippy::uninlined_format_args)] +#![allow(clippy::collapsible_match)] // The rustc crates we need extern crate rustc_abi; @@ -86,14 +87,12 @@ }; use rustc_codegen_ssa::base::codegen_crate; use rustc_codegen_ssa::target_features::cfg_target_feature; -use rustc_codegen_ssa::traits::{ - CodegenBackend, ExtraBackendMethods, ThinBufferMethods, WriteBackendMethods, -}; -use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig}; +use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods}; +use rustc_codegen_ssa::{CompiledModule, CompiledModules, CrateInfo, ModuleCodegen, TargetConfig}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::IntoDynSyncSend; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; @@ -288,11 +287,12 @@ fn provide(&self, providers: &mut Providers) { |tcx, ()| gcc_util::global_gcc_features(tcx.sess) } - fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { - let target_cpu = target_cpu(tcx.sess); - let res = codegen_crate(self.clone(), tcx, target_cpu.to_string()); + fn target_cpu(&self, sess: &Session) -> String { + target_cpu(sess).to_owned() + } - Box::new(res) + fn codegen_crate(&self, tcx: TyCtxt<'_>, crate_info: &CrateInfo) -> Box { + Box::new(codegen_crate(self.clone(), tcx, crate_info)) } fn join_codegen( @@ -300,7 +300,7 @@ fn join_codegen( ongoing_codegen: Box, sess: &Session, _outputs: &OutputFilenames, - ) -> (CodegenResults, FxIndexMap) { + ) -> (CompiledModules, FxIndexMap) { ongoing_codegen .downcast::>() .expect("Expected GccCodegenBackend's OngoingCodegen, found Box") @@ -371,16 +371,6 @@ fn compile_codegen_unit( self.lto_supported.load(Ordering::SeqCst), ) } - - fn target_machine_factory( - &self, - _sess: &Session, - _opt_level: OptLevel, - _features: &[String], - ) -> TargetMachineFactoryFn { - // TODO(antoyo): set opt level. - Arc::new(|_, _| ()) - } } #[derive(Clone, Copy, PartialEq)] @@ -423,22 +413,23 @@ unsafe impl Send for SyncContext {} // FIXME(antoyo): that shouldn't be Sync. Parallel compilation is currently disabled with "CodegenBackend::supports_parallel()". unsafe impl Sync for SyncContext {} -pub struct ThinBuffer; - -impl ThinBufferMethods for ThinBuffer { - fn data(&self) -> &[u8] { - &[] - } -} - impl WriteBackendMethods for GccCodegenBackend { type Module = GccContext; type TargetMachine = (); type ModuleBuffer = ModuleBuffer; type ThinData = (); - type ThinBuffer = ThinBuffer; - fn run_and_optimize_fat_lto( + fn target_machine_factory( + &self, + _sess: &Session, + _opt_level: OptLevel, + _features: &[String], + ) -> TargetMachineFactoryFn { + // TODO(antoyo): set opt level. + Arc::new(|_, _| ()) + } + + fn optimize_and_codegen_fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, @@ -447,7 +438,7 @@ fn run_and_optimize_fat_lto( _exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, - ) -> ModuleCodegen { + ) -> CompiledModule { back::lto::run_fat(cgcx, prof, shared_emitter, each_linked_rlib_for_lto, modules) } @@ -458,20 +449,12 @@ fn run_thin_lto( // FIXME(bjorn3): Limit LTO exports to these symbols _exported_symbols_for_lto: &[String], _each_linked_rlib_for_lto: &[PathBuf], - _modules: Vec<(String, Self::ThinBuffer)>, + _modules: Vec<(String, Self::ModuleBuffer)>, _cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec) { unreachable!() } - fn print_pass_timings(&self) { - unimplemented!(); - } - - fn print_statistics(&self) { - unimplemented!() - } - fn optimize( _cgcx: &CodegenContext, _prof: &SelfProfilerRef, @@ -482,13 +465,13 @@ fn optimize( module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level)); } - fn optimize_thin( + fn optimize_and_codegen_thin( _cgcx: &CodegenContext, _prof: &SelfProfilerRef, _shared_emitter: &SharedEmitter, _tm_factory: TargetMachineFactoryFn, _thin: ThinModule, - ) -> ModuleCodegen { + ) -> CompiledModule { unreachable!() } @@ -499,14 +482,12 @@ fn codegen( module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule { - back::write::codegen(cgcx, prof, shared_emitter, module, config) + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); + let dcx = dcx.handle(); + back::write::codegen(cgcx, prof, dcx, module, config) } - fn prepare_thin(_module: ModuleCodegen) -> (String, Self::ThinBuffer) { - unreachable!() - } - - fn serialize_module(_module: ModuleCodegen) -> (String, Self::ModuleBuffer) { + fn serialize_module(_module: Self::Module, _is_thin: bool) -> Self::ModuleBuffer { unimplemented!(); } } diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 68fca5a17ad3..48d1b0163909 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -288,7 +288,9 @@ fn scalar_gcc_type_at<'gcc>( Float(f) => cx.type_from_float(f), Pointer(address_space) => { // If we know the alignment, pick something better than i8. - let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset) { + let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset) + && pointee.align > rustc_abi::Align::ONE + { cx.type_pointee_for_align(pointee.align) } else { cx.type_i8() diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 90c87494c3c5..0ffff2d331b1 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -31,7 +31,6 @@ rustc_llvm = { path = "../rustc_llvm" } rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } -rustc_query_system = { path = "../rustc_query_system" } rustc_sanitizers = { path = "../rustc_sanitizers" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 80d77be1cc38..1bd40d32285a 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -1,8 +1,9 @@ +use std::assert_matches; + use rustc_abi::{BackendRepr, Float, Integer, Primitive, Scalar}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::mir::operand::OperandValue; use rustc_codegen_ssa::traits::*; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::Instance; use rustc_middle::ty::layout::TyAndLayout; diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index b5ab26aea492..546fa87ff561 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -460,7 +460,8 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( { to_add.push(create_alloc_family_attr(cx.llcx)); if let Some(instance) = instance - && let Some(name) = find_attr!(tcx.get_all_attrs(instance.def_id()), rustc_hir::attrs::AttributeKind::RustcAllocatorZeroedVariant {name} => name) + && let Some(name) = + find_attr!(tcx, instance.def_id(), RustcAllocatorZeroedVariant {name} => name) { to_add.push(llvm::CreateAttrStringValue( cx.llcx, diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 5d272d10930b..f6cd229cb106 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -2,7 +2,6 @@ use std::ffi::{CStr, CString}; use std::fs::File; use std::path::{Path, PathBuf}; -use std::ptr::NonNull; use std::sync::Arc; use std::{io, iter, slice}; @@ -13,7 +12,7 @@ CodegenContext, FatLtoInput, SharedEmitter, TargetMachineFactoryFn, }; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; +use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; @@ -25,7 +24,8 @@ use tracing::{debug, info}; use crate::back::write::{ - self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, save_temp_bitcode, + self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, codegen, + save_temp_bitcode, }; use crate::errors::{LlvmError, LtoBitcodeFromRlib}; use crate::llvm::{self, build_string}; @@ -187,7 +187,7 @@ pub(crate) fn run_thin( dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], - modules: Vec<(String, ThinBuffer)>, + modules: Vec<(String, ModuleBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec) { let (symbols_below_threshold, upstream_modules) = @@ -203,12 +203,6 @@ pub(crate) fn run_thin( thin_lto(cgcx, prof, dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold) } -pub(crate) fn prepare_thin(module: ModuleCodegen) -> (String, ThinBuffer) { - let name = module.name; - let buffer = ThinBuffer::new(module.module_llvm.llmod(), true); - (name, buffer) -} - fn fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, @@ -297,7 +291,7 @@ fn fat_lto( // way we know of to do that is to serialize them to a string and them parse // them later. Not great but hey, that's why it's "fat" LTO, right? for module in in_memory { - let buffer = ModuleBuffer::new(module.module_llvm.llmod()); + let buffer = ModuleBuffer::new(module.module_llvm.llmod(), false); let llmod_id = CString::new(&module.name[..]).unwrap(); serialized_modules.push((SerializedModule::Local(buffer), llmod_id)); } @@ -400,7 +394,7 @@ fn thin_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, - modules: Vec<(String, ThinBuffer)>, + modules: Vec<(String, ModuleBuffer)>, serialized_modules: Vec<(SerializedModule, CString)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, symbols_below_threshold: &[*const libc::c_char], @@ -634,7 +628,9 @@ pub(crate) fn run_pass_manager( }; unsafe { - write::llvm_optimize(cgcx, prof, dcx, module, None, config, opt_level, opt_stage, stage); + write::llvm_optimize( + cgcx, prof, dcx, module, None, None, config, opt_level, opt_stage, stage, + ); } if cfg!(feature = "llvm_enzyme") && enable_ad && !thin { @@ -643,7 +639,7 @@ pub(crate) fn run_pass_manager( if !config.autodiff.contains(&config::AutoDiff::NoPostopt) { unsafe { write::llvm_optimize( - cgcx, prof, dcx, module, None, config, opt_level, opt_stage, stage, + cgcx, prof, dcx, module, None, None, config, opt_level, opt_stage, stage, ); } } @@ -658,31 +654,26 @@ pub(crate) fn run_pass_manager( debug!("lto done"); } -pub struct ModuleBuffer(&'static mut llvm::ModuleBuffer); +#[repr(transparent)] +pub(crate) struct Buffer(&'static mut llvm::Buffer); -unsafe impl Send for ModuleBuffer {} -unsafe impl Sync for ModuleBuffer {} +unsafe impl Send for Buffer {} +unsafe impl Sync for Buffer {} -impl ModuleBuffer { - pub(crate) fn new(m: &llvm::Module) -> ModuleBuffer { - ModuleBuffer(unsafe { llvm::LLVMRustModuleBufferCreate(m) }) - } -} - -impl ModuleBufferMethods for ModuleBuffer { - fn data(&self) -> &[u8] { +impl Buffer { + pub(crate) fn data(&self) -> &[u8] { unsafe { - let ptr = llvm::LLVMRustModuleBufferPtr(self.0); - let len = llvm::LLVMRustModuleBufferLen(self.0); + let ptr = llvm::LLVMRustBufferPtr(self.0); + let len = llvm::LLVMRustBufferLen(self.0); slice::from_raw_parts(ptr, len) } } } -impl Drop for ModuleBuffer { +impl Drop for Buffer { fn drop(&mut self) { unsafe { - llvm::LLVMRustModuleBufferFree(&mut *(self.0 as *mut _)); + llvm::LLVMRustBufferFree(&mut *(self.0 as *mut _)); } } } @@ -700,58 +691,32 @@ fn drop(&mut self) { } } -pub struct ThinBuffer(&'static mut llvm::ThinLTOBuffer); +pub struct ModuleBuffer { + data: Buffer, +} -unsafe impl Send for ThinBuffer {} -unsafe impl Sync for ThinBuffer {} - -impl ThinBuffer { - pub(crate) fn new(m: &llvm::Module, is_thin: bool) -> ThinBuffer { +impl ModuleBuffer { + pub(crate) fn new(m: &llvm::Module, is_thin: bool) -> ModuleBuffer { unsafe { - let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin); - ThinBuffer(buffer) - } - } - - pub(crate) unsafe fn from_raw_ptr(ptr: *mut llvm::ThinLTOBuffer) -> ThinBuffer { - let mut ptr = NonNull::new(ptr).unwrap(); - ThinBuffer(unsafe { ptr.as_mut() }) - } - - pub(crate) fn thin_link_data(&self) -> &[u8] { - unsafe { - let ptr = llvm::LLVMRustThinLTOBufferThinLinkDataPtr(self.0) as *const _; - let len = llvm::LLVMRustThinLTOBufferThinLinkDataLen(self.0); - slice::from_raw_parts(ptr, len) + let buffer = llvm::LLVMRustModuleSerialize(m, is_thin); + ModuleBuffer { data: Buffer(buffer) } } } } -impl ThinBufferMethods for ThinBuffer { +impl ModuleBufferMethods for ModuleBuffer { fn data(&self) -> &[u8] { - unsafe { - let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _; - let len = llvm::LLVMRustThinLTOBufferLen(self.0); - slice::from_raw_parts(ptr, len) - } + self.data.data() } } -impl Drop for ThinBuffer { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustThinLTOBufferFree(&mut *(self.0 as *mut _)); - } - } -} - -pub(crate) fn optimize_thin_module( +pub(crate) fn optimize_and_codegen_thin_module( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, tm_factory: TargetMachineFactoryFn, thin_module: ThinModule, -) -> ModuleCodegen { +) -> CompiledModule { let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); @@ -830,7 +795,7 @@ pub(crate) fn optimize_thin_module( save_temp_bitcode(cgcx, &module, "thin-lto-after-pm"); } } - module + codegen(cgcx, prof, shared_emitter, module, &cgcx.module_config) } /// Maps LLVM module identifiers to their corresponding LLVM LTO cache keys diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 3e3ccd39e674..efd4e55d5a85 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -1,7 +1,6 @@ use std::ffi::{CStr, CString}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; -use std::ptr::null_mut; use std::sync::Arc; use std::{fs, slice, str}; @@ -29,7 +28,7 @@ }; use tracing::{debug, trace}; -use crate::back::lto::ThinBuffer; +use crate::back::lto::{Buffer, ModuleBuffer}; use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ LlvmSelfProfiler, selfprofile_after_pass_callback, selfprofile_before_pass_callback, @@ -336,13 +335,13 @@ pub(crate) fn save_temp_bitcode( &module.name, cgcx.invocation_temp.as_deref(), ); - write_bitcode_to_file(module, &path) + write_bitcode_to_file(&module.module_llvm, &path) } -fn write_bitcode_to_file(module: &ModuleCodegen, path: &Path) { +fn write_bitcode_to_file(module: &ModuleLlvm, path: &Path) { unsafe { let path = path_to_c_string(&path); - let llmod = module.module_llvm.llmod(); + let llmod = module.llmod(); llvm::LLVMWriteBitcodeToFile(llmod, path.as_ptr()); } } @@ -563,7 +562,8 @@ pub(crate) unsafe fn llvm_optimize( prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, module: &ModuleCodegen, - thin_lto_buffer: Option<&mut *mut llvm::ThinLTOBuffer>, + thin_lto_buffer: Option<&mut Option>, + thin_lto_summary_buffer: Option<&mut Option>, config: &ModuleConfig, opt_level: config::OptLevel, opt_stage: llvm::OptStage, @@ -786,8 +786,7 @@ fn handle_offload<'ll>(cx: &'ll SimpleCx<'_>, old_fn: &llvm::Value) { config.verify_llvm_ir, config.lint_llvm_ir, thin_lto_buffer, - config.emit_thin_lto, - config.emit_thin_lto_summary, + thin_lto_summary_buffer, merge_functions, unroll_loops, vectorize_slp, @@ -906,13 +905,8 @@ pub(crate) fn optimize( let _handlers = DiagnosticHandlers::new(cgcx, shared_emitter, llcx, module, CodegenDiagnosticsStage::Opt); - if config.emit_no_opt_bc { - let out = cgcx.output_filenames.temp_path_ext_for_cgu( - "no-opt.bc", - &module.name, - cgcx.invocation_temp.as_deref(), - ); - write_bitcode_to_file(module, &out) + if module.kind == ModuleKind::Regular { + save_temp_bitcode(cgcx, module, "no-opt"); } // FIXME(ZuseZ4): support SanitizeHWAddress and prevent illegal/unsupported opts @@ -933,13 +927,14 @@ pub(crate) fn optimize( // The bitcode obtained during the `codegen` phase is no longer suitable for performing LTO. // It may have undergone LTO due to ThinLocal, so we need to obtain the embedded bitcode at // this point. - let mut thin_lto_buffer = if (module.kind == ModuleKind::Regular + let (mut thin_lto_buffer, mut thin_lto_summary_buffer) = if (module.kind + == ModuleKind::Regular && config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full)) || config.emit_thin_lto_summary { - Some(null_mut()) + (Some(None), config.emit_thin_lto_summary.then_some(None)) } else { - None + (None, None) }; unsafe { llvm_optimize( @@ -948,6 +943,7 @@ pub(crate) fn optimize( dcx, module, thin_lto_buffer.as_mut(), + thin_lto_summary_buffer.as_mut(), config, opt_level, opt_stage, @@ -955,17 +951,18 @@ pub(crate) fn optimize( ) }; if let Some(thin_lto_buffer) = thin_lto_buffer { - let thin_lto_buffer = unsafe { ThinBuffer::from_raw_ptr(thin_lto_buffer) }; + let thin_lto_buffer = thin_lto_buffer.unwrap(); module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec()); let bc_summary_out = cgcx.output_filenames.temp_path_for_cgu( OutputType::ThinLinkBitcode, &module.name, cgcx.invocation_temp.as_deref(), ); - if config.emit_thin_lto_summary + if let Some(thin_lto_summary_buffer) = thin_lto_summary_buffer && let Some(thin_link_bitcode_filename) = bc_summary_out.file_name() { - let summary_data = thin_lto_buffer.thin_link_data(); + let thin_lto_summary_buffer = thin_lto_summary_buffer.unwrap(); + let summary_data = thin_lto_summary_buffer.data(); prof.artifact_size( "llvm_bitcode_summary", thin_link_bitcode_filename.to_string_lossy(), @@ -1033,7 +1030,7 @@ pub(crate) fn codegen( "LLVM_module_codegen_make_bitcode", &*module.name, ); - ThinBuffer::new(llmod, config.emit_thin_lto) + ModuleBuffer::new(llmod, cgcx.lto != Lto::Fat) }; let data = thin.data(); let _timer = prof diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 4ffc836f5559..2d91caf40f3c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -196,13 +196,14 @@ pub(crate) fn load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> & pub(crate) const UNNAMED: *const c_char = c"".as_ptr(); impl<'ll, CX: Borrow>> BackendTypes for GenericBuilder<'_, 'll, CX> { - type Value = as BackendTypes>::Value; - type Metadata = as BackendTypes>::Metadata; type Function = as BackendTypes>::Function; type BasicBlock = as BackendTypes>::BasicBlock; - type Type = as BackendTypes>::Type; type Funclet = as BackendTypes>::Funclet; + type Value = as BackendTypes>::Value; + type Type = as BackendTypes>::Type; + type FunctionSignature = as BackendTypes>::FunctionSignature; + type DIScope = as BackendTypes>::DIScope; type DILocation = as BackendTypes>::DILocation; type DIVariable = as BackendTypes>::DIVariable; @@ -714,7 +715,7 @@ fn scalar_load_metadata<'a, 'll, 'tcx>( } if let Some(pointee) = layout.pointee_info_at(bx, offset) - && let Some(_) = pointee.safe + && pointee.align > Align::ONE { bx.align_metadata(load, pointee.align); } @@ -1296,6 +1297,10 @@ fn catch_switch( ret } + fn get_funclet_cleanuppad(&self, funclet: &Funclet<'ll>) -> &'ll Value { + funclet.cleanuppad() + } + // Atomic Operations fn atomic_cmpxchg( &mut self, diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 4b433e2b6361..f8f6439a7b0e 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -1,9 +1,11 @@ use std::ptr; -use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; +use rustc_ast::expand::autodiff_attrs::{DiffActivity, DiffMode}; use rustc_ast::expand::typetree::FncTree; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_hir::attrs::RustcAutodiff; use rustc_middle::ty::{Instance, PseudoCanonicalInput, TyCtxt, TypingEnv}; use rustc_middle::{bug, ty}; use rustc_target::callconv::PassMode; @@ -18,7 +20,7 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, typing_env: TypingEnv<'tcx>, - da: &mut Vec, + da: &mut ThinVec, ) { let fn_ty = instance.ty(tcx, typing_env); @@ -295,7 +297,7 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( outer_name: &str, ret_ty: &'ll Type, fn_args: &[&'ll Value], - attrs: AutoDiffAttrs, + attrs: &RustcAutodiff, dest: PlaceRef<'tcx, &'ll Value>, fnc_tree: FncTree, ) { diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index a60e2d1f0ab0..18a95e810bee 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -3,11 +3,12 @@ use bitflags::Flags; use llvm::Linkage::*; use rustc_abi::Align; +use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; use rustc_middle::bug; -use rustc_middle::ty::offload_meta::{MappingFlags, OffloadMetadata}; +use rustc_middle::ty::offload_meta::{MappingFlags, OffloadMetadata, OffloadSize}; use crate::builder::Builder; use crate::common::CodegenCx; @@ -450,7 +451,15 @@ pub(crate) fn gen_define_handling<'ll>( // FIXME(offload): add `OMP_MAP_TARGET_PARAM = 0x20` only if necessary let transfer_kernel = vec![MappingFlags::TARGET_PARAM.bits(); transfer_to.len()]; - let offload_sizes = add_priv_unnamed_arr(&cx, &format!(".offload_sizes.{symbol}"), &sizes); + let actual_sizes = sizes + .iter() + .map(|s| match s { + OffloadSize::Static(sz) => *sz, + OffloadSize::Dynamic => 0, + }) + .collect::>(); + let offload_sizes = + add_priv_unnamed_arr(&cx, &format!(".offload_sizes.{symbol}"), &actual_sizes); let memtransfer_begin = add_priv_unnamed_arr(&cx, &format!(".offload_maptypes.{symbol}.begin"), &transfer_to); let memtransfer_kernel = @@ -499,9 +508,6 @@ pub(crate) fn gen_define_handling<'ll>( region_id, }; - // FIXME(Sa4dUs): use this global for constant offload sizes - cx.add_compiler_used_global(result.offload_sizes); - cx.offload_kernel_cache.borrow_mut().insert(symbol, result); result @@ -535,6 +541,15 @@ pub(crate) fn scalar_width<'ll>(cx: &'ll SimpleCx<'_>, ty: &'ll Type) -> u64 { } } +fn get_runtime_size<'ll, 'tcx>( + _cx: &CodegenCx<'ll, 'tcx>, + _val: &'ll Value, + _meta: &OffloadMetadata, +) -> &'ll Value { + // FIXME(Sa4dUs): handle dynamic-size data (e.g. slices) + bug!("offload does not support dynamic sizes yet"); +} + // For each kernel *call*, we now use some of our previous declared globals to move data to and from // the gpu. For now, we only handle the data transfer part of it. // If two consecutive kernels use the same memory, we still move it to the host and back to the gpu. @@ -564,15 +579,17 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( ) { let cx = builder.cx; let OffloadKernelGlobals { + offload_sizes, memtransfer_begin, memtransfer_kernel, memtransfer_end, region_id, - .. } = offload_data; let OffloadKernelDims { num_workgroups, threads_per_block, workgroup_dims, thread_dims } = offload_dims; + let has_dynamic = metadata.iter().any(|m| matches!(m.payload_size, OffloadSize::Dynamic)); + let tgt_decl = offload_globals.launcher_fn; let tgt_target_kernel_ty = offload_globals.launcher_ty; @@ -596,7 +613,24 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( let a2 = builder.direct_alloca(ty, Align::EIGHT, ".offload_ptrs"); // These represent the sizes in bytes, e.g. the entry for `&[f64; 16]` will be 8*16. let ty2 = cx.type_array(cx.type_i64(), num_args); - let a4 = builder.direct_alloca(ty2, Align::EIGHT, ".offload_sizes"); + + let a4 = if has_dynamic { + let alloc = builder.direct_alloca(ty2, Align::EIGHT, ".offload_sizes"); + + builder.memcpy( + alloc, + Align::EIGHT, + offload_sizes, + Align::EIGHT, + cx.get_const_i64(8 * args.len() as u64), + MemFlags::empty(), + None, + ); + + alloc + } else { + offload_sizes + }; //%kernel_args = alloca %struct.__tgt_kernel_arguments, align 8 let a5 = builder.direct_alloca(tgt_kernel_decl, Align::EIGHT, "kernel_args"); @@ -648,9 +682,12 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( builder.store(vals[i as usize], gep1, Align::EIGHT); let gep2 = builder.inbounds_gep(ty, a2, &[i32_0, idx]); builder.store(geps[i as usize], gep2, Align::EIGHT); - let gep3 = builder.inbounds_gep(ty2, a4, &[i32_0, idx]); - // FIXME(offload): write an offload frontend and handle arbitrary types. - builder.store(cx.get_const_i64(metadata[i as usize].payload_size), gep3, Align::EIGHT); + + if matches!(metadata[i as usize].payload_size, OffloadSize::Dynamic) { + let gep3 = builder.inbounds_gep(ty2, a4, &[i32_0, idx]); + let size_val = get_runtime_size(cx, args[i as usize], &metadata[i as usize]); + builder.store(size_val, gep3, Align::EIGHT); + } } // For now we have a very simplistic indexing scheme into our @@ -662,13 +699,14 @@ fn get_geps<'ll, 'tcx>( a1: &'ll Value, a2: &'ll Value, a4: &'ll Value, + is_dynamic: bool, ) -> [&'ll Value; 3] { let cx = builder.cx; let i32_0 = cx.get_const_i32(0); let gep1 = builder.inbounds_gep(ty, a1, &[i32_0, i32_0]); let gep2 = builder.inbounds_gep(ty, a2, &[i32_0, i32_0]); - let gep3 = builder.inbounds_gep(ty2, a4, &[i32_0, i32_0]); + let gep3 = if is_dynamic { builder.inbounds_gep(ty2, a4, &[i32_0, i32_0]) } else { a4 }; [gep1, gep2, gep3] } @@ -692,7 +730,7 @@ fn generate_mapper_call<'ll, 'tcx>( // Step 2) let s_ident_t = offload_globals.ident_t_global; - let geps = get_geps(builder, ty, ty2, a1, a2, a4); + let geps = get_geps(builder, ty, ty2, a1, a2, a4, has_dynamic); generate_mapper_call( builder, geps, @@ -725,7 +763,7 @@ fn generate_mapper_call<'ll, 'tcx>( // %41 = call i32 @__tgt_target_kernel(ptr @1, i64 -1, i32 2097152, i32 256, ptr @.kernel_1.region_id, ptr %kernel_args) // Step 4) - let geps = get_geps(builder, ty, ty2, a1, a2, a4); + let geps = get_geps(builder, ty, ty2, a1, a2, a4, has_dynamic); generate_mapper_call( builder, geps, diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index f2261ab79340..a134e97cc891 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -12,7 +12,7 @@ use rustc_hashes::Hash128; use rustc_hir::def_id::DefId; use rustc_middle::bug; -use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, PointerArithmetic, Scalar}; +use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::TyCtxt; use rustc_session::cstore::DllImport; use tracing::debug; @@ -20,7 +20,7 @@ use crate::consts::const_alloc_to_llvm; pub(crate) use crate::context::CodegenCx; use crate::context::{GenericCx, SCx}; -use crate::llvm::{self, BasicBlock, ConstantInt, FALSE, Metadata, TRUE, ToLlvmBool, Type, Value}; +use crate::llvm::{self, BasicBlock, ConstantInt, FALSE, TRUE, ToLlvmBool, Type, Value}; /* * A note on nomenclature of linking: "extern", "foreign", and "upcall". @@ -82,15 +82,15 @@ pub(crate) fn bundle(&self) -> &llvm::OperandBundle<'ll> { } impl<'ll, CX: Borrow>> BackendTypes for GenericCx<'ll, CX> { - type Value = &'ll Value; - type Metadata = &'ll Metadata; // FIXME(eddyb) replace this with a `Function` "subclass" of `Value`. type Function = &'ll Value; - type BasicBlock = &'ll BasicBlock; - type Type = &'ll Type; type Funclet = Funclet<'ll>; + type Value = &'ll Value; + type Type = &'ll Type; + type FunctionSignature = &'ll Type; + type DIScope = &'ll llvm::debuginfo::DIScope; type DILocation = &'ll llvm::debuginfo::DILocation; type DIVariable = &'ll llvm::debuginfo::DIVariable; @@ -359,10 +359,6 @@ fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: &'ll Type) -> } } - fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value { - const_alloc_to_llvm(self, alloc.inner(), /*static*/ false) - } - fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value { unsafe { llvm::LLVMConstInBoundsGEP2( diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 2b04f81c267f..1bdafbd3e4c6 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -772,8 +772,12 @@ impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { /// /// The pointer will always be in the default address space. If global variables default to a /// different address space, an addrspacecast is inserted. - fn static_addr_of(&self, cv: &'ll Value, align: Align, kind: Option<&str>) -> &'ll Value { - let gv = self.static_addr_of_impl(cv, align, kind); + fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> &'ll Value { + // FIXME: should we cache `const_alloc_to_llvm` to avoid repeating this for the + // same `ConstAllocation`? + let cv = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); + + let gv = self.static_addr_of_impl(cv, alloc.inner().align, kind); // static_addr_of_impl returns the bare global variable, which might not be in the default // address space. Cast to the default address space if necessary. self.const_pointercast(gv, self.type_ptr()) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index b27b1a88f133..18818fd1a56c 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,9 +1,9 @@ +use std::assert_matches; use std::sync::Arc; use itertools::Itertools; use rustc_abi::Align; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods}; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::FxIndexMap; use rustc_index::IndexVec; use rustc_middle::ty::TyCtxt; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index c759d46b7d2f..04c0b6953290 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -480,8 +480,7 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>( }, ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id), ty::Pat(base, _) => return type_di_node(cx, base), - // FIXME(unsafe_binders): impl debug info - ty::UnsafeBinder(_) => unimplemented!(), + ty::UnsafeBinder(_) => build_unsafe_binder_type_di_node(cx, t, unique_type_id), ty::Alias(..) | ty::Param(_) | ty::Bound(..) @@ -1488,6 +1487,56 @@ fn build_vtable_type_di_node<'ll, 'tcx>( .di_node } +/// Creates the debuginfo node for `unsafe<'a> T` binder types. +/// +/// We treat an unsafe binder like a struct with a single field named `inner` +/// rather than delegating to the inner type's DI node directly. This way the +/// debugger shows the binder's own type name, and the wrapped value is still +/// accessible through the `inner` field. +fn build_unsafe_binder_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + binder_type: Ty<'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let ty::UnsafeBinder(inner) = binder_type.kind() else { + bug!( + "Only ty::UnsafeBinder is valid for build_unsafe_binder_type_di_node. Found {:?} instead.", + binder_type + ) + }; + let inner_type = inner.skip_binder(); + let inner_type_di_node = type_di_node(cx, inner_type); + + let type_name = compute_debuginfo_type_name(cx.tcx, binder_type, true); + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &type_name, + None, + cx.size_and_align_of(binder_type), + NO_SCOPE_METADATA, + DIFlags::FlagZero, + ), + |cx, unsafe_binder_type_di_node| { + let inner_layout = cx.layout_of(inner_type); + smallvec![build_field_di_node( + cx, + unsafe_binder_type_di_node, + "inner", + inner_layout, + Size::ZERO, + DIFlags::FlagZero, + inner_type_di_node, + None, + )] + }, + NO_GENERICS, + ) +} + /// Get the global variable for the vtable. /// /// When using global variables, we may have created an addrspacecast to get a pointer to the diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index 4ecc3086e1bd..348c54b00ba9 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -21,8 +21,8 @@ size_and_align_of, type_di_node, unknown_file_metadata, visibility_di_flags, }; use crate::debuginfo::utils::DIB; +use crate::llvm; use crate::llvm::debuginfo::{DIFile, DIFlags, DIType}; -use crate::llvm::{self}; // The names of the associated constants in each variant wrapper struct. // These have to match up with the names being used in `intrinsic.natvis`. diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index caff35860797..556158c286a8 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -21,8 +21,8 @@ file_metadata_from_def_id, type_di_node, unknown_file_metadata, }; use crate::debuginfo::utils::{DIB, create_DIArray, get_namespace_for_item}; +use crate::llvm; use crate::llvm::debuginfo::{DIFlags, DIType}; -use crate::llvm::{self}; mod cpp_like; mod native; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index 1ae6e6e5eeca..a3299af35ac2 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -5,9 +5,8 @@ use rustc_codegen_ssa::debuginfo::type_names::compute_debuginfo_type_name; use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo}; use rustc_codegen_ssa::traits::{ConstCodegenMethods, MiscCodegenMethods}; -use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; -use rustc_middle::ty::{self}; +use rustc_middle::{bug, ty}; use smallvec::smallvec; use crate::common::{AsCCharPtr, CodegenCx}; @@ -18,8 +17,8 @@ unknown_file_metadata, visibility_di_flags, }; use crate::debuginfo::utils::{DIB, create_DIArray, get_namespace_for_item}; +use crate::llvm; use crate::llvm::debuginfo::{DIFile, DIFlags, DIType}; -use crate::llvm::{self}; /// Build the debuginfo node for an enum type. The listing below shows how such a /// type looks like at the LLVM IR/DWARF level. It is a `DW_TAG_structure_type` diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 37200fdc41af..316ec9cb73e7 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -12,8 +12,8 @@ use super::{DefinitionLocation, SmallVec, UNKNOWN_LINE_NUMBER, unknown_file_metadata}; use crate::common::CodegenCx; use crate::debuginfo::utils::{DIB, create_DIArray, debug_context}; +use crate::llvm; use crate::llvm::debuginfo::{DIFlags, DIScope, DIType}; -use crate::llvm::{self}; mod private { use rustc_macros::HashStable; diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index af154a1aea80..a7b5bdbf7bdf 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -2,7 +2,9 @@ use std::path::Path; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, msg}; +use rustc_errors::{ + Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, format_diag_message, msg, +}; use rustc_macros::Diagnostic; use rustc_span::Span; @@ -24,7 +26,7 @@ impl Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let diag: Diag<'_, G> = self.0.into_diag(dcx, level); let (message, _) = diag.messages.first().expect("`LlvmError` with no message"); - let message = dcx.eagerly_translate_to_string(message.clone(), diag.args.iter()); + let message = format_diag_message(message, &diag.args); Diag::new( dcx, level, diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 6bcc8135f35e..af50afa057ed 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1,20 +1,19 @@ use std::cmp::Ordering; use std::ffi::c_uint; -use std::ptr; +use std::{assert_matches, ptr}; use rustc_abi::{ Align, BackendRepr, ExternAbi, Float, HasDataLayout, Primitive, Size, WrappingRange, }; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}; -use rustc_codegen_ssa::codegen_attrs::autodiff_attrs; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; use rustc_codegen_ssa::errors::{ExpectedPointerMutability, InvalidMonomorphization}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; use rustc_codegen_ssa::traits::*; -use rustc_data_structures::assert_matches; +use rustc_hir as hir; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_hir::{self as hir}; +use rustc_hir::find_attr; use rustc_middle::mir::BinOp; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; use rustc_middle::ty::offload_meta::OffloadMetadata; @@ -38,7 +37,7 @@ use crate::errors::{ AutoDiffWithoutEnable, AutoDiffWithoutLto, OffloadWithoutEnable, OffloadWithoutFatLTO, }; -use crate::llvm::{self, Metadata, Type, Value}; +use crate::llvm::{self, Type, Value}; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; @@ -799,8 +798,9 @@ fn type_checked_load( &mut self, llvtable: &'ll Value, vtable_byte_offset: u64, - typeid: &'ll Metadata, + typeid: &[u8], ) -> Self::Value { + let typeid = self.create_metadata(typeid); let typeid = self.get_metadata_value(typeid); let vtable_byte_offset = self.const_i32(vtable_byte_offset as i32); let type_checked_load = self.call_intrinsic( @@ -1367,7 +1367,9 @@ fn codegen_autodiff<'ll, 'tcx>( let val_arr = get_args_from_tuple(bx, args[2], fn_diff); let diff_symbol = symbol_name_for_instance_in_crate(tcx, fn_diff.clone(), LOCAL_CRATE); - let Some(mut diff_attrs) = autodiff_attrs(tcx, fn_diff.def_id()) else { + let Some(Some(mut diff_attrs)) = + find_attr!(tcx, fn_diff.def_id(), RustcAutodiff(attr) => attr.clone()) + else { bug!("could not find autodiff attrs") }; @@ -1389,7 +1391,7 @@ fn codegen_autodiff<'ll, 'tcx>( &diff_symbol, llret_ty, &val_arr, - diff_attrs.clone(), + &diff_attrs, result, fnc_tree, ); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 3310fe4f43f8..17b6956ebd53 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -5,10 +5,8 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(extern_types)] #![feature(file_buffered)] -#![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] #![feature(macro_derive)] @@ -33,7 +31,7 @@ TargetMachineFactoryFn, }; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig}; +use rustc_codegen_ssa::{CompiledModule, CompiledModules, CrateInfo, ModuleCodegen, TargetConfig}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; @@ -79,24 +77,18 @@ #[derive(Clone)] pub struct LlvmCodegenBackend(()); -struct TimeTraceProfiler { - enabled: bool, -} +struct TimeTraceProfiler {} impl TimeTraceProfiler { - fn new(enabled: bool) -> Self { - if enabled { - unsafe { llvm::LLVMRustTimeTraceProfilerInitialize() } - } - TimeTraceProfiler { enabled } + fn new() -> Self { + unsafe { llvm::LLVMRustTimeTraceProfilerInitialize() } + TimeTraceProfiler {} } } impl Drop for TimeTraceProfiler { fn drop(&mut self) { - if self.enabled { - unsafe { llvm::LLVMRustTimeTraceProfilerFinishThread() } - } + unsafe { llvm::LLVMRustTimeTraceProfilerFinishThread() } } } @@ -122,6 +114,16 @@ fn compile_codegen_unit( ) -> (ModuleCodegen, u64) { base::compile_codegen_unit(tcx, cgu_name) } +} + +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, @@ -130,39 +132,7 @@ fn target_machine_factory( ) -> TargetMachineFactoryFn { back::write::target_machine_factory(sess, optlvl, target_features) } - - fn spawn_named_thread( - time_trace: bool, - name: String, - f: F, - ) -> std::io::Result> - where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, - { - std::thread::Builder::new().name(name).spawn(move || { - let _profiler = TimeTraceProfiler::new(time_trace); - f() - }) - } -} - -impl WriteBackendMethods for LlvmCodegenBackend { - type Module = ModuleLlvm; - type ModuleBuffer = back::lto::ModuleBuffer; - type TargetMachine = OwnedTargetMachine; - type ThinData = back::lto::ThinData; - type ThinBuffer = back::lto::ThinBuffer; - 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 run_and_optimize_fat_lto( + fn optimize_and_codegen_fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, @@ -170,7 +140,7 @@ fn run_and_optimize_fat_lto( exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, - ) -> ModuleCodegen { + ) -> CompiledModule { let mut module = back::lto::run_fat( cgcx, prof, @@ -185,7 +155,7 @@ fn run_and_optimize_fat_lto( let dcx = dcx.handle(); back::lto::run_pass_manager(cgcx, prof, dcx, &mut module, false); - module + back::write::codegen(cgcx, prof, shared_emitter, module, &cgcx.module_config) } fn run_thin_lto( cgcx: &CodegenContext, @@ -193,7 +163,7 @@ fn run_thin_lto( dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], - modules: Vec<(String, Self::ThinBuffer)>, + modules: Vec<(String, Self::ModuleBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec) { back::lto::run_thin( @@ -215,14 +185,14 @@ fn optimize( ) { back::write::optimize(cgcx, prof, shared_emitter, module, config) } - fn optimize_thin( + fn optimize_and_codegen_thin( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, tm_factory: TargetMachineFactoryFn, thin: ThinModule, - ) -> ModuleCodegen { - back::lto::optimize_thin_module(cgcx, prof, shared_emitter, tm_factory, thin) + ) -> CompiledModule { + back::lto::optimize_and_codegen_thin_module(cgcx, prof, shared_emitter, tm_factory, thin) } fn codegen( cgcx: &CodegenContext, @@ -233,11 +203,8 @@ fn codegen( ) -> CompiledModule { back::write::codegen(cgcx, prof, shared_emitter, module, config) } - fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer) { - back::lto::prepare_thin(module) - } - fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer) { - (module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod())) + fn serialize_module(module: Self::Module, is_thin: bool) -> Self::ModuleBuffer { + back::lto::ModuleBuffer::new(module.llmod(), is_thin) } } @@ -364,12 +331,12 @@ fn replaced_intrinsics(&self) -> Vec { will_not_use_fallback } - fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { - Box::new(rustc_codegen_ssa::base::codegen_crate( - LlvmCodegenBackend(()), - tcx, - crate::llvm_util::target_cpu(tcx.sess).to_string(), - )) + fn target_cpu(&self, sess: &Session) -> String { + crate::llvm_util::target_cpu(sess).to_string() + } + + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, crate_info: &CrateInfo) -> Box { + Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx, crate_info)) } fn join_codegen( @@ -377,8 +344,8 @@ fn join_codegen( ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, - ) -> (CodegenResults, FxIndexMap) { - let (codegen_results, work_products) = ongoing_codegen + ) -> (CompiledModules, FxIndexMap) { + let (compiled_modules, work_products) = ongoing_codegen .downcast::>() .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") .join(sess); @@ -390,13 +357,24 @@ fn join_codegen( }); } - (codegen_results, work_products) + (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 link( &self, sess: &Session, - codegen_results: CodegenResults, + compiled_modules: CompiledModules, + crate_info: CrateInfo, metadata: EncodedMetadata, outputs: &OutputFilenames, ) { @@ -409,7 +387,8 @@ fn link( link_binary( sess, &LlvmArchiveBuilderBuilder, - codegen_results, + compiled_modules, + crate_info, metadata, outputs, self.name(), diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 4956549f2ed3..f9af42494cad 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -536,9 +536,6 @@ pub(crate) enum DiagnosticLevel { unsafe extern "C" { // LLVMRustThinLTOData pub(crate) type ThinLTOData; - - // LLVMRustThinLTOBuffer - pub(crate) type ThinLTOBuffer; } /// LLVMRustThinLTOModule @@ -848,7 +845,7 @@ pub struct GEPNoWrapFlags : c_uint { } unsafe extern "C" { - pub(crate) type ModuleBuffer; + pub(crate) type Buffer; } pub(crate) type SelfProfileBeforePassCallback = @@ -2375,9 +2372,8 @@ pub(crate) fn LLVMRustOptimize<'a>( NoPrepopulatePasses: bool, VerifyIR: bool, LintIR: bool, - ThinLTOBuffer: Option<&mut *mut ThinLTOBuffer>, - EmitThinLTO: bool, - EmitThinLTOSummary: bool, + ThinLTOBuffer: Option<&mut Option>, + ThinLTOSummaryBuffer: Option<&mut Option>, MergeFunctions: bool, UnrollLoops: bool, SLPVectorize: bool, @@ -2458,22 +2454,13 @@ pub(crate) fn LLVMRustUnpackSMDiagnostic( pub(crate) fn LLVMRustSetModulePICLevel(M: &Module); pub(crate) fn LLVMRustSetModulePIELevel(M: &Module); pub(crate) fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); - pub(crate) fn LLVMRustModuleBufferCreate(M: &Module) -> &'static mut ModuleBuffer; - pub(crate) fn LLVMRustModuleBufferPtr(p: &ModuleBuffer) -> *const u8; - pub(crate) fn LLVMRustModuleBufferLen(p: &ModuleBuffer) -> usize; - pub(crate) fn LLVMRustModuleBufferFree(p: &'static mut ModuleBuffer); + pub(crate) fn LLVMRustBufferPtr(p: &Buffer) -> *const u8; + pub(crate) fn LLVMRustBufferLen(p: &Buffer) -> usize; + pub(crate) fn LLVMRustBufferFree(p: &'static mut Buffer); pub(crate) fn LLVMRustModuleCost(M: &Module) -> u64; pub(crate) fn LLVMRustModuleInstructionStats(M: &Module) -> u64; - pub(crate) fn LLVMRustThinLTOBufferCreate( - M: &Module, - is_thin: bool, - ) -> &'static mut ThinLTOBuffer; - pub(crate) fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer); - pub(crate) fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char; - pub(crate) fn LLVMRustThinLTOBufferLen(M: &ThinLTOBuffer) -> size_t; - pub(crate) fn LLVMRustThinLTOBufferThinLinkDataPtr(M: &ThinLTOBuffer) -> *const c_char; - pub(crate) fn LLVMRustThinLTOBufferThinLinkDataLen(M: &ThinLTOBuffer) -> size_t; + pub(crate) fn LLVMRustModuleSerialize(M: &Module, is_thin: bool) -> &'static mut Buffer; pub(crate) fn LLVMRustCreateThinLTOData( Modules: *const ThinLTOModule, NumModules: size_t, diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 621004e756ec..2871326b28b5 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -284,6 +284,10 @@ pub(crate) fn set_comdat(llmod: &Module, llglobal: &Value, name: &CStr) { } } +pub(crate) fn count_params(llfn: &Value) -> c_uint { + LLVMCountParams(llfn) +} + /// Safe wrapper around `LLVMGetParam`, because segfaults are no fun. pub(crate) fn get_param(llfn: &Value, index: c_uint) -> &Value { unsafe { diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 1a8fbf67f0bc..44a77f0fad7d 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::ffi::CString; use rustc_abi::AddressSpace; @@ -6,13 +7,17 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::bug; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir::mono::Visibility; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf}; -use rustc_middle::ty::{self, Instance, TypeVisitableExt}; +use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt}; use rustc_session::config::CrateType; +use rustc_target::callconv::{FnAbi, PassMode}; use rustc_target::spec::{Arch, RelocModel}; use tracing::debug; +use crate::abi::FnAbiLlvmExt; +use crate::builder::Builder; use crate::context::CodegenCx; use crate::errors::SymbolAlreadyDefined; use crate::type_of::LayoutLlvmExt; @@ -46,7 +51,7 @@ fn predefine_static( self.assume_dso_local(g, false); let attrs = self.tcx.codegen_instance_attrs(instance.def); - self.add_aliases(g, &attrs.foreign_item_symbol_aliases); + self.add_static_aliases(g, &attrs.foreign_item_symbol_aliases); self.instances.borrow_mut().insert(instance, g); } @@ -60,11 +65,29 @@ fn predefine_fn( ) { assert!(!instance.args.has_infer()); - let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); + let attrs = self.tcx.codegen_instance_attrs(instance.def); + + let lldecl = + self.predefine_without_aliases(instance, &attrs, linkage, visibility, symbol_name); + self.add_function_aliases(instance, lldecl, &attrs, &attrs.foreign_item_symbol_aliases); + + self.instances.borrow_mut().insert(instance, lldecl); + } +} + +impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { + fn predefine_without_aliases( + &self, + instance: Instance<'tcx>, + attrs: &Cow<'_, CodegenFnAttrs>, + linkage: Linkage, + visibility: Visibility, + symbol_name: &str, + ) -> &'ll llvm::Value { + let fn_abi: &FnAbi<'tcx, Ty<'tcx>> = self.fn_abi_of_instance(instance, ty::List::empty()); let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance)); llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage)); - let attrs = self.tcx.codegen_instance_attrs(instance.def); - base::set_link_section(lldecl, &attrs); + base::set_link_section(lldecl, attrs); if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR) && self.tcx.sess.target.supports_comdat() { @@ -76,20 +99,45 @@ fn predefine_fn( self.assume_dso_local(lldecl, false); - self.add_aliases(lldecl, &attrs.foreign_item_symbol_aliases); - - self.instances.borrow_mut().insert(instance, lldecl); + lldecl } -} -impl CodegenCx<'_, '_> { - fn add_aliases(&self, aliasee: &llvm::Value, aliases: &[(DefId, Linkage, Visibility)]) { + /// LLVM has the concept of an `alias`. + /// We need this for the "externally implementable items" feature, + /// though it's generally useful. + /// + /// On macos, though this might be a more general problem, function symbols + /// have a fixed target architecture. This is necessary, since macos binaries + /// may contain code for both ARM and x86 macs. + /// + /// LLVM *can* add attributes for target architecture to function symbols, + /// cannot do so for statics, but importantly, also cannot for aliases + /// *even* when aliases may refer to a function symbol. + /// + /// This is not a problem: instead of using LLVM aliases, we can just generate + /// a new function symbol (with target architecture!) which effectively comes down to: + /// + /// ```ignore (illustrative example) + /// fn alias_name(...args) { + /// original_name(...args) + /// } + /// ``` + /// + /// That's also an alias. + /// + /// This does mean that the alias symbol has a different address than the original symbol + /// (assuming no optimizations by LLVM occur). This is unacceptable for statics. + /// So for statics we do want to use LLVM aliases, which is fine, + /// since for those we don't care about target architecture anyway. + /// + /// So, this function is for static aliases. See [`add_function_aliases`](Self::add_function_aliases) for the alternative. + fn add_static_aliases(&self, aliasee: &llvm::Value, aliases: &[(DefId, Linkage, Visibility)]) { let ty = self.get_type_of_global(aliasee); for (alias, linkage, visibility) in aliases { let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias)); + tracing::debug!("STATIC ALIAS: {alias:?} {linkage:?} {visibility:?}"); - tracing::debug!("ALIAS: {alias:?} {linkage:?} {visibility:?}"); let lldecl = llvm::add_alias( self.llmod, ty, @@ -103,6 +151,61 @@ fn add_aliases(&self, aliasee: &llvm::Value, aliases: &[(DefId, Linkage, Visibil } } + /// See [`add_static_aliases`](Self::add_static_aliases) for docs. + fn add_function_aliases( + &self, + aliasee_instance: Instance<'tcx>, + aliasee: &'ll llvm::Value, + attrs: &Cow<'_, CodegenFnAttrs>, + aliases: &[(DefId, Linkage, Visibility)], + ) { + for (alias, linkage, visibility) in aliases { + let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias)); + tracing::debug!("FUNCTION ALIAS: {alias:?} {linkage:?} {visibility:?}"); + + // predefine another copy of the original instance + // with a new symbol name + let alias_lldecl = self.predefine_without_aliases( + aliasee_instance, + attrs, + *linkage, + *visibility, + symbol_name.name, + ); + + let fn_abi: &FnAbi<'tcx, Ty<'tcx>> = + self.fn_abi_of_instance(aliasee_instance, ty::List::empty()); + + // both the alias and the aliasee have the same ty + let fn_ty = fn_abi.llvm_type(self); + let start_llbb = Builder::append_block(self, alias_lldecl, "start"); + let mut start_bx = Builder::build(self, start_llbb); + + let num_params = llvm::count_params(alias_lldecl); + let mut args = Vec::with_capacity(num_params as usize); + for index in 0..num_params { + args.push(llvm::get_param(alias_lldecl, index)); + } + + let call = start_bx.call( + fn_ty, + Some(attrs), + Some(fn_abi), + aliasee, + &args, + None, + Some(aliasee_instance), + ); + + match &fn_abi.ret.mode { + PassMode::Ignore | PassMode::Indirect { .. } => start_bx.ret_void(), + PassMode::Direct(_) | PassMode::Pair { .. } | PassMode::Cast { .. } => { + start_bx.ret(call) + } + } + } + } + /// A definition or declaration can be assumed to be local to a group of /// libraries that form a single DSO or executable. /// Marks the local as DSO if so. diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index d8b77369a34f..2026b06d104d 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -15,7 +15,7 @@ use crate::abi::{FnAbiLlvmExt, LlvmType}; use crate::common; use crate::context::{CodegenCx, GenericCx, SCx}; -use crate::llvm::{self, FALSE, Metadata, TRUE, ToGeneric, ToLlvmBool, Type, Value}; +use crate::llvm::{self, FALSE, TRUE, ToGeneric, ToLlvmBool, Type, Value}; use crate::type_of::LayoutLlvmExt; impl PartialEq for Type { @@ -319,10 +319,6 @@ fn set_type_metadata(&self, function: &'ll Value, typeid: &[u8]) { self.global_set_metadata_node(function, llvm::MD_type, &v); } - fn typeid_metadata(&self, typeid: &[u8]) -> Option<&'ll Metadata> { - Some(self.create_metadata(typeid)) - } - fn add_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) { let kcfi_type_metadata = [llvm::LLVMValueAsMetadata(self.const_u32(kcfi_typeid))]; self.global_add_metadata_node(function, llvm::MD_kcfi_type, &kcfi_type_metadata); diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 460f4afea963..9e6b36646434 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -8,7 +8,7 @@ use rustc_middle::bug; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; -use rustc_target::spec::{Abi, Arch, Env}; +use rustc_target::spec::{Arch, Env, RustcAbi}; use crate::builder::Builder; use crate::llvm::{Type, Value}; @@ -272,7 +272,7 @@ fn emit_powerpc_va_arg<'ll, 'tcx>( // Rust does not currently support any powerpc softfloat targets. let target = &bx.cx.tcx.sess.target; - let is_soft_float_abi = target.abi == Abi::SoftFloat; + let is_soft_float_abi = target.rustc_abi == Some(RustcAbi::Softfloat); assert!(!is_soft_float_abi); // All instances of VaArgSafe are passed directly. @@ -1077,7 +1077,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( AllowHigherAlign::Yes, ForceRightAdjust::Yes, ), - Arch::RiscV32 if target.abi == Abi::Ilp32e => { + Arch::RiscV32 if target.llvm_abiname == "ilp32e" => { // FIXME: clang manually adjusts the alignment for this ABI. It notes: // // > To be compatible with GCC's behaviors, we force arguments with diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index 081fe0aa91aa..9858d9335dc6 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -28,7 +28,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{DiagArgValue, IntoDiagArg}; -use rustc_hir::attrs::{AttributeKind, CguFields, CguKind}; +use rustc_hir::attrs::{CguFields, CguKind}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::{self as hir, find_attr}; use rustc_middle::mir::mono::CodegenUnitNameBuilder; @@ -89,7 +89,7 @@ struct AssertModuleSource<'tcx> { impl<'tcx> AssertModuleSource<'tcx> { fn check_attrs(&mut self, attrs: &[hir::Attribute]) { for &(span, cgu_fields) in find_attr!(attrs, - AttributeKind::RustcCguTestAttr(e) => e) + RustcCguTestAttr(e) => e) .into_iter() .flatten() { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index acc1c0b9f0de..56dca6c8b902 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -18,18 +18,19 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_errors::{DiagCtxtHandle, LintDiagnostic}; +use rustc_errors::DiagCtxtHandle; use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::attrs::NativeLibKind; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; -use rustc_macros::LintDiagnostic; +use rustc_lint_defs::builtin::LINKER_INFO; +use rustc_macros::Diagnostic; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; use rustc_metadata::{ EncodedMetadata, NativeLibSearchFallback, find_native_static_library, walk_native_lib_search_dirs, }; use rustc_middle::bug; -use rustc_middle::lint::lint_level; +use rustc_middle::lint::diag_lint_level; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; @@ -60,7 +61,8 @@ use super::{apple, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; use crate::{ - CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file, + CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors, + looks_like_rust_object_file, }; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { @@ -76,7 +78,8 @@ pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { pub fn link_binary( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, - codegen_results: CodegenResults, + compiled_modules: CompiledModules, + crate_info: CrateInfo, metadata: EncodedMetadata, outputs: &OutputFilenames, codegen_backend: &'static str, @@ -84,7 +87,7 @@ pub fn link_binary( let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); let mut tempfiles_for_stdout_output: Vec = Vec::new(); - for &crate_type in &codegen_results.crate_info.crate_types { + for &crate_type in &crate_info.crate_types { // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) && !output_metadata @@ -98,25 +101,20 @@ pub fn link_binary( } sess.time("link_binary_check_files_are_writeable", || { - for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { + for obj in compiled_modules.modules.iter().filter_map(|m| m.object.as_ref()) { check_file_is_writeable(obj, sess); } }); if outputs.outputs.should_link() { - let output = out_filename( - sess, - crate_type, - outputs, - codegen_results.crate_info.local_crate_name, - ); + let output = out_filename(sess, crate_type, outputs, crate_info.local_crate_name); let tmpdir = TempDirBuilder::new() .prefix("rustc") .tempdir_in(output.parent().unwrap_or_else(|| Path::new("."))) .unwrap_or_else(|error| sess.dcx().emit_fatal(errors::CreateTempDir { error })); let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps); - let crate_name = format!("{}", codegen_results.crate_info.local_crate_name); + let crate_name = format!("{}", crate_info.local_crate_name); let out_filename = output.file_for_writing( outputs, OutputType::Exe, @@ -130,7 +128,8 @@ pub fn link_binary( link_rlib( sess, archive_builder_builder, - &codegen_results, + &compiled_modules, + &crate_info, &metadata, RlibFlavor::Normal, &path, @@ -141,7 +140,8 @@ pub fn link_binary( link_staticlib( sess, archive_builder_builder, - &codegen_results, + &compiled_modules, + &crate_info, &metadata, &out_filename, &path, @@ -153,7 +153,8 @@ pub fn link_binary( archive_builder_builder, crate_type, &out_filename, - &codegen_results, + &compiled_modules, + &crate_info, &metadata, path.as_ref(), codegen_backend, @@ -218,7 +219,7 @@ pub fn link_binary( |module: &CompiledModule| maybe_remove_temps_from_module(false, false, module); // Otherwise, always remove the allocator module temporaries. - if let Some(ref allocator_module) = codegen_results.allocator_module { + if let Some(ref allocator_module) = compiled_modules.allocator_module { remove_temps_from_module(allocator_module); } @@ -237,7 +238,7 @@ pub fn link_binary( let (preserve_objects, preserve_dwarf_objects) = preserve_objects_for_their_debuginfo(sess); debug!(?preserve_objects, ?preserve_dwarf_objects); - for module in &codegen_results.modules { + for module in &compiled_modules.modules { maybe_remove_temps_from_module(preserve_objects, preserve_dwarf_objects, module); } }); @@ -298,7 +299,8 @@ pub fn each_linked_rlib( fn link_rlib<'a>( sess: &'a Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, - codegen_results: &CodegenResults, + compiled_modules: &CompiledModules, + crate_info: &CrateInfo, metadata: &EncodedMetadata, flavor: RlibFlavor, tmpdir: &MaybeTempDir, @@ -327,7 +329,7 @@ fn link_rlib<'a>( RlibFlavor::StaticlibBase => None, }; - for m in &codegen_results.modules { + for m in &compiled_modules.modules { if let Some(obj) = m.object.as_ref() { ab.add_file(obj); } @@ -340,7 +342,7 @@ fn link_rlib<'a>( match flavor { RlibFlavor::Normal => {} RlibFlavor::StaticlibBase => { - let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()); + let obj = compiled_modules.allocator_module.as_ref().and_then(|m| m.object.as_ref()); if let Some(obj) = obj { ab.add_file(obj); } @@ -366,7 +368,7 @@ fn link_rlib<'a>( // feature then we'll need to figure out how to record what objects were // loaded from the libraries found here and then encode that into the // metadata of the rlib we're generating somehow. - for lib in codegen_results.crate_info.used_libraries.iter() { + for lib in crate_info.used_libraries.iter() { let NativeLibKind::Static { bundle: None | Some(true), .. } = lib.kind else { continue; }; @@ -394,7 +396,7 @@ fn link_rlib<'a>( for output_path in raw_dylib::create_raw_dylib_dll_import_libs( sess, archive_builder_builder, - codegen_results.crate_info.used_libraries.iter(), + crate_info.used_libraries.iter(), tmpdir.as_ref(), true, ) { @@ -457,7 +459,8 @@ fn link_rlib<'a>( fn link_staticlib( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, - codegen_results: &CodegenResults, + compiled_modules: &CompiledModules, + crate_info: &CrateInfo, metadata: &EncodedMetadata, out_filename: &Path, tempdir: &MaybeTempDir, @@ -466,72 +469,67 @@ fn link_staticlib( let mut ab = link_rlib( sess, archive_builder_builder, - codegen_results, + compiled_modules, + crate_info, metadata, RlibFlavor::StaticlibBase, tempdir, ); let mut all_native_libs = vec![]; - let res = each_linked_rlib( - &codegen_results.crate_info, - Some(CrateType::StaticLib), - &mut |cnum, path| { - let lto = are_upstream_rust_objects_already_included(sess) - && !ignored_for_lto(sess, &codegen_results.crate_info, cnum); + let res = each_linked_rlib(crate_info, Some(CrateType::StaticLib), &mut |cnum, path| { + let lto = are_upstream_rust_objects_already_included(sess) + && !ignored_for_lto(sess, crate_info, cnum); - let native_libs = codegen_results.crate_info.native_libraries[&cnum].iter(); - let relevant = native_libs.clone().filter(|lib| relevant_lib(sess, lib)); - let relevant_libs: FxIndexSet<_> = relevant.filter_map(|lib| lib.filename).collect(); + let native_libs = crate_info.native_libraries[&cnum].iter(); + let relevant = native_libs.clone().filter(|lib| relevant_lib(sess, lib)); + let relevant_libs: FxIndexSet<_> = relevant.filter_map(|lib| lib.filename).collect(); - let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect(); - ab.add_archive( - path, - Box::new(move |fname: &str| { - // Ignore metadata files, no matter the name. - if fname == METADATA_FILENAME { - return true; - } + let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect(); + ab.add_archive( + path, + Box::new(move |fname: &str| { + // Ignore metadata files, no matter the name. + if fname == METADATA_FILENAME { + return true; + } - // Don't include Rust objects if LTO is enabled - if lto && looks_like_rust_object_file(fname) { - return true; - } + // Don't include Rust objects if LTO is enabled + if lto && looks_like_rust_object_file(fname) { + return true; + } - // Skip objects for bundled libs. - if bundled_libs.contains(&Symbol::intern(fname)) { - return true; - } + // Skip objects for bundled libs. + if bundled_libs.contains(&Symbol::intern(fname)) { + return true; + } - false - }), - ) - .unwrap(); + false + }), + ) + .unwrap(); - archive_builder_builder - .extract_bundled_libs(path, tempdir.as_ref(), &relevant_libs) - .unwrap_or_else(|e| sess.dcx().emit_fatal(e)); + archive_builder_builder + .extract_bundled_libs(path, tempdir.as_ref(), &relevant_libs) + .unwrap_or_else(|e| sess.dcx().emit_fatal(e)); - for filename in relevant_libs.iter() { - let joined = tempdir.as_ref().join(filename.as_str()); - let path = joined.as_path(); - ab.add_archive(path, Box::new(|_| false)).unwrap(); - } + for filename in relevant_libs.iter() { + let joined = tempdir.as_ref().join(filename.as_str()); + let path = joined.as_path(); + ab.add_archive(path, Box::new(|_| false)).unwrap(); + } - all_native_libs - .extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned()); - }, - ); + all_native_libs.extend(crate_info.native_libraries[&cnum].iter().cloned()); + }); if let Err(e) = res { sess.dcx().emit_fatal(e); } ab.build(out_filename); - let crates = codegen_results.crate_info.used_crates.iter(); + let crates = crate_info.used_crates.iter(); - let fmts = codegen_results - .crate_info + let fmts = crate_info .dependency_formats .get(&CrateType::StaticLib) .expect("no dependency formats for staticlib"); @@ -541,8 +539,8 @@ fn link_staticlib( let Some(Linkage::Dynamic) = fmts.get(cnum) else { continue; }; - let crate_name = codegen_results.crate_info.crate_name[&cnum]; - let used_crate_source = &codegen_results.crate_info.used_crate_source[&cnum]; + let crate_name = crate_info.crate_name[&cnum]; + let used_crate_source = &crate_info.used_crate_source[&cnum]; if let Some(path) = &used_crate_source.dylib { all_rust_dylibs.push(&**path); } else if used_crate_source.rmeta.is_some() { @@ -552,7 +550,7 @@ fn link_staticlib( } } - all_native_libs.extend_from_slice(&codegen_results.crate_info.used_libraries); + all_native_libs.extend_from_slice(&crate_info.used_libraries); for print in &sess.opts.prints { if print.kind == PrintKind::NativeStaticLibs { @@ -563,7 +561,12 @@ fn link_staticlib( /// Use `thorin` (rust implementation of a dwarf packaging utility) to link DWARF objects into a /// DWARF package. -fn link_dwarf_object(sess: &Session, cg_results: &CodegenResults, executable_out_filename: &Path) { +fn link_dwarf_object( + sess: &Session, + compiled_modules: &CompiledModules, + crate_info: &CrateInfo, + executable_out_filename: &Path, +) { let mut dwp_out_filename = executable_out_filename.to_path_buf().into_os_string(); dwp_out_filename.push(".dwp"); debug!(?dwp_out_filename, ?executable_out_filename); @@ -604,20 +607,21 @@ fn read_input(&self, path: &Path) -> std::io::Result<&[u8]> { // Input objs contain .o/.dwo files from the current crate. match sess.opts.unstable_opts.split_dwarf_kind { SplitDwarfKind::Single => { - for input_obj in cg_results.modules.iter().filter_map(|m| m.object.as_ref()) { + for input_obj in compiled_modules.modules.iter().filter_map(|m| m.object.as_ref()) { package.add_input_object(input_obj)?; } } SplitDwarfKind::Split => { - for input_obj in cg_results.modules.iter().filter_map(|m| m.dwarf_object.as_ref()) { + for input_obj in + compiled_modules.modules.iter().filter_map(|m| m.dwarf_object.as_ref()) + { package.add_input_object(input_obj)?; } } } // Input rlibs contain .o/.dwo files from dependencies. - let input_rlibs = cg_results - .crate_info + let input_rlibs = crate_info .used_crate_source .items() .filter_map(|(_, csource)| csource.rlib.as_ref()) @@ -662,7 +666,7 @@ fn read_input(&self, path: &Path) -> std::io::Result<&[u8]> { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$inner}")] /// Translating this is kind of useless. We don't pass translation flags to the linker, so we'd just /// end up with inconsistent languages within the same diagnostic. @@ -670,6 +674,147 @@ struct LinkerOutput { inner: String, } +fn is_msvc_link_exe(sess: &Session) -> bool { + let (linker_path, flavor) = linker_and_flavor(sess); + sess.target.is_like_msvc + && flavor == LinkerFlavor::Msvc(Lld::No) + // Match exactly "link.exe" + && linker_path.to_str() == Some("link.exe") +} + +fn is_macos_ld(sess: &Session) -> bool { + let (_, flavor) = linker_and_flavor(sess); + sess.target.is_like_darwin && matches!(flavor, LinkerFlavor::Darwin(_, Lld::No)) +} + +fn is_windows_gnu_ld(sess: &Session) -> bool { + let (_, flavor) = linker_and_flavor(sess); + sess.target.is_like_windows + && !sess.target.is_like_msvc + && matches!(flavor, LinkerFlavor::Gnu(_, Lld::No)) +} + +fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8], stderr: &[u8]) { + let mut escaped_stderr = escape_string(&stderr); + let mut escaped_stdout = escape_string(&stdout); + let mut linker_info = String::new(); + + info!("linker stderr:\n{}", &escaped_stderr); + info!("linker stdout:\n{}", &escaped_stdout); + + fn for_each(bytes: &[u8], mut f: impl FnMut(&str, &mut String)) -> String { + let mut output = String::new(); + if let Ok(str) = str::from_utf8(bytes) { + info!("line: {str}"); + output = String::with_capacity(str.len()); + for line in str.lines() { + f(line.trim(), &mut output); + } + } + escape_string(output.trim().as_bytes()) + } + + if is_msvc_link_exe(sess) { + info!("inferred MSVC link.exe"); + + escaped_stdout = for_each(&stdout, |line, output| { + // Hide some progress messages from link.exe that we don't care about. + // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 + if line.starts_with(" Creating library") + || line.starts_with("Generating code") + || line.starts_with("Finished generating code") + { + linker_info += line; + linker_info += "\r\n"; + } else { + *output += line; + *output += "\r\n" + } + }); + } else if is_macos_ld(sess) { + info!("inferred macOS LD"); + + // FIXME: Tracked by https://github.com/rust-lang/rust/issues/136113 + let deployment_mismatch = |line: &str| { + line.starts_with("ld: warning: object file (") + && line.contains("was built for newer 'macOS' version") + && line.contains("than being linked") + }; + // FIXME: This is a real warning we would like to show, but it hits too many crates + // to want to turn it on immediately. + let search_path = |line: &str| { + line.starts_with("ld: warning: search path '") && line.ends_with("' not found") + }; + escaped_stderr = for_each(&stderr, |line, output| { + // This duplicate library warning is just not helpful at all. + if line.starts_with("ld: warning: ignoring duplicate libraries: ") + || deployment_mismatch(line) + || search_path(line) + { + linker_info += line; + linker_info += "\n"; + } else { + *output += line; + *output += "\n" + } + }); + } else if is_windows_gnu_ld(sess) { + info!("inferred Windows GNU LD"); + + let mut saw_exclude_symbol = false; + // See https://github.com/rust-lang/rust/issues/112368. + // FIXME: maybe check that binutils is older than 2.40 before downgrading this warning? + let exclude_symbols = |line: &str| { + line.starts_with("Warning: .drectve `-exclude-symbols:") + && line.ends_with("' unrecognized") + }; + escaped_stderr = for_each(&stderr, |line, output| { + if exclude_symbols(line) { + saw_exclude_symbol = true; + linker_info += line; + linker_info += "\n"; + } else if saw_exclude_symbol && line == "Warning: corrupt .drectve at end of def file" { + linker_info += line; + linker_info += "\n"; + } else { + *output += line; + *output += "\n" + } + }); + } + + let lint_msg = |msg| { + diag_lint_level( + sess, + LINKER_MESSAGES, + levels.linker_messages, + None, + LinkerOutput { inner: msg }, + ); + }; + let lint_info = |msg| { + diag_lint_level(sess, LINKER_INFO, levels.linker_info, None, LinkerOutput { inner: msg }); + }; + + if !escaped_stderr.is_empty() { + // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present. + escaped_stderr = + escaped_stderr.strip_prefix("warning: ").unwrap_or(&escaped_stderr).to_owned(); + // Windows GNU LD prints uppercase Warning + escaped_stderr = escaped_stderr + .strip_prefix("Warning: ") + .unwrap_or(&escaped_stderr) + .replace(": warning: ", ": "); + lint_msg(format!("linker stderr: {escaped_stderr}")); + } + if !escaped_stdout.is_empty() { + lint_msg(format!("linker stdout: {}", escaped_stdout)) + } + if !linker_info.is_empty() { + lint_info(linker_info); + } +} + /// Create a dynamic library or executable. /// /// This will invoke the system linker/cc to create the resulting file. This links to all upstream @@ -679,7 +824,8 @@ fn link_natively( archive_builder_builder: &dyn ArchiveBuilderBuilder, crate_type: CrateType, out_filename: &Path, - codegen_results: &CodegenResults, + compiled_modules: &CompiledModules, + crate_info: &CrateInfo, metadata: &EncodedMetadata, tmpdir: &Path, codegen_backend: &'static str, @@ -705,7 +851,8 @@ fn link_natively( crate_type, tmpdir, temp_filename, - codegen_results, + compiled_modules, + crate_info, metadata, self_contained_components, codegen_backend, @@ -856,11 +1003,6 @@ fn link_natively( match prog { Ok(prog) => { - let is_msvc_link_exe = sess.target.is_like_msvc - && flavor == LinkerFlavor::Msvc(Lld::No) - // Match exactly "link.exe" - && linker_path.to_str() == Some("link.exe"); - if !prog.status.success() { let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); @@ -880,7 +1022,7 @@ fn link_natively( if let Some(code) = prog.status.code() { // All Microsoft `link.exe` linking ror codes are // four digit numbers in the range 1000 to 9999 inclusive - if is_msvc_link_exe && (code < 1000 || code > 9999) { + if is_msvc_link_exe(sess) && (code < 1000 || code > 9999) { let is_vs_installed = find_msvc_tools::find_vs_version().is_ok(); let has_linker = find_msvc_tools::find_tool(sess.target.arch.desc(), "link.exe") @@ -912,48 +1054,8 @@ fn link_natively( sess.dcx().abort_if_errors(); } - let stderr = escape_string(&prog.stderr); - let mut stdout = escape_string(&prog.stdout); - info!("linker stderr:\n{}", &stderr); - info!("linker stdout:\n{}", &stdout); - - // Hide some progress messages from link.exe that we don't care about. - // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 - if is_msvc_link_exe { - if let Ok(str) = str::from_utf8(&prog.stdout) { - let mut output = String::with_capacity(str.len()); - for line in stdout.lines() { - if line.starts_with(" Creating library") - || line.starts_with("Generating code") - || line.starts_with("Finished generating code") - { - continue; - } - output += line; - output += "\r\n" - } - stdout = escape_string(output.trim().as_bytes()) - } - } - - let level = codegen_results.crate_info.lint_levels.linker_messages; - let lint = |msg| { - lint_level(sess, LINKER_MESSAGES, level, None, |diag| { - LinkerOutput { inner: msg }.decorate_lint(diag) - }) - }; - - if !prog.stderr.is_empty() { - // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present. - let stderr = stderr - .strip_prefix("warning: ") - .unwrap_or(&stderr) - .replace(": warning: ", ": "); - lint(format!("linker stderr: {stderr}")); - } - if !stdout.is_empty() { - lint(format!("linker stdout: {}", stdout)) - } + info!("reporting linker output: flavor={flavor:?}"); + report_linker_output(sess, crate_info.lint_levels, &prog.stdout, &prog.stderr); } Err(e) => { let linker_not_found = e.kind() == io::ErrorKind::NotFound; @@ -1015,7 +1117,9 @@ fn link_natively( // We cannot rely on the .o paths in the executable because they may have been // remapped by --remap-path-prefix and therefore invalid, so we need to provide // the .o/.dwo paths explicitly. - SplitDebuginfo::Packed => link_dwarf_object(sess, codegen_results, out_filename), + SplitDebuginfo::Packed => { + link_dwarf_object(sess, compiled_modules, crate_info, out_filename) + } } let strip = sess.opts.cg.strip; @@ -1910,11 +2014,11 @@ fn add_late_link_args( sess: &Session, flavor: LinkerFlavor, crate_type: CrateType, - codegen_results: &CodegenResults, + crate_info: &CrateInfo, ) { let any_dynamic_crate = crate_type == CrateType::Dylib || crate_type == CrateType::Sdylib - || codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| { + || crate_info.dependency_formats.iter().any(|(ty, list)| { *ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic) }); if any_dynamic_crate { @@ -2076,8 +2180,8 @@ fn add_linked_symbol_object( } /// Add object files containing code from the current crate. -fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) { - for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { +fn add_local_crate_regular_objects(cmd: &mut dyn Linker, compiled_modules: &CompiledModules) { + for obj in compiled_modules.modules.iter().filter_map(|m| m.object.as_ref()) { cmd.add_object(obj); } } @@ -2085,12 +2189,13 @@ fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &Codeg /// Add object files for allocator code linked once for the whole crate tree. fn add_local_crate_allocator_objects( cmd: &mut dyn Linker, - codegen_results: &CodegenResults, + compiled_modules: &CompiledModules, + crate_info: &CrateInfo, crate_type: CrateType, ) { - if needs_allocator_shim_for_linking(&codegen_results.crate_info.dependency_formats, crate_type) - { - if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()) + if needs_allocator_shim_for_linking(&crate_info.dependency_formats, crate_type) { + if let Some(obj) = + compiled_modules.allocator_module.as_ref().and_then(|m| m.object.as_ref()) { cmd.add_object(obj); } @@ -2104,7 +2209,7 @@ fn add_local_crate_metadata_objects( archive_builder_builder: &dyn ArchiveBuilderBuilder, crate_type: CrateType, tmpdir: &Path, - codegen_results: &CodegenResults, + crate_info: &CrateInfo, metadata: &EncodedMetadata, ) { // When linking a dynamic library, we put the metadata into a section of the @@ -2114,7 +2219,7 @@ fn add_local_crate_metadata_objects( let data = archive_builder_builder.create_dylib_metadata_wrapper( sess, &metadata, - &codegen_results.crate_info.metadata_symbol, + &crate_info.metadata_symbol, ); let obj = emit_wrapper_file(sess, &data, tmpdir, "rmeta.o"); @@ -2159,7 +2264,7 @@ fn add_relro_args(cmd: &mut dyn Linker, sess: &Session) { fn add_rpath_args( cmd: &mut dyn Linker, sess: &Session, - codegen_results: &CodegenResults, + crate_info: &CrateInfo, out_filename: &Path, ) { if !sess.target.has_rpath { @@ -2170,11 +2275,10 @@ fn add_rpath_args( // where extern libraries might live, based on the // add_lib_search_paths if sess.opts.cg.rpath { - let libs = codegen_results - .crate_info + let libs = crate_info .used_crates .iter() - .filter_map(|cnum| codegen_results.crate_info.used_crate_source[cnum].dylib.as_deref()) + .filter_map(|cnum| crate_info.used_crate_source[cnum].dylib.as_deref()) .collect::>(); let rpath_config = RPathConfig { libs: &*libs, @@ -2267,7 +2371,8 @@ fn linker_with_args( crate_type: CrateType, tmpdir: &Path, out_filename: &Path, - codegen_results: &CodegenResults, + compiled_modules: &CompiledModules, + crate_info: &CrateInfo, metadata: &EncodedMetadata, self_contained_components: LinkSelfContainedComponents, codegen_backend: &'static str, @@ -2278,17 +2383,17 @@ fn linker_with_args( path, flavor, self_contained_components.are_any_components_enabled(), - &codegen_results.crate_info.target_cpu, + &crate_info.target_cpu, codegen_backend, ); let link_output_kind = link_output_kind(sess, crate_type); - let mut export_symbols = codegen_results.crate_info.exported_symbols[&crate_type].clone(); + let mut export_symbols = crate_info.exported_symbols[&crate_type].clone(); if crate_type == CrateType::Cdylib { let mut seen = FxHashSet::default(); - for lib in &codegen_results.crate_info.used_libraries { + for lib in &crate_info.used_libraries { if let NativeLibKind::Static { export_symbols: Some(true), .. } = lib.kind && seen.insert((lib.name, lib.verbatim)) { @@ -2322,12 +2427,7 @@ fn linker_with_args( // Pre-link CRT objects. add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained_crt_objects); - add_linked_symbol_object( - cmd, - sess, - tmpdir, - &codegen_results.crate_info.linked_symbols[&crate_type], - ); + add_linked_symbol_object(cmd, sess, tmpdir, &crate_info.linked_symbols[&crate_type]); // Sanitizer libraries. add_sanitizer_libraries(sess, flavor, crate_type, cmd); @@ -2359,17 +2459,17 @@ fn linker_with_args( // link line. And finally upstream native libraries can't depend on anything // in this DAG so far because they can only depend on other native libraries // and such dependencies are also required to be specified. - add_local_crate_regular_objects(cmd, codegen_results); + add_local_crate_regular_objects(cmd, compiled_modules); add_local_crate_metadata_objects( cmd, sess, archive_builder_builder, crate_type, tmpdir, - codegen_results, + crate_info, metadata, ); - add_local_crate_allocator_objects(cmd, codegen_results, crate_type); + add_local_crate_allocator_objects(cmd, compiled_modules, crate_info, crate_type); // Avoid linking to dynamic libraries unless they satisfy some undefined symbols // at the point at which they are specified on the command line. @@ -2386,7 +2486,7 @@ fn linker_with_args( cmd, sess, archive_builder_builder, - codegen_results, + crate_info, tmpdir, link_output_kind, ); @@ -2396,7 +2496,7 @@ fn linker_with_args( cmd, sess, archive_builder_builder, - codegen_results, + crate_info, crate_type, tmpdir, link_output_kind, @@ -2407,7 +2507,7 @@ fn linker_with_args( cmd, sess, archive_builder_builder, - codegen_results, + crate_info, tmpdir, link_output_kind, ); @@ -2430,7 +2530,7 @@ fn linker_with_args( for output_path in raw_dylib::create_raw_dylib_dll_import_libs( sess, archive_builder_builder, - codegen_results.crate_info.used_libraries.iter(), + crate_info.used_libraries.iter(), tmpdir, true, ) { @@ -2439,7 +2539,7 @@ fn linker_with_args( } else { for (link_path, as_needed) in raw_dylib::create_raw_dylib_elf_stub_shared_objects( sess, - codegen_results.crate_info.used_libraries.iter(), + crate_info.used_libraries.iter(), &raw_dylib_dir, ) { // Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects. @@ -2450,16 +2550,14 @@ fn linker_with_args( // they are used within inlined functions or instantiated generic functions. We do this *after* // handling the raw-dylib symbols in the current crate to make sure that those are chosen first // by the linker. - let dependency_linkage = codegen_results - .crate_info + let dependency_linkage = crate_info .dependency_formats .get(&crate_type) .expect("failed to find crate type in dependency format list"); // We sort the libraries below #[allow(rustc::potential_query_instability)] - let mut native_libraries_from_nonstatics = codegen_results - .crate_info + let mut native_libraries_from_nonstatics = crate_info .native_libraries .iter() .filter_map(|(&cnum, libraries)| { @@ -2501,7 +2599,7 @@ fn linker_with_args( // FIXME: Built-in target specs occasionally use this for linking system libraries, // eliminate all such uses by migrating them to `#[link]` attributes in `lib(std,c,unwind)` // and remove the option. - add_late_link_args(cmd, sess, flavor, crate_type, codegen_results); + add_late_link_args(cmd, sess, flavor, crate_type, crate_info); // ------------ Arbitrary order-independent options ------------ @@ -2514,7 +2612,7 @@ fn linker_with_args( self_contained_components, flavor, crate_type, - codegen_results, + crate_info, out_filename, tmpdir, ); @@ -2554,7 +2652,7 @@ fn add_order_independent_options( self_contained_components: LinkSelfContainedComponents, flavor: LinkerFlavor, crate_type: CrateType, - codegen_results: &CodegenResults, + crate_info: &CrateInfo, out_filename: &Path, tmpdir: &Path, ) { @@ -2599,18 +2697,15 @@ fn add_order_independent_options( "--target", &versioned_llvm_target(sess), "--target-cpu", - &codegen_results.crate_info.target_cpu, + &crate_info.target_cpu, ]); - if codegen_results.crate_info.target_features.len() > 0 { - cmd.link_arg(&format!( - "--target-feature={}", - &codegen_results.crate_info.target_features.join(",") - )); + if crate_info.target_features.len() > 0 { + cmd.link_arg(&format!("--target-feature={}", &crate_info.target_features.join(","))); } } else if flavor == LinkerFlavor::Ptx { - cmd.link_args(&["--fallback-arch", &codegen_results.crate_info.target_cpu]); + cmd.link_args(&["--fallback-arch", &crate_info.target_cpu]); } else if flavor == LinkerFlavor::Bpf { - cmd.link_args(&["--cpu", &codegen_results.crate_info.target_cpu]); + cmd.link_args(&["--cpu", &crate_info.target_cpu]); if let Some(feat) = [sess.opts.cg.target_feature.as_str(), &sess.target.options.features] .into_iter() .find(|feat| !feat.is_empty()) @@ -2627,7 +2722,7 @@ fn add_order_independent_options( if crate_type == CrateType::Executable && sess.target.is_like_windows - && let Some(s) = &codegen_results.crate_info.windows_subsystem + && let Some(s) = &crate_info.windows_subsystem { cmd.windows_subsystem(*s); } @@ -2655,8 +2750,8 @@ fn add_order_independent_options( let natvis_visualizers = collect_natvis_visualizers( tmpdir, sess, - &codegen_results.crate_info.local_crate_name, - &codegen_results.crate_info.natvis_debugger_visualizers, + &crate_info.local_crate_name, + &crate_info.natvis_debugger_visualizers, ); // Pass debuginfo, NatVis debugger visualizers and strip flags down to the linker. @@ -2681,7 +2776,7 @@ fn add_order_independent_options( cmd.ehcont_guard(); } - add_rpath_args(cmd, sess, codegen_results, out_filename); + add_rpath_args(cmd, sess, crate_info, out_filename); } // Write the NatVis debugger visualizer files for each crate to the temp directory and gather the file paths. @@ -2715,7 +2810,7 @@ fn add_native_libs_from_crate( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, - codegen_results: &CodegenResults, + crate_info: &CrateInfo, tmpdir: &Path, bundled_libs: &FxIndexSet, cnum: CrateNum, @@ -2732,15 +2827,15 @@ fn add_native_libs_from_crate( if link_static && cnum != LOCAL_CRATE && !bundled_libs.is_empty() { // If rlib contains native libs as archives, unpack them to tmpdir. - let rlib = codegen_results.crate_info.used_crate_source[&cnum].rlib.as_ref().unwrap(); + let rlib = crate_info.used_crate_source[&cnum].rlib.as_ref().unwrap(); archive_builder_builder .extract_bundled_libs(rlib, tmpdir, bundled_libs) .unwrap_or_else(|e| sess.dcx().emit_fatal(e)); } let native_libs = match cnum { - LOCAL_CRATE => &codegen_results.crate_info.used_libraries, - _ => &codegen_results.crate_info.native_libraries[&cnum], + LOCAL_CRATE => &crate_info.used_libraries, + _ => &crate_info.native_libraries[&cnum], }; let mut last = (None, NativeLibKind::Unspecified, false); @@ -2816,7 +2911,7 @@ fn add_local_native_libraries( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, - codegen_results: &CodegenResults, + crate_info: &CrateInfo, tmpdir: &Path, link_output_kind: LinkOutputKind, ) { @@ -2827,7 +2922,7 @@ fn add_local_native_libraries( cmd, sess, archive_builder_builder, - codegen_results, + crate_info, tmpdir, &Default::default(), LOCAL_CRATE, @@ -2841,7 +2936,7 @@ fn add_upstream_rust_crates( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, - codegen_results: &CodegenResults, + crate_info: &CrateInfo, crate_type: CrateType, tmpdir: &Path, link_output_kind: LinkOutputKind, @@ -2853,8 +2948,7 @@ fn add_upstream_rust_crates( // Linking to a rlib involves just passing it to the linker (the linker // will slurp up the object files inside), and linking to a dynamic library // involves just passing the right -l flag. - let data = codegen_results - .crate_info + let data = crate_info .dependency_formats .get(&crate_type) .expect("failed to find crate type in dependency format list"); @@ -2868,7 +2962,7 @@ fn add_upstream_rust_crates( cmd.link_or_cc_arg("-bnoipath"); } - for &cnum in &codegen_results.crate_info.used_crates { + for &cnum in &crate_info.used_crates { // We may not pass all crates through to the linker. Some crates may appear statically in // an existing dylib, meaning we'll pick up all the symbols from the dylib. // We must always link crates `compiler_builtins` and `profiler_builtins` statically. @@ -2877,14 +2971,14 @@ fn add_upstream_rust_crates( let linkage = data[cnum]; let link_static_crate = linkage == Linkage::Static || linkage == Linkage::IncludedFromDylib - && (codegen_results.crate_info.compiler_builtins == Some(cnum) - || codegen_results.crate_info.profiler_runtime == Some(cnum)); + && (crate_info.compiler_builtins == Some(cnum) + || crate_info.profiler_runtime == Some(cnum)); let mut bundled_libs = Default::default(); match linkage { Linkage::Static | Linkage::IncludedFromDylib | Linkage::NotLinked => { if link_static_crate { - bundled_libs = codegen_results.crate_info.native_libraries[&cnum] + bundled_libs = crate_info.native_libraries[&cnum] .iter() .filter_map(|lib| lib.filename) .collect(); @@ -2892,7 +2986,7 @@ fn add_upstream_rust_crates( cmd, sess, archive_builder_builder, - codegen_results, + crate_info, tmpdir, cnum, &bundled_libs, @@ -2900,7 +2994,7 @@ fn add_upstream_rust_crates( } } Linkage::Dynamic => { - let src = &codegen_results.crate_info.used_crate_source[&cnum]; + let src = &crate_info.used_crate_source[&cnum]; add_dynamic_crate(cmd, sess, src.dylib.as_ref().unwrap()); } } @@ -2920,7 +3014,7 @@ fn add_upstream_rust_crates( cmd, sess, archive_builder_builder, - codegen_results, + crate_info, tmpdir, &bundled_libs, cnum, @@ -2935,11 +3029,11 @@ fn add_upstream_native_libraries( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, - codegen_results: &CodegenResults, + crate_info: &CrateInfo, tmpdir: &Path, link_output_kind: LinkOutputKind, ) { - for &cnum in &codegen_results.crate_info.used_crates { + for &cnum in &crate_info.used_crates { // Static libraries are not linked here, they are linked in `add_upstream_rust_crates`. // FIXME: Merge this function to `add_upstream_rust_crates` so that all native libraries // are linked together with their respective upstream crates, and in their originally @@ -2958,7 +3052,7 @@ fn add_upstream_native_libraries( cmd, sess, archive_builder_builder, - codegen_results, + crate_info, tmpdir, &Default::default(), cnum, @@ -3023,19 +3117,18 @@ fn add_static_crate( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, - codegen_results: &CodegenResults, + crate_info: &CrateInfo, tmpdir: &Path, cnum: CrateNum, bundled_lib_file_names: &FxIndexSet, ) { - let src = &codegen_results.crate_info.used_crate_source[&cnum]; + let src = &crate_info.used_crate_source[&cnum]; let cratepath = src.rlib.as_ref().unwrap(); let mut link_upstream = |path: &Path| cmd.link_staticlib_by_path(&rehome_lib_path(sess, path), false); - if !are_upstream_rust_objects_already_included(sess) - || ignored_for_lto(sess, &codegen_results.crate_info, cnum) + if !are_upstream_rust_objects_already_included(sess) || ignored_for_lto(sess, crate_info, cnum) { link_upstream(cratepath); return; @@ -3050,8 +3143,7 @@ fn add_static_crate( let canonical_name = name.replace('-', "_"); let upstream_rust_objects_already_included = are_upstream_rust_objects_already_included(sess); - let is_builtins = - sess.target.no_builtins || !codegen_results.crate_info.is_no_builtins.contains(&cnum); + let is_builtins = sess.target.no_builtins || !crate_info.is_no_builtins.contains(&cnum); let mut archive = archive_builder_builder.new_archive_builder(sess); if let Err(error) = archive.add_archive( diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs index 45c217168a8d..477c9478c360 100644 --- a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs +++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs @@ -271,10 +271,10 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport] vers.push((version_name, dynstr)); id }; - syms.push((name, dynstr, Some(ver))); + syms.push((name, dynstr, Some(ver), symbol.is_fn)); } else { let dynstr = stub.add_dynamic_string(symbol_name.as_bytes()); - syms.push((symbol_name, dynstr, None)); + syms.push((symbol_name, dynstr, None, symbol.is_fn)); } } @@ -398,10 +398,11 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport] // .dynsym stub.write_null_dynamic_symbol(); - for (_name, dynstr, _ver) in syms.iter().copied() { + for (_name, dynstr, _ver, is_fn) in syms.iter().copied() { + let sym_type = if is_fn { elf::STT_FUNC } else { elf::STT_NOTYPE }; stub.write_dynamic_symbol(&write::Sym { name: Some(dynstr), - st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE, + st_info: (elf::STB_GLOBAL << 4) | sym_type, st_other: elf::STV_DEFAULT, section: Some(text_section), st_shndx: 0, // ignored by object in favor of the `section` field @@ -417,7 +418,7 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport] if !vers.is_empty() { // .gnu_version stub.write_null_gnu_versym(); - for (_name, _dynstr, ver) in syms.iter().copied() { + for (_name, _dynstr, ver, _is_fn) in syms.iter().copied() { stub.write_gnu_versym(if let Some(ver) = ver { assert!((2 + ver as u16) < elf::VERSYM_HIDDEN); elf::VERSYM_HIDDEN | (2 + ver as u16) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 10c4eedb58e8..3ace1a8c266c 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -527,7 +527,8 @@ fn set_output_kind( ) { match output_kind { LinkOutputKind::DynamicNoPicExe => { - if !self.is_ld && self.is_gnu { + // noop on windows w/ gcc, warning w/ clang + if !self.is_ld && self.is_gnu && !self.sess.target.is_like_windows { self.cc_arg("-no-pie"); } } diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index 80b3b5a4d7c0..a2c951c16d28 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -42,7 +42,7 @@ pub fn data(&self) -> &[u8] { pub struct ThinShared { pub data: B::ThinData, - pub thin_buffers: Vec, + pub thin_buffers: Vec, pub serialized_modules: Vec>, pub module_names: Vec, } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 87a043fbdf24..1770251fcba4 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -3,22 +3,19 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender, channel}; -use std::{fs, io, mem, str, thread}; +use std::{assert_matches, fs, io, mem, str, thread}; use rustc_abi::Size; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::jobserver::{self, Acquired}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; use rustc_errors::emitter::Emitter; -use rustc_errors::translation::Translator; use rustc_errors::{ Diag, DiagArgMap, DiagCtxt, DiagCtxtHandle, DiagMessage, ErrCode, FatalError, FatalErrorMarker, Level, MultiSpan, Style, Suggestions, catch_fatal_errors, }; use rustc_fs_util::link_or_copy; -use rustc_hir::attrs::AttributeKind; use rustc_hir::find_attr; use rustc_incremental::{ copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, @@ -44,7 +41,7 @@ use crate::errors::ErrorCreatingRemarkDir; use crate::traits::*; use crate::{ - CachedModuleCodegen, CodegenResults, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, + CachedModuleCodegen, CompiledModule, CompiledModules, CrateInfo, ModuleCodegen, ModuleKind, errors, }; @@ -96,12 +93,10 @@ pub struct ModuleConfig { // Flags indicating which outputs to produce. pub emit_pre_lto_bc: bool, - pub emit_no_opt_bc: bool, pub emit_bc: bool, pub emit_ir: bool, pub emit_asm: bool, pub emit_obj: EmitObj, - pub emit_thin_lto: bool, pub emit_thin_lto_summary: bool, // Miscellaneous flags. These are mostly copied from command-line @@ -198,7 +193,6 @@ macro_rules! if_regular { save_temps || need_pre_lto_bitcode_for_incr_comp(sess), false ), - emit_no_opt_bc: if_regular!(save_temps, false), emit_bc: if_regular!( save_temps || sess.opts.output_types.contains_key(&OutputType::Bitcode), save_temps @@ -212,9 +206,6 @@ macro_rules! if_regular { false ), emit_obj, - // thin lto summaries prevent fat lto, so do not emit them if fat - // lto is requested. See PR #136840 for background information. - emit_thin_lto: sess.opts.unstable_opts.emit_thin_lto && sess.lto() != Lto::Fat, emit_thin_lto_summary: if_regular!( sess.opts.output_types.contains_key(&OutputType::ThinLinkBitcode), false @@ -362,13 +353,13 @@ pub struct CodegenContext { pub parallel: bool, } -fn generate_thin_lto_work( +fn generate_thin_lto_work( cgcx: &CodegenContext, prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], - needs_thin_lto: Vec<(String, B::ThinBuffer)>, + needs_thin_lto: Vec<(String, B::ModuleBuffer)>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Vec<(ThinLtoWorkItem, u64)> { let _prof_timer = prof.generic_activity("codegen_thin_generate_lto_work"); @@ -400,16 +391,8 @@ fn generate_thin_lto_work( .collect() } -pub struct CompiledModules { - pub modules: Vec, - pub allocator_module: Option, -} - enum MaybeLtoModules { - NoLto { - modules: Vec, - allocator_module: Option, - }, + NoLto(CompiledModules), FatLto { cgcx: CodegenContext, exported_symbols_for_lto: Arc>, @@ -422,7 +405,7 @@ enum MaybeLtoModules { cgcx: CodegenContext, exported_symbols_for_lto: Arc>, each_linked_rlib_file_for_lto: Vec, - needs_thin_lto: Vec<(String, ::ThinBuffer)>, + needs_thin_lto: Vec<(String, ::ModuleBuffer)>, lto_import_only_modules: Vec<(SerializedModule<::ModuleBuffer>, WorkProduct)>, }, @@ -449,15 +432,12 @@ fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool { pub(crate) fn start_async_codegen( backend: B, tcx: TyCtxt<'_>, - target_cpu: String, + crate_info: &CrateInfo, allocator_module: Option>, ) -> OngoingCodegen { let (coordinator_send, coordinator_receive) = channel(); - let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID); - let no_builtins = find_attr!(crate_attrs, AttributeKind::NoBuiltins); - - let crate_info = CrateInfo::new(tcx, target_cpu); + let no_builtins = find_attr!(tcx, crate, NoBuiltins); let regular_config = ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins); let allocator_config = ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins); @@ -468,7 +448,7 @@ pub(crate) fn start_async_codegen( let coordinator_thread = start_executing_work( backend.clone(), tcx, - &crate_info, + crate_info, shared_emitter, codegen_worker_send, coordinator_receive, @@ -480,7 +460,6 @@ pub(crate) fn start_async_codegen( OngoingCodegen { backend, - crate_info, codegen_worker_receive, shared_emitter_main, @@ -800,7 +779,7 @@ pub(crate) enum WorkItemResult { /// The backend has finished compiling a CGU, which now needs to go through /// thin LTO. - NeedsThinLto(String, B::ThinBuffer), + NeedsThinLto(String, B::ModuleBuffer), } pub enum FatLtoInput { @@ -842,7 +821,7 @@ pub(crate) fn compute_per_cgu_lto_type( } } -fn execute_optimize_work_item( +fn execute_optimize_work_item( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: SharedEmitter, @@ -875,22 +854,22 @@ fn execute_optimize_work_item( WorkItemResult::Finished(module) } ComputedLtoType::Thin => { - let (name, thin_buffer) = B::prepare_thin(module); + let thin_buffer = B::serialize_module(module.module_llvm, true); if let Some(path) = bitcode { fs::write(&path, thin_buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); } - WorkItemResult::NeedsThinLto(name, thin_buffer) + WorkItemResult::NeedsThinLto(module.name, thin_buffer) } ComputedLtoType::Fat => match bitcode { Some(path) => { - let (name, buffer) = B::serialize_module(module); + let buffer = B::serialize_module(module.module_llvm, false); fs::write(&path, buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); WorkItemResult::NeedsFatLto(FatLtoInput::Serialized { - name, + name: module.name, buffer: SerializedModule::Local(buffer), }) } @@ -987,7 +966,7 @@ fn execute_copy_from_cache_work_item( } } -fn do_fat_lto( +fn do_fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: SharedEmitter, @@ -1008,7 +987,7 @@ fn do_fat_lto( needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module }) } - let module = B::run_and_optimize_fat_lto( + B::optimize_and_codegen_fat_lto( cgcx, prof, &shared_emitter, @@ -1016,18 +995,17 @@ fn do_fat_lto( exported_symbols_for_lto, each_linked_rlib_for_lto, needs_fat_lto, - ); - B::codegen(cgcx, prof, &shared_emitter, module, &cgcx.module_config) + ) } -fn do_thin_lto( +fn do_thin_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: SharedEmitter, tm_factory: TargetMachineFactoryFn, exported_symbols_for_lto: Arc>, each_linked_rlib_for_lto: Vec, - needs_thin_lto: Vec<(String, ::ThinBuffer)>, + needs_thin_lto: Vec<(String, ::ModuleBuffer)>, lto_import_only_modules: Vec<( SerializedModule<::ModuleBuffer>, WorkProduct, @@ -1173,7 +1151,7 @@ fn do_thin_lto( compiled_modules } -fn execute_thin_lto_work_item( +fn execute_thin_lto_work_item( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: SharedEmitter, @@ -1182,8 +1160,7 @@ fn execute_thin_lto_work_item( ) -> CompiledModule { let _timer = prof.generic_activity_with_arg("codegen_module_perform_lto", module.name()); - let module = B::optimize_thin(cgcx, prof, &shared_emitter, tm_factory, module); - B::codegen(cgcx, prof, &shared_emitter, module, &cgcx.module_config) + B::optimize_and_codegen_thin(cgcx, prof, &shared_emitter, tm_factory, module) } /// Messages sent to the coordinator. @@ -1490,7 +1467,9 @@ fn start_executing_work( // Each LLVM module is automatically sent back to the coordinator for LTO if // necessary. There's already optimizations in place to avoid sending work // back to the coordinator if LTO isn't requested. - return B::spawn_named_thread(cgcx.time_trace, "coordinator".to_string(), move || { + let f = move || { + let _profiler = if cgcx.time_trace { B::thread_profiler() } else { Box::new(()) }; + // This is where we collect codegen units that have gone all the way // through codegen and LLVM. let mut compiled_modules = vec![]; @@ -1811,8 +1790,8 @@ enum CodegenState { )); } else { if let Some(allocator_module) = allocator_module.take() { - let (name, thin_buffer) = B::prepare_thin(allocator_module); - needs_thin_lto.push((name, thin_buffer)); + let thin_buffer = B::serialize_module(allocator_module.module_llvm, true); + needs_thin_lto.push((allocator_module.name, thin_buffer)); } return Ok(MaybeLtoModules::ThinLto { @@ -1825,14 +1804,17 @@ enum CodegenState { } } - Ok(MaybeLtoModules::NoLto { + Ok(MaybeLtoModules::NoLto(CompiledModules { modules: compiled_modules, allocator_module: allocator_module.map(|allocator_module| { B::codegen(&cgcx, &prof, &shared_emitter, allocator_module, &allocator_config) }), - }) - }) - .expect("failed to spawn coordinator thread"); + })) + }; + return std::thread::Builder::new() + .name("coordinator".to_owned()) + .spawn(f) + .expect("failed to spawn coordinator thread"); // A heuristic that determines if we have enough LLVM WorkItems in the // queue so that the main thread can do LLVM work instead of codegen @@ -1896,7 +1878,7 @@ fn queue_full_enough(items_in_queue: usize, workers_running: usize) -> bool { #[must_use] pub(crate) struct WorkerFatalError; -fn spawn_work<'a, B: ExtraBackendMethods>( +fn spawn_work<'a, B: WriteBackendMethods>( cgcx: &CodegenContext, prof: &'a SelfProfilerRef, shared_emitter: SharedEmitter, @@ -1911,7 +1893,10 @@ fn spawn_work<'a, B: ExtraBackendMethods>( let cgcx = cgcx.clone(); let prof = prof.clone(); - B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { + let name = work.short_description(); + let f = move || { + let _profiler = if cgcx.time_trace { B::thread_profiler() } else { Box::new(()) }; + let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, &prof, shared_emitter, m), WorkItem::CopyPostLtoArtifacts(m) => WorkItemResult::Finished( @@ -1932,11 +1917,11 @@ fn spawn_work<'a, B: ExtraBackendMethods>( Err(_) => Message::WorkItem:: { result: Err(None) }, }; drop(coordinator_send.send(msg)); - }) - .expect("failed to spawn work thread"); + }; + std::thread::Builder::new().name(name).spawn(f).expect("failed to spawn work thread"); } -fn spawn_thin_lto_work( +fn spawn_thin_lto_work( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: SharedEmitter, @@ -1947,7 +1932,10 @@ fn spawn_thin_lto_work( let cgcx = cgcx.clone(); let prof = prof.clone(); - B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { + let name = work.short_description(); + let f = move || { + let _profiler = if cgcx.time_trace { B::thread_profiler() } else { Box::new(()) }; + let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { ThinLtoWorkItem::CopyPostLtoArtifacts(m) => { execute_copy_from_cache_work_item(&cgcx, &prof, shared_emitter, m) @@ -1970,8 +1958,8 @@ fn spawn_thin_lto_work( Err(_) => ThinLtoMessage::WorkItem { result: Err(None) }, }; drop(coordinator_send.send(msg)); - }) - .expect("failed to spawn work thread"); + }; + std::thread::Builder::new().name(name).spawn(f).expect("failed to spawn work thread"); } enum SharedEmitterMessage { @@ -2042,10 +2030,6 @@ fn emit_diagnostic(&mut self, mut diag: rustc_errors::DiagInner) { fn source_map(&self) -> Option<&SourceMap> { None } - - fn translator(&self) -> &Translator { - panic!("shared emitter attempted to translate a diagnostic"); - } } impl SharedEmitterMain { @@ -2124,20 +2108,20 @@ fn check(&self, sess: &Session, blocking: bool) { } } -pub struct Coordinator { +pub struct Coordinator { sender: Sender>, future: Option, ()>>>, // Only used for the Message type. phantom: PhantomData, } -impl Coordinator { +impl Coordinator { fn join(mut self) -> std::thread::Result, ()>> { self.future.take().unwrap().join() } } -impl Drop for Coordinator { +impl Drop for Coordinator { fn drop(&mut self) { if let Some(future) = self.future.take() { // If we haven't joined yet, signal to the coordinator that it should spawn no more @@ -2148,9 +2132,8 @@ fn drop(&mut self) { } } -pub struct OngoingCodegen { +pub struct OngoingCodegen { pub backend: B, - pub crate_info: CrateInfo, pub output_filenames: Arc, // Field order below is intended to terminate the coordinator thread before two fields below // drop and prematurely close channels used by coordinator thread. See `Coordinator`'s @@ -2160,8 +2143,8 @@ pub struct OngoingCodegen { pub shared_emitter_main: SharedEmitterMain, } -impl OngoingCodegen { - pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap) { +impl OngoingCodegen { + pub fn join(self, sess: &Session) -> (CompiledModules, FxIndexMap) { self.shared_emitter_main.check(sess, true); let maybe_lto_modules = sess.time("join_worker_thread", || match self.coordinator.join() { @@ -2181,9 +2164,9 @@ pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap { + MaybeLtoModules::NoLto(compiled_modules) => { drop(shared_emitter); - CompiledModules { modules, allocator_module } + compiled_modules } MaybeLtoModules::FatLto { cgcx, @@ -2257,25 +2240,7 @@ pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap) { @@ -2301,7 +2266,7 @@ pub(crate) fn wait_for_signal_to_codegen_item(&self) { } } -pub(crate) fn submit_codegened_module_to_llvm( +pub(crate) fn submit_codegened_module_to_llvm( coordinator: &Coordinator, module: ModuleCodegen, cost: u64, @@ -2310,7 +2275,7 @@ pub(crate) fn submit_codegened_module_to_llvm( drop(coordinator.sender.send(Message::CodegenDone:: { llvm_work_item, cost })); } -pub(crate) fn submit_post_lto_module_to_llvm( +pub(crate) fn submit_post_lto_module_to_llvm( coordinator: &Coordinator, module: CachedModuleCodegen, ) { @@ -2318,7 +2283,7 @@ pub(crate) fn submit_post_lto_module_to_llvm( drop(coordinator.sender.send(Message::CodegenDone:: { llvm_work_item, cost: 0 })); } -pub(crate) fn submit_pre_lto_module_to_llvm( +pub(crate) fn submit_pre_lto_module_to_llvm( tcx: TyCtxt<'_>, coordinator: &Coordinator, module: CachedModuleCodegen, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 85c8890d661c..911844bdbd5d 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -13,8 +13,8 @@ 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::{AttributeKind, DebuggerVisualizerType, OptimizeAttr}; -use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE}; +use rustc_hir::attrs::{DebuggerVisualizerType, OptimizeAttr}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemId, Target, find_attr}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; @@ -681,7 +681,7 @@ pub fn allocator_shim_contents(tcx: TyCtxt<'_>, kind: AllocatorKind) -> Vec( backend: B, tcx: TyCtxt<'_>, - target_cpu: String, + crate_info: &CrateInfo, ) -> OngoingCodegen { if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() { // The target has no default cpu, but none is set explicitly @@ -719,7 +719,7 @@ pub fn codegen_crate( None }; - let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu, allocator_module); + let ongoing_codegen = start_async_codegen(backend.clone(), tcx, crate_info, allocator_module); // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization @@ -894,7 +894,7 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo { let linked_symbols = crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect(); let local_crate_name = tcx.crate_name(LOCAL_CRATE); - let windows_subsystem = find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::WindowsSubsystem(kind, _) => *kind); + let windows_subsystem = find_attr!(tcx, crate, WindowsSubsystem(kind, _) => *kind); // This list is used when generating the command line to pass through to // system linker. The linker expects undefined symbols on the left of the @@ -911,7 +911,7 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo { .rev() .copied() .filter(|&cnum| { - let link = !tcx.dep_kind(cnum).macros_only(); + let link = !tcx.crate_dep_kind(cnum).macros_only(); if link && tcx.is_compiler_builtins(cnum) { compiler_builtins = Some(cnum); return false; @@ -1118,7 +1118,7 @@ pub fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> ) }); - if tcx.try_mark_green(&dep_node) { + if tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() { // We can re-use either the pre- or the post-thinlto state. If no LTO is // being performed then we can use post-LTO artifacts, otherwise we must // reuse pre-LTO artifacts diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index edd73f418036..b8a8bb3ad419 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,20 +1,16 @@ -use std::str::FromStr; - use rustc_abi::{Align, ExternAbi}; -use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; -use rustc_ast::{LitKind, MetaItem, MetaItemInner}; use rustc_hir::attrs::{ AttributeKind, EiiImplResolution, InlineAttr, Linkage, RtsanSetting, UsedBy, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{self as hir, Attribute, find_attr}; +use rustc_macros::Diagnostic; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs, }; use rustc_middle::mir::mono::Visibility; use rustc_middle::query::Providers; -use rustc_middle::span_bug; use rustc_middle::ty::{self as ty, TyCtxt}; use rustc_session::lint; use rustc_session::parse::feature_err; @@ -80,7 +76,7 @@ fn process_builtin_attrs( interesting_spans.inline = Some(*span); } AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, - AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), + AttributeKind::RustcAlign { align, .. } => codegen_fn_attrs.alignment = Some(*align), AttributeKind::LinkName { name, .. } => { // FIXME Remove check for foreign functions once #[link_name] on non-foreign // functions is a hard error @@ -223,16 +219,14 @@ fn process_builtin_attrs( AttributeKind::RustcObjcSelector { methname, .. } => { codegen_fn_attrs.objc_selector = Some(*methname); } - AttributeKind::EiiForeignItem => { + AttributeKind::RustcEiiForeignItem => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; } AttributeKind::EiiImpls(impls) => { for i in impls { let foreign_item = match i.resolution { EiiImplResolution::Macro(def_id) => { - let Some(extern_item) = find_attr!( - tcx.get_all_attrs(def_id), - AttributeKind::EiiDeclaration(target) => target.foreign_item + let Some(extern_item) = find_attr!(tcx, def_id, EiiDeclaration(target) => target.foreign_item ) else { tcx.dcx().span_delayed_bug( i.span, @@ -352,8 +346,7 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code // When `no_builtins` is applied at the crate level, we should add the // `no-builtins` attribute to each function to ensure it takes effect in LTO. - let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID); - let no_builtins = find_attr!(crate_attrs, AttributeKind::NoBuiltins); + let no_builtins = find_attr!(tcx, crate, NoBuiltins); if no_builtins { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS; } @@ -393,6 +386,17 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code } } +#[derive(Diagnostic)] +#[diag("non-default `sanitize` will have no effect after inlining")] +struct SanitizeOnInline { + #[note("inlining requested here")] + inline_span: Span, +} + +#[derive(Diagnostic)] +#[diag("the async executor can run blocking code, without realtime sanitizer catching it")] +struct AsyncBlocking; + fn check_result( tcx: TyCtxt<'_>, did: LocalDefId, @@ -433,10 +437,12 @@ fn check_result( (interesting_spans.sanitize, interesting_spans.inline) { let hir_id = tcx.local_def_id_to_hir_id(did); - tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, sanitize_span, |lint| { - lint.primary_message("non-default `sanitize` will have no effect after inlining"); - lint.span_note(inline_span, "inlining requested here"); - }) + tcx.emit_node_span_lint( + lint::builtin::INLINE_NO_SANITIZE, + hir_id, + sanitize_span, + SanitizeOnInline { inline_span }, + ) } // warn for nonblocking async functions, blocks and closures. @@ -453,13 +459,11 @@ fn check_result( != rustc_hir::ClosureKind::Closure)) { let hir_id = tcx.local_def_id_to_hir_id(did); - tcx.node_span_lint( + tcx.emit_node_span_lint( lint::builtin::RTSAN_NONBLOCKING_ASYNC, hir_id, sanitize_span, - |lint| { - lint.primary_message(r#"the async executor can run blocking code, without realtime sanitizer catching it"#); - } + AsyncBlocking, ); } @@ -483,9 +487,8 @@ fn check_result( .map(|features| (features.name.as_str(), true)) .collect(), ) { - let span = - find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature{attr_span: span, ..} => *span) - .unwrap_or_else(|| tcx.def_span(did)); + let span = find_attr!(tcx, did, TargetFeature{attr_span: span, ..} => *span) + .unwrap_or_else(|| tcx.def_span(did)); tcx.dcx() .create_err(errors::TargetFeatureDisableOrEnable { @@ -504,7 +507,7 @@ fn handle_lang_items( attrs: &[Attribute], codegen_fn_attrs: &mut CodegenFnAttrs, ) { - let lang_item = find_attr!(attrs, AttributeKind::Lang(lang, _) => lang); + let lang_item = find_attr!(attrs, Lang(lang, _) => lang); // Weak lang items have the same semantics as "std internal" symbols in the // sense that they're preserved through all our LTO passes and only @@ -583,7 +586,8 @@ fn sanitizer_settings_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerFnAttrs }; // Check for a sanitize annotation directly on this def. - if let Some((on_set, off_set, rtsan)) = find_attr!(tcx.get_all_attrs(did), AttributeKind::Sanitize {on_set, off_set, rtsan, ..} => (on_set, off_set, rtsan)) + if let Some((on_set, off_set, rtsan)) = + find_attr!(tcx, did, Sanitize {on_set, off_set, rtsan, ..} => (on_set, off_set, rtsan)) { // the on set is the set of sanitizers explicitly enabled. // we mask those out since we want the set of disabled sanitizers here @@ -617,115 +621,6 @@ fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option { tcx.codegen_fn_attrs(tcx.trait_item_of(def_id)?).alignment } -/// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)] -/// macros. There are two forms. The pure one without args to mark primal functions (the functions -/// being differentiated). The other form is #[rustc_autodiff(Mode, ActivityList)] on top of the -/// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never -/// panic, unless we introduced a bug when parsing the autodiff macro. -//FIXME(jdonszelmann): put in the main loop. No need to have two..... :/ Let's do that when we make autodiff parsed. -pub fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { - let attrs = tcx.get_attrs(id, sym::rustc_autodiff); - - let attrs = attrs.filter(|attr| attr.has_name(sym::rustc_autodiff)).collect::>(); - - // check for exactly one autodiff attribute on placeholder functions. - // There should only be one, since we generate a new placeholder per ad macro. - let attr = match &attrs[..] { - [] => return None, - [attr] => attr, - _ => { - span_bug!(attrs[1].span(), "cg_ssa: rustc_autodiff should only exist once per source"); - } - }; - - let list = attr.meta_item_list().unwrap_or_default(); - - // empty autodiff attribute macros (i.e. `#[autodiff]`) are used to mark source functions - if list.is_empty() { - return Some(AutoDiffAttrs::source()); - } - - let [mode, width_meta, input_activities @ .., ret_activity] = &list[..] else { - span_bug!(attr.span(), "rustc_autodiff attribute must contain mode, width and activities"); - }; - let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode { - p1.segments.first().unwrap().ident - } else { - span_bug!(attr.span(), "rustc_autodiff attribute must contain mode"); - }; - - // parse mode - let mode = match mode.as_str() { - "Forward" => DiffMode::Forward, - "Reverse" => DiffMode::Reverse, - _ => { - span_bug!(mode.span, "rustc_autodiff attribute contains invalid mode"); - } - }; - - let width: u32 = match width_meta { - MetaItemInner::MetaItem(MetaItem { path: p1, .. }) => { - let w = p1.segments.first().unwrap().ident; - match w.as_str().parse() { - Ok(val) => val, - Err(_) => { - span_bug!(w.span, "rustc_autodiff width should fit u32"); - } - } - } - MetaItemInner::Lit(lit) => { - if let LitKind::Int(val, _) = lit.kind { - match val.get().try_into() { - Ok(val) => val, - Err(_) => { - span_bug!(lit.span, "rustc_autodiff width should fit u32"); - } - } - } else { - span_bug!(lit.span, "rustc_autodiff width should be an integer"); - } - } - }; - - // First read the ret symbol from the attribute - let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity else { - span_bug!(attr.span(), "rustc_autodiff attribute must contain the return activity"); - }; - let ret_symbol = p1.segments.first().unwrap().ident; - - // Then parse it into an actual DiffActivity - let Ok(ret_activity) = DiffActivity::from_str(ret_symbol.as_str()) else { - span_bug!(ret_symbol.span, "invalid return activity"); - }; - - // Now parse all the intermediate (input) activities - let mut arg_activities: Vec = vec![]; - for arg in input_activities { - let arg_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p2, .. }) = arg { - match p2.segments.first() { - Some(x) => x.ident, - None => { - span_bug!( - arg.span(), - "rustc_autodiff attribute must contain the input activity" - ); - } - } - } else { - span_bug!(arg.span(), "rustc_autodiff attribute must contain the input activity"); - }; - - match DiffActivity::from_str(arg_symbol.as_str()) { - Ok(arg_activity) => arg_activities.push(arg_activity), - Err(_) => { - span_bug!(arg_symbol.span, "invalid input activity"); - } - } - } - - Some(AutoDiffAttrs { mode, width, ret_activity, input_activity: arg_activities }) -} - pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { codegen_fn_attrs, diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 1c88a8da5ea9..d3b2caca4742 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -430,7 +430,19 @@ fn push_debuginfo_type_name<'tcx>( push_closure_or_coroutine_name(tcx, def_id, args, qualified, output, visited); } } - ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binders)"), + ty::UnsafeBinder(inner) => { + if cpp_like_debuginfo { + output.push_str("unsafe$<"); + } else { + output.push_str("unsafe "); + } + + push_debuginfo_type_name(tcx, inner.skip_binder(), qualified, output, visited); + + if cpp_like_debuginfo { + push_close_angle_bracket(cpp_like_debuginfo, output); + } + } ty::Param(_) | ty::Error(_) | ty::Infer(_) diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 74f7829ace95..be1965f67491 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -10,7 +10,7 @@ use rustc_errors::{ Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, msg, }; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::layout::LayoutError; use rustc_middle::ty::{FloatTy, Ty}; use rustc_span::{Span, Symbol}; @@ -1164,7 +1164,7 @@ pub(crate) struct XcrunSdkPathWarning { pub stderr: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("enabling the `neon` target feature on the current target is unsound due to ABI issues")] pub(crate) struct Aarch64SoftfloatNeon; diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index cbfa12e21eba..ced4b59c4f0c 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -1,8 +1,6 @@ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] #![feature(file_buffered)] -#![feature(if_let_guard)] #![feature(negative_impls)] #![feature(string_from_utf8_lossy_owned)] #![feature(trait_alias)] @@ -24,6 +22,7 @@ use rustc_hir::CRATE_HIR_ID; use rustc_hir::attrs::{CfgEntry, NativeLibKind, WindowsSubsystemKind}; use rustc_hir::def_id::CrateNum; +use rustc_lint_defs::builtin::LINKER_INFO; use rustc_macros::{Decodable, Encodable}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::WorkProduct; @@ -209,7 +208,8 @@ fn from(lib: &cstore::NativeLib) -> Self { /// identifiers (`CrateNum`) to `CrateSource`. The other fields map `CrateNum` to the crate's own /// additional properties, so that effectively we can retrieve each dependent crate's `CrateSource` /// and the corresponding properties without referencing information outside of a `CrateInfo`. -#[derive(Debug, Encodable, Decodable)] +// rustc_codegen_cranelift needs a Clone impl for its jit mode, which isn't tested in rust CI +#[derive(Clone, Debug, Encodable, Decodable)] pub struct CrateInfo { pub target_cpu: String, pub target_features: Vec, @@ -251,10 +251,9 @@ pub struct TargetConfig { } #[derive(Encodable, Decodable)] -pub struct CodegenResults { +pub struct CompiledModules { pub modules: Vec, pub allocator_module: Option, - pub crate_info: CrateInfo, } pub enum CodegenErrors { @@ -293,11 +292,12 @@ pub fn looks_like_rust_object_file(filename: &str) -> bool { const RLINK_VERSION: u32 = 1; const RLINK_MAGIC: &[u8] = b"rustlink"; -impl CodegenResults { +impl CompiledModules { pub fn serialize_rlink( sess: &Session, rlink_file: &Path, - codegen_results: &CodegenResults, + compiled_modules: &CompiledModules, + crate_info: &CrateInfo, metadata: &EncodedMetadata, outputs: &OutputFilenames, ) -> Result { @@ -307,7 +307,8 @@ pub fn serialize_rlink( // Encoder's inner representation of `u32`. encoder.emit_raw_bytes(&RLINK_VERSION.to_be_bytes()); encoder.emit_str(sess.cfg_version); - Encodable::encode(codegen_results, &mut encoder); + Encodable::encode(compiled_modules, &mut encoder); + Encodable::encode(crate_info, &mut encoder); Encodable::encode(metadata, &mut encoder); Encodable::encode(outputs, &mut encoder); encoder.finish().map_err(|(_path, err)| err) @@ -316,7 +317,7 @@ pub fn serialize_rlink( pub fn deserialize_rlink( sess: &Session, data: Vec, - ) -> Result<(Self, EncodedMetadata, OutputFilenames), CodegenErrors> { + ) -> Result<(Self, CrateInfo, EncodedMetadata, OutputFilenames), CodegenErrors> { // The Decodable machinery is not used here because it panics if the input data is invalid // and because its internal representation may change. if !data.starts_with(RLINK_MAGIC) { @@ -346,10 +347,11 @@ pub fn deserialize_rlink( }); } - let codegen_results = CodegenResults::decode(&mut decoder); + let compiled_modules = CompiledModules::decode(&mut decoder); + let crate_info = CrateInfo::decode(&mut decoder); let metadata = EncodedMetadata::decode(&mut decoder); let outputs = OutputFilenames::decode(&mut decoder); - Ok((codegen_results, metadata, outputs)) + Ok((compiled_modules, crate_info, metadata, outputs)) } } @@ -361,10 +363,14 @@ pub fn deserialize_rlink( #[derive(Copy, Clone, Debug, Encodable, Decodable)] pub struct CodegenLintLevels { linker_messages: LevelAndSource, + linker_info: LevelAndSource, } impl CodegenLintLevels { pub fn from_tcx(tcx: TyCtxt<'_>) -> Self { - Self { linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID) } + Self { + linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID), + linker_info: tcx.lint_level_at_node(LINKER_INFO, CRATE_HIR_ID), + } } } diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 2fa466b50017..b87034f9b33b 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -114,9 +114,7 @@ pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( let vtable_alloc_id = tcx.vtable_allocation((ty, trait_ref)); let vtable_allocation = tcx.global_alloc(vtable_alloc_id).unwrap_memory(); - let vtable_const = cx.const_data_from_alloc(vtable_allocation); - let align = cx.data_layout().pointer_align().abi; - let vtable = cx.static_addr_of(vtable_const, align, Some("vtable")); + let vtable = cx.static_addr_of(vtable_allocation, Some("vtable")); cx.apply_vcall_visibility_metadata(ty, trait_ref, vtable); cx.create_vtable_debuginfo(ty, trait_ref, vtable); @@ -139,9 +137,8 @@ pub(crate) fn load_vtable<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( && bx.cx().sess().lto() == Lto::Fat { if let Some(trait_ref) = dyn_trait_in_self(bx.tcx(), ty) { - let typeid = - bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), trait_ref).as_bytes()).unwrap(); - let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid); + let typeid = typeid_for_trait_ref(bx.tcx(), trait_ref); + let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid.as_bytes()); return func; } else if nonnull { bug!("load nonnull value from a vtable without a principal trait") diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 35de8b5e1486..cf643931717b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -215,19 +215,18 @@ fn do_call>( mir::UnwindAction::Continue => None, mir::UnwindAction::Unreachable => None, mir::UnwindAction::Terminate(reason) => { - if fx.mir[self.bb].is_cleanup && base::wants_new_eh_instructions(fx.cx.tcx().sess) { + if fx.mir[self.bb].is_cleanup && base::wants_wasm_eh(fx.cx.tcx().sess) { + // For wasm, we need to generate a nested `cleanuppad within %outer_pad` + // to catch exceptions during cleanup and call `panic_in_cleanup`. + Some(fx.terminate_block(reason, Some(self.bb))) + } else if fx.mir[self.bb].is_cleanup + && base::wants_new_eh_instructions(fx.cx.tcx().sess) + { // MSVC SEH will abort automatically if an exception tries to // propagate out from cleanup. - - // FIXME(@mirkootter): For wasm, we currently do not support terminate during - // cleanup, because this requires a few more changes: The current code - // caches the `terminate_block` for each function; funclet based code - however - - // requires a different terminate_block for each funclet - // Until this is implemented, we just do not unwind inside cleanup blocks - None } else { - Some(fx.terminate_block(reason)) + Some(fx.terminate_block(reason, None)) } } }; @@ -239,7 +238,7 @@ fn do_call>( if let Some(unwind_block) = unwind_block { let ret_llbb = if let Some((_, target)) = destination { - fx.llbb(target) + self.llbb_with_cleanup(fx, target) } else { fx.unreachable_block() }; @@ -310,7 +309,7 @@ fn do_inlineasm>( ) -> MergingSucc { let unwind_target = match unwind { mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)), - mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason)), + mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason, None)), mir::UnwindAction::Continue => None, mir::UnwindAction::Unreachable => None, }; @@ -318,7 +317,7 @@ fn do_inlineasm>( if operands.iter().any(|x| matches!(x, InlineAsmOperandRef::Label { .. })) { assert!(unwind_target.is_none()); let ret_llbb = if let Some(target) = destination { - fx.llbb(target) + self.llbb_with_cleanup(fx, target) } else { fx.unreachable_block() }; @@ -335,7 +334,7 @@ fn do_inlineasm>( MergingSucc::False } else if let Some(cleanup) = unwind_target { let ret_llbb = if let Some(target) = destination { - fx.llbb(target) + self.llbb_with_cleanup(fx, target) } else { fx.unreachable_block() }; @@ -1147,19 +1146,51 @@ fn codegen_call_terminator( (args, None) }; + // Special logic for tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments. + // + // Normally an indirect argument with `on_stack: false` would be passed as a pointer into + // the caller's stack frame. For tail calls, that would be unsound, because the caller's + // stack frame is overwritten by the callee's stack frame. + // + // Therefore we store the argument for the callee in the corresponding caller's slot. + // Because guaranteed tail calls demand that the caller's signature matches the callee's, + // the corresponding slot has the correct type. + // + // To handle cases like the one below, the tail call arguments must first be copied to a + // temporary, and only then copied to the caller's argument slots. + // + // ``` + // // A struct big enough that it is not passed via registers. + // pub struct Big([u64; 4]); + // + // fn swapper(a: Big, b: Big) -> (Big, Big) { + // become swapper_helper(b, a); + // } + // ``` + 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 , ..}` + // 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, .. }) { + continue; + } + + let op = self.codegen_operand(bx, &arg.node); + let tmp = PlaceRef::alloca(bx, op.layout); + bx.lifetime_start(tmp.val.llval, tmp.layout.size); + op.store_with_annotation(bx, tmp); + + tail_call_temporaries[i] = Some(tmp); + } + } + // When generating arguments we sometimes introduce temporary allocations with lifetime // that extend for the duration of a call. Keep track of those allocations and their sizes // to generate `lifetime_end` when the call returns. let mut lifetime_ends_after_call: Vec<(Bx::Value, Size)> = Vec::new(); 'make_args: for (i, arg) in first_args.iter().enumerate() { - if kind == CallKind::Tail && matches!(fn_abi.args[i].mode, PassMode::Indirect { .. }) { - // FIXME: https://github.com/rust-lang/rust/pull/144232#discussion_r2218543841 - span_bug!( - fn_span, - "arguments using PassMode::Indirect are currently not supported for tail calls" - ); - } - let mut op = self.codegen_operand(bx, &arg.node); if let (0, Some(ty::InstanceKind::Virtual(_, idx))) = (i, instance.map(|i| i.def)) { @@ -1210,18 +1241,54 @@ fn codegen_call_terminator( } } - // The callee needs to own the argument memory if we pass it - // by-ref, so make a local copy of non-immediate constants. - match (&arg.node, op.val) { - (&mir::Operand::Copy(_), Ref(PlaceValue { llextra: None, .. })) - | (&mir::Operand::Constant(_), Ref(PlaceValue { llextra: None, .. })) => { - let tmp = PlaceRef::alloca(bx, op.layout); - bx.lifetime_start(tmp.val.llval, tmp.layout.size); - op.store_with_annotation(bx, tmp); - op.val = Ref(tmp.val); - lifetime_ends_after_call.push((tmp.val.llval, tmp.layout.size)); + match kind { + CallKind::Normal => { + // The callee needs to own the argument memory if we pass it + // by-ref, so make a local copy of non-immediate constants. + if let &mir::Operand::Copy(_) | &mir::Operand::Constant(_) = &arg.node + && let Ref(PlaceValue { llextra: None, .. }) = op.val + { + let tmp = PlaceRef::alloca(bx, op.layout); + bx.lifetime_start(tmp.val.llval, tmp.layout.size); + op.store_with_annotation(bx, tmp); + op.val = Ref(tmp.val); + lifetime_ends_after_call.push((tmp.val.llval, tmp.layout.size)); + } + } + CallKind::Tail => { + if let PassMode::Indirect { on_stack: false, .. } = fn_abi.args[i].mode { + let Some(tmp) = tail_call_temporaries[i].take() else { + span_bug!( + fn_span, + "missing temporary for indirect tail call argument #{i}" + ) + }; + + let local = self.mir.args_iter().nth(i).unwrap(); + + match &self.locals[local] { + LocalRef::Place(arg) => { + bx.typed_place_copy(arg.val, tmp.val, fn_abi.args[i].layout); + op.val = Ref(arg.val); + } + LocalRef::Operand(arg) => { + let Ref(place_value) = arg.val else { + bug!("only `Ref` should use `PassMode::Indirect`"); + }; + bx.typed_place_copy(place_value, tmp.val, fn_abi.args[i].layout); + op.val = arg.val; + } + LocalRef::UnsizedPlace(_) => { + span_bug!(fn_span, "unsized types are not supported") + } + LocalRef::PendingOperand => { + span_bug!(fn_span, "argument local should not be pending") + } + }; + + bx.lifetime_end(tmp.val.llval, tmp.layout.size); + } } - _ => {} } self.codegen_argument( @@ -1830,8 +1897,39 @@ fn unreachable_block(&mut self) -> Bx::BasicBlock { }) } - fn terminate_block(&mut self, reason: UnwindTerminateReason) -> Bx::BasicBlock { - if let Some((cached_bb, cached_reason)) = self.terminate_block + fn terminate_block( + &mut self, + reason: UnwindTerminateReason, + outer_catchpad_bb: Option, + ) -> Bx::BasicBlock { + // mb_funclet_bb should be present if and only if the target is wasm and + // we're terminating because of an unwind in a cleanup block. In that + // case we have nested funclets and the inner catch_switch needs to know + // what outer catch_pad it is contained in. + debug_assert!( + outer_catchpad_bb.is_some() + == (base::wants_wasm_eh(self.cx.tcx().sess) + && reason == UnwindTerminateReason::InCleanup) + ); + + // When we aren't in a wasm InCleanup block, there's only one terminate + // block needed so we cache at START_BLOCK index. + let mut cache_bb = mir::START_BLOCK; + // In wasm eh InCleanup, use the outer funclet's cleanup BB as the cache + // key. + if let Some(outer_bb) = outer_catchpad_bb { + let cleanup_kinds = + self.cleanup_kinds.as_ref().expect("cleanup_kinds required for funclets"); + cache_bb = cleanup_kinds[outer_bb] + .funclet_bb(outer_bb) + .expect("funclet_bb should be in a funclet"); + + // Ensure the outer funclet is created first + if self.funclets[cache_bb].is_none() { + self.landing_pad_for(cache_bb); + } + } + if let Some((cached_bb, cached_reason)) = self.terminate_blocks[cache_bb] && reason == cached_reason { return cached_bb; @@ -1869,12 +1967,35 @@ fn terminate_block(&mut self, reason: UnwindTerminateReason) -> Bx::BasicBlock { // cp_terminate: // %cp = catchpad within %cs [null, i32 64, null] // ... + // + // By contrast, on WebAssembly targets, we specifically _do_ want to + // catch foreign exceptions. The situation with MSVC is a + // regrettable hack which we don't want to extend to other targets + // unless necessary. For WebAssembly, to generate catch(...) and + // catch only C++ exception instead of generating a catch_all, we + // need to call the intrinsics @llvm.wasm.get.exception and + // @llvm.wasm.get.ehselector in the catch pad. Since we don't do + // this, we generate a catch_all. We originally got this behavior + // by accident but it luckily matches our intention. llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate"); - let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate"); let mut cs_bx = Bx::build(self.cx, llbb); - let cs = cs_bx.catch_switch(None, None, &[cp_llbb]); + + // For wasm InCleanup blocks, our catch_switch is nested within the + // outer catchpad, so we need to provide it as the parent value to + // catch_switch. + let mut outer_cleanuppad = None; + if outer_catchpad_bb.is_some() { + // Get the outer funclet's catchpad + let outer_funclet = self.funclets[cache_bb] + .as_ref() + .expect("landing_pad_for didn't create funclet"); + outer_cleanuppad = Some(cs_bx.get_funclet_cleanuppad(outer_funclet)); + } + let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate"); + let cs = cs_bx.catch_switch(outer_cleanuppad, None, &[cp_llbb]); + drop(cs_bx); bx = Bx::build(self.cx, cp_llbb); let null = @@ -1895,13 +2016,18 @@ fn terminate_block(&mut self, reason: UnwindTerminateReason) -> Bx::BasicBlock { } else { // Specifying more arguments than necessary usually doesn't // hurt, but the `WasmEHPrepare` LLVM pass does not recognize - // anything other than a single `null` as a `catch (...)` block, + // anything other than a single `null` as a `catch_all` block, // leading to problems down the line during instruction // selection. &[null] as &[_] }; funclet = Some(bx.catch_pad(cs, args)); + // On wasm, if we wanted to generate a catch(...) and only catch C++ + // exceptions, we'd call @llvm.wasm.get.exception and + // @llvm.wasm.get.ehselector selectors here. We want a catch_all so + // we leave them out. This is intentionally diverging from the MSVC + // behavior. } else { llbb = Bx::append_block(self.cx, self.llfn, "terminate"); bx = Bx::build(self.cx, llbb); @@ -1927,7 +2053,7 @@ fn terminate_block(&mut self, reason: UnwindTerminateReason) -> Bx::BasicBlock { bx.unreachable(); - self.terminate_block = Some((llbb, reason)); + self.terminate_blocks[cache_bb] = Some((llbb, reason)); llbb } diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index f5ee9406f4bf..fd0c7c656ac2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -1,4 +1,4 @@ -use rustc_abi::WrappingRange; +use rustc_abi::{Align, WrappingRange}; use rustc_middle::mir::SourceInfo; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -179,9 +179,11 @@ pub fn codegen_intrinsic_call( let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128; bx.range_metadata(value, WrappingRange { start: 0, end: size_bound }); } - // Alignment is always nonzero. + // Alignment is always a power of two, thus 1..=0x800…000, + // but also bounded by the maximum we support in type layout. sym::vtable_align => { - bx.range_metadata(value, WrappingRange { start: 1, end: !0 }) + let align_bound = Align::max_for_target(bx.data_layout()).bytes().into(); + bx.range_metadata(value, WrappingRange { start: 1, end: align_bound }) } _ => {} } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 819abb9ae644..1a0f66d31cca 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -90,8 +90,11 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// Cached unreachable block unreachable_block: Option, - /// Cached terminate upon unwinding block and its reason - terminate_block: Option<(Bx::BasicBlock, UnwindTerminateReason)>, + /// Cached terminate upon unwinding block and its reason. For non-wasm + /// targets, there is at most one such block per function, stored at index + /// `START_BLOCK`. For wasm targets, each funclet needs its own terminate + /// block, indexed by the cleanup block that is the funclet's head. + terminate_blocks: IndexVec>, /// A bool flag for each basic block indicating whether it is a cold block. /// A cold block is a block that is unlikely to be executed at runtime. @@ -227,7 +230,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( personality_slot: None, cached_llbbs, unreachable_block: None, - terminate_block: None, + terminate_blocks: IndexVec::from_elem(None, &mir.basic_blocks), cleanup_kinds, landing_pads: IndexVec::from_elem(None, &mir.basic_blocks), funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()), diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 50ca676b5d05..85fa890453d8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -245,10 +245,7 @@ fn from_const_alloc>( _ if layout.is_zst() => OperandRef::zero_sized(layout), _ => { // Neither a scalar nor scalar pair. Load from a place - // FIXME: should we cache `const_data_from_alloc` to avoid repeating this for the - // same `ConstAllocation`? - let init = bx.const_data_from_alloc(alloc); - let base_addr = bx.static_addr_of(init, alloc_align, None); + let base_addr = bx.static_addr_of(alloc, None); let llval = bx.const_ptr_byte_offset(base_addr, offset); bx.load_operand(PlaceRef::new_sized(llval, layout)) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index ca8c8dd06ba6..2cb96c4ec0f5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -710,7 +710,6 @@ pub(crate) fn codegen_rvalue_operand( OperandRef { val: operand.val, layout, move_annotation: None } } mir::Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), - mir::Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in codegen"), } } diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index d0121f764380..bc3ffa24d528 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -1,5 +1,5 @@ use rustc_middle::mir::{self, NonDivergingIntrinsic, StmtDebugInfo}; -use rustc_middle::span_bug; +use rustc_middle::{bug, span_bug, ty}; use tracing::instrument; use super::{FunctionCx, LocalRef}; @@ -77,15 +77,21 @@ pub(crate) fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Stateme let dst_val = self.codegen_operand(bx, dst); let src_val = self.codegen_operand(bx, src); let count = self.codegen_operand(bx, count).immediate(); - let pointee_layout = dst_val - .layout - .pointee_info_at(bx, rustc_abi::Size::ZERO) - .expect("Expected pointer"); - let bytes = bx.mul(count, bx.const_usize(pointee_layout.size.bytes())); - let align = pointee_layout.align; + let &ty::RawPtr(pointee, _) = dst_val.layout.ty.kind() else { + bug!("expected pointer") + }; + let pointee_layout = bx + .tcx() + .layout_of(bx.typing_env().as_query_input(pointee)) + .expect("expected pointee to have a layout"); + let elem_size = pointee_layout.layout.size().bytes(); + let bytes = bx.mul(count, bx.const_usize(elem_size)); + + let align = pointee_layout.layout.align.abi; let dst = dst_val.immediate(); let src = src_val.immediate(); + bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty(), None); } mir::StatementKind::FakeRead(..) diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index e1bd8014d7a2..52ffc321cbb6 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -1,6 +1,6 @@ //! Computing the size and alignment of a value. -use rustc_abi::WrappingRange; +use rustc_abi::{Align, WrappingRange}; use rustc_hir::LangItem; use rustc_middle::bug; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; @@ -36,8 +36,10 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Size is always <= isize::MAX. let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128; bx.range_metadata(size, WrappingRange { start: 0, end: size_bound }); - // Alignment is always nonzero. - bx.range_metadata(align, WrappingRange { start: 1, end: !0 }); + // Alignment is always a power of two, thus 1..=0x800…000, + // but also bounded by the maximum we support in type layout. + let align_bound = Align::max_for_target(bx.data_layout()).bytes().into(); + bx.range_metadata(align, WrappingRange { start: 1, end: align_bound }); (size, align) } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 7ebff77710fb..7b95562ddda3 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -10,25 +10,24 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; -use rustc_session::config::{self, CrateType, OutputFilenames, PrintRequest}; +use rustc_session::config::{CrateType, OutputFilenames, PrintRequest}; use rustc_span::Symbol; use super::CodegenObject; use super::write::WriteBackendMethods; use crate::back::archive::ArArchiveBuilderBuilder; use crate::back::link::link_binary; -use crate::back::write::TargetMachineFactoryFn; -use crate::{CodegenResults, ModuleCodegen, TargetConfig}; +use crate::{CompiledModules, CrateInfo, ModuleCodegen, TargetConfig}; pub trait BackendTypes { - type Value: CodegenObject + PartialEq; - type Metadata: CodegenObject; type Function: CodegenObject; - type BasicBlock: Copy; - type Type: CodegenObject + PartialEq; type Funclet; + type Value: CodegenObject + PartialEq; + type Type: CodegenObject + PartialEq; + type FunctionSignature: CodegenObject + PartialEq; + // FIXME(eddyb) find a common convention for all of the debuginfo-related // names (choose between `Dbg`, `Debug`, `DebugInfo`, `DI` etc.). type DIScope: Copy + Hash + PartialEq + Eq; @@ -103,7 +102,9 @@ fn metadata_loader(&self) -> Box { fn provide(&self, _providers: &mut Providers) {} - fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box; + fn target_cpu(&self, sess: &Session) -> String; + + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, crate_info: &CrateInfo) -> Box; /// This is called on the returned `Box` from [`codegen_crate`](Self::codegen_crate) /// @@ -115,20 +116,26 @@ fn join_codegen( ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, - ) -> (CodegenResults, FxIndexMap); + ) -> (CompiledModules, FxIndexMap); - /// This is called on the returned [`CodegenResults`] from [`join_codegen`](Self::join_codegen). + fn print_pass_timings(&self) {} + + fn print_statistics(&self) {} + + /// This is called on the returned [`CompiledModules`] from [`join_codegen`](Self::join_codegen). fn link( &self, sess: &Session, - codegen_results: CodegenResults, + compiled_modules: CompiledModules, + crate_info: CrateInfo, metadata: EncodedMetadata, outputs: &OutputFilenames, ) { link_binary( sess, &ArArchiveBuilderBuilder, - codegen_results, + compiled_modules, + crate_info, metadata, outputs, self.name(), @@ -137,7 +144,7 @@ fn link( } pub trait ExtraBackendMethods: - CodegenBackend + WriteBackendMethods + Sized + Send + Sync + DynSend + DynSync + WriteBackendMethods + Sized + Send + Sync + DynSend + DynSync { fn codegen_allocator<'tcx>( &self, @@ -154,26 +161,6 @@ fn compile_codegen_unit( cgu_name: Symbol, ) -> (ModuleCodegen, u64); - fn target_machine_factory( - &self, - sess: &Session, - opt_level: config::OptLevel, - target_features: &[String], - ) -> TargetMachineFactoryFn; - - fn spawn_named_thread( - _time_trace: bool, - name: String, - f: F, - ) -> std::io::Result> - where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, - { - std::thread::Builder::new().name(name).spawn(f) - } - /// Returns `true` if this backend can be safely called from multiple threads. /// /// Defaults to `true`. diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 3486bd140ece..05e94b8019f4 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -1,7 +1,7 @@ +use std::assert_matches; use std::ops::Deref; use rustc_abi::{Align, Scalar, Size, WrappingRange}; -use rustc_data_structures::assert_matches; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; @@ -50,10 +50,10 @@ pub trait BuilderMethods<'a, 'tcx>: type CodegenCx: CodegenMethods< 'tcx, Value = Self::Value, - Metadata = Self::Metadata, Function = Self::Function, BasicBlock = Self::BasicBlock, Type = Self::Type, + FunctionSignature = Self::FunctionSignature, Funclet = Self::Funclet, DIScope = Self::DIScope, DILocation = Self::DILocation, @@ -125,7 +125,7 @@ fn switch_with_weights( fn invoke( &mut self, - llty: Self::Type, + llty: Self::FunctionSignature, fn_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, llfn: Self::Value, @@ -552,12 +552,12 @@ fn select( fn set_personality_fn(&mut self, personality: Self::Function); - // These are used by everyone except msvc + // These are used by everyone except msvc and wasm EH fn cleanup_landing_pad(&mut self, pers_fn: Self::Function) -> (Self::Value, Self::Value); fn filter_landing_pad(&mut self, pers_fn: Self::Function); fn resume(&mut self, exn0: Self::Value, exn1: Self::Value); - // These are used only by msvc + // These are used by msvc and wasm EH fn cleanup_pad(&mut self, parent: Option, args: &[Self::Value]) -> Self::Funclet; fn cleanup_ret(&mut self, funclet: &Self::Funclet, unwind: Option); fn catch_pad(&mut self, parent: Self::Value, args: &[Self::Value]) -> Self::Funclet; @@ -567,6 +567,7 @@ fn catch_switch( unwind: Option, handlers: &[Self::BasicBlock], ) -> Self::Value; + fn get_funclet_cleanuppad(&self, funclet: &Self::Funclet) -> Self::Value; fn atomic_cmpxchg( &mut self, @@ -622,7 +623,7 @@ fn atomic_rmw( /// assuming the function does not explicitly pass the destination as a pointer in `args`. fn call( &mut self, - llty: Self::Type, + llty: Self::FunctionSignature, caller_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, fn_val: Self::Value, @@ -633,7 +634,7 @@ fn call( fn tail_call( &mut self, - llty: Self::Type, + llty: Self::FunctionSignature, caller_attrs: Option<&CodegenFnAttrs>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, llfn: Self::Value, diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index d83a04d814be..4178a9742e26 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -1,5 +1,5 @@ use rustc_abi as abi; -use rustc_middle::mir::interpret::{ConstAllocation, Scalar}; +use rustc_middle::mir::interpret::Scalar; use super::BackendTypes; @@ -37,8 +37,6 @@ pub trait ConstCodegenMethods: BackendTypes { fn const_to_opt_uint(&self, v: Self::Value) -> Option; fn const_to_opt_u128(&self, v: Self::Value, sign_ext: bool) -> Option; - fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value; - fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value; fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index 04183c2801e7..d1e6436f6b1e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -41,7 +41,7 @@ fn type_checked_load( &mut self, llvtable: Self::Value, vtable_byte_offset: u64, - typeid: Self::Metadata, + typeid: &[u8], ) -> Self::Value; /// Trait method used to inject `va_start` on the "spoofed" `VaList` in /// Rust defined C-variadic functions. diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index 710fab279016..6a0f88983349 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -25,5 +25,5 @@ fn apply_vcall_visibility_metadata( fn apply_target_cpu_attr(&self, llfn: Self::Function); /// Declares the extern "C" main function for the entry point. Returns None if the symbol /// already exists. - fn declare_c_main(&self, fn_type: Self::Type) -> Option; + fn declare_c_main(&self, fn_type: Self::FunctionSignature) -> Option; } diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 6d1ac717c0b8..f46d07ea5008 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -48,7 +48,7 @@ ArgAbiBuilderMethods, BaseTypeCodegenMethods, DerivedTypeCodegenMethods, LayoutTypeCodegenMethods, TypeCodegenMethods, TypeMembershipCodegenMethods, }; -pub use self::write::{ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods}; +pub use self::write::{ModuleBufferMethods, WriteBackendMethods}; pub trait CodegenObject = Copy + fmt::Debug; diff --git a/compiler/rustc_codegen_ssa/src/traits/statics.rs b/compiler/rustc_codegen_ssa/src/traits/statics.rs index 0e1e445c72f3..c72621302535 100644 --- a/compiler/rustc_codegen_ssa/src/traits/statics.rs +++ b/compiler/rustc_codegen_ssa/src/traits/statics.rs @@ -1,10 +1,10 @@ -use rustc_abi::Align; use rustc_hir::def_id::DefId; +use rustc_middle::mir::interpret::ConstAllocation; use super::BackendTypes; pub trait StaticCodegenMethods: BackendTypes { - fn static_addr_of(&self, cv: Self::Value, align: Align, kind: Option<&str>) -> Self::Value; + fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> Self::Value; fn codegen_static(&mut self, def_id: DefId); } diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 32c24965e1bf..45ea8384b2d4 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -23,7 +23,7 @@ pub trait BaseTypeCodegenMethods: BackendTypes { fn type_f128(&self) -> Self::Type; fn type_array(&self, ty: Self::Type, len: u64) -> Self::Type; - fn type_func(&self, args: &[Self::Type], ret: Self::Type) -> Self::Type; + fn type_func(&self, args: &[Self::Type], ret: Self::Type) -> Self::FunctionSignature; fn type_kind(&self, ty: Self::Type) -> TypeKind; fn type_ptr(&self) -> Self::Type; fn type_ptr_ext(&self, address_space: AddressSpace) -> Self::Type; @@ -114,7 +114,7 @@ pub trait LayoutTypeCodegenMethods<'tcx>: BackendTypes { /// such as when it's stack-allocated or when it's being loaded or stored. fn backend_type(&self, layout: TyAndLayout<'tcx>) -> Self::Type; fn cast_backend_type(&self, ty: &CastTarget) -> Self::Type; - fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Type; + fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::FunctionSignature; fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Type; fn reg_backend_type(&self, ty: &Reg) -> Self::Type; /// The backend type used for a rust type when it's in an SSA register. @@ -156,9 +156,6 @@ fn is_backend_ref(&self, layout: TyAndLayout<'tcx>) -> bool { pub trait TypeMembershipCodegenMethods<'tcx>: BackendTypes { fn add_type_metadata(&self, _function: Self::Function, _typeid: &[u8]) {} fn set_type_metadata(&self, _function: Self::Function, _typeid: &[u8]) {} - fn typeid_metadata(&self, _typeid: &[u8]) -> Option { - None - } fn add_kcfi_type_metadata(&self, _function: Self::Function, _typeid: u32) {} fn set_kcfi_type_metadata(&self, _function: Self::Function, _typeid: u32) {} } diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 5f5d0ac5d9fc..5d2313092fa8 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -1,8 +1,10 @@ +use std::any::Any; use std::path::PathBuf; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::DiagCtxtHandle; use rustc_middle::dep_graph::WorkProduct; +use rustc_session::{Session, config}; use crate::back::lto::{SerializedModule, ThinModule}; use crate::back::write::{ @@ -15,11 +17,19 @@ pub trait WriteBackendMethods: Clone + 'static { type TargetMachine; type ModuleBuffer: ModuleBufferMethods; type ThinData: Send + Sync; - type ThinBuffer: ThinBufferMethods; + fn thread_profiler() -> Box { + Box::new(()) + } + fn target_machine_factory( + &self, + sess: &Session, + opt_level: config::OptLevel, + target_features: &[String], + ) -> TargetMachineFactoryFn; /// Performs fat LTO by merging all modules into a single one, running autodiff /// if necessary and running any further optimizations - fn run_and_optimize_fat_lto( + fn optimize_and_codegen_fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, @@ -27,7 +37,7 @@ fn run_and_optimize_fat_lto( exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, - ) -> ModuleCodegen; + ) -> CompiledModule; /// Performs thin LTO by performing necessary global analysis and returning two /// lists, one of the modules that need optimization and another for modules that /// can simply be copied over from the incr. comp. cache. @@ -37,11 +47,9 @@ fn run_thin_lto( dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], - modules: Vec<(String, Self::ThinBuffer)>, + modules: Vec<(String, Self::ModuleBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec); - fn print_pass_timings(&self); - fn print_statistics(&self); fn optimize( cgcx: &CodegenContext, prof: &SelfProfilerRef, @@ -49,13 +57,13 @@ fn optimize( module: &mut ModuleCodegen, config: &ModuleConfig, ); - fn optimize_thin( + fn optimize_and_codegen_thin( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, tm_factory: TargetMachineFactoryFn, thin: ThinModule, - ) -> ModuleCodegen; + ) -> CompiledModule; fn codegen( cgcx: &CodegenContext, prof: &SelfProfilerRef, @@ -63,12 +71,7 @@ fn codegen( module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule; - fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer); - fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer); -} - -pub trait ThinBufferMethods: Send + Sync { - fn data(&self) -> &[u8]; + fn serialize_module(module: Self::Module, is_thin: bool) -> Self::ModuleBuffer; } pub trait ModuleBufferMethods: Send + Sync { diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 57396b657a3f..a45f1124f0af 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -1,15 +1,14 @@ //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations. use std::borrow::Cow; -use std::mem; use std::num::NonZero; use std::ops::Deref; +use std::{assert_matches, mem}; -use rustc_data_structures::assert_matches; use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::visit::Visitor; @@ -215,7 +214,7 @@ pub fn check_body(&mut self) { return; } - if !tcx.has_attr(def_id, sym::rustc_do_not_const_check) { + if !find_attr!(tcx, def_id, RustcDoNotConstCheck) { self.visit_body(body); } @@ -334,7 +333,7 @@ fn check_static(&mut self, def_id: DefId, span: Span) { self.tcx.dcx().span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`"); } if let Some(def_id) = def_id.as_local() - && let Err(guar) = self.tcx.ensure_ok().check_well_formed(hir::OwnerId { def_id }) + && let Err(guar) = self.tcx.ensure_result().check_well_formed(hir::OwnerId { def_id }) { self.error_emitted = Some(guar); } @@ -577,7 +576,7 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { Rvalue::Aggregate(kind, ..) => { if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref() - && let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id) + && let Some(coroutine_kind) = self.tcx.coroutine_kind(*def_id) { self.check_op(ops::Coroutine(coroutine_kind)); } @@ -645,8 +644,6 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { Rvalue::Cast(_, _, _) => {} - Rvalue::ShallowInitBox(_, _) => {} - Rvalue::UnaryOp(op, operand) => { let ty = operand.ty(self.body, self.tcx); match op { @@ -815,6 +812,10 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location }); } + if self.tcx.fn_sig(callee).skip_binder().c_variadic() { + self.check_op(ops::FnCallCVariadic) + } + // At this point, we are calling a function, `callee`, whose `DefId` is known... // `begin_panic` and `panic_display` functions accept generic @@ -845,13 +846,6 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location return; } - // This can be called on stable via the `vec!` macro. - if tcx.is_lang_item(callee, LangItem::ExchangeMalloc) { - self.check_op(ops::HeapAllocation); - // Allow this call, skip all the checks below. - return; - } - // Intrinsics are language primitives, not regular calls, so treat them separately. if let Some(intrinsic) = tcx.intrinsic(callee) { if !tcx.is_const_fn(callee) { diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index 1adba200caaf..3f4527a8750b 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -5,7 +5,6 @@ //! it finds operations that are invalid in a certain context. use rustc_errors::DiagCtxtHandle; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, find_attr}; use rustc_middle::ty::{self, PolyFnSig, TyCtxt}; @@ -81,9 +80,7 @@ pub fn rustc_allow_const_fn_unstable( def_id: LocalDefId, feature_gate: Symbol, ) -> bool { - let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); - - find_attr!(attrs, AttributeKind::RustcAllowConstFnUnstable(syms, _) if syms.contains(&feature_gate)) + find_attr!(tcx, def_id, RustcAllowConstFnUnstable(syms, _) if syms.contains(&feature_gate)) } /// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable". diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 5b62ba8c1605..d64cf8e03293 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -75,6 +75,27 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { } } +/// A c-variadic function call. +#[derive(Debug)] +pub(crate) struct FnCallCVariadic; +impl<'tcx> NonConstOp<'tcx> for FnCallCVariadic { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { + Status::Unstable { + gate: sym::const_c_variadic, + gate_already_checked: false, + safe_to_expose_on_stable: false, + is_function_call: true, + } + } + + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { + ccx.tcx.sess.create_feature_err( + errors::NonConstCVariadicCall { span, kind: ccx.const_kind() }, + sym::const_c_variadic, + ) + } +} + /// A call to a function that is in a trait, or has trait bounds that make it conditionally-const. #[derive(Debug)] pub(crate) struct ConditionallyConstCall<'tcx> { @@ -244,6 +265,7 @@ macro_rules! error { } } CallKind::FnCall { fn_trait_id, self_ty } => { + let kind = ccx.const_kind(); let note = match self_ty.kind() { FnDef(def_id, ..) => { let span = tcx.def_span(*def_id); @@ -253,8 +275,8 @@ macro_rules! error { Some(errors::NonConstClosureNote::FnDef { span }) } - FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr), - Closure(..) => Some(errors::NonConstClosureNote::Closure), + FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr { kind }), + Closure(..) => Some(errors::NonConstClosureNote::Closure { kind }), _ => None, }; @@ -540,18 +562,6 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { } } -#[derive(Debug)] -pub(crate) struct HeapAllocation; -impl<'tcx> NonConstOp<'tcx> for HeapAllocation { - fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { - ccx.dcx().create_err(errors::UnallowedHeapAllocations { - span, - kind: ccx.const_kind(), - teach: ccx.tcx.sess.teach(E0010), - }) - } -} - #[derive(Debug)] pub(crate) struct InlineAsm; impl<'tcx> NonConstOp<'tcx> for InlineAsm { 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 16e142a85ee6..34d4ce9f9f2c 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,3 +1,4 @@ +use rustc_hir::find_attr; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, BasicBlock, Location}; use rustc_middle::ty::TyCtxt; @@ -34,7 +35,7 @@ pub fn check_live_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>) { return; } - if tcx.has_attr(body.source.def_id(), sym::rustc_do_not_const_check) { + if find_attr!(tcx, body.source.def_id(), RustcDoNotConstCheck) { return; } diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 462254f064cf..1fbd0cd23405 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -237,8 +237,7 @@ pub fn in_rvalue<'tcx, Q, F>( Rvalue::Use(operand) | Rvalue::Repeat(operand, _) | Rvalue::UnaryOp(_, operand) - | Rvalue::Cast(_, operand, _) - | Rvalue::ShallowInitBox(operand, _) => in_operand::(cx, in_local, operand), + | Rvalue::Cast(_, operand, _) => in_operand::(cx, in_local, operand), Rvalue::BinaryOp(_, box (lhs, rhs)) => { in_operand::(cx, in_local, lhs) || in_operand::(cx, in_local, rhs) diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index d4cc21996aea..044b8b091b8d 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -192,7 +192,6 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { } mir::Rvalue::Cast(..) - | mir::Rvalue::ShallowInitBox(..) | mir::Rvalue::Use(..) | mir::Rvalue::CopyForDeref(..) | mir::Rvalue::ThreadLocalRef(..) diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 69aef3b047df..41d21bd74f98 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -110,7 +110,7 @@ pub fn get_span_and_frames<'tcx>( tcx: TyCtxtAt<'tcx>, stack: &[Frame<'tcx, impl Provenance, impl Sized>], ) -> (Span, Vec) { - let mut stacktrace = Frame::generate_stacktrace_from_stack(stack); + let mut stacktrace = Frame::generate_stacktrace_from_stack(stack, *tcx); // Filter out `requires_caller_location` frames. stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx)); let span = stacktrace.last().map(|f| f.span).unwrap_or(tcx.span); @@ -252,7 +252,7 @@ pub(super) fn lint<'tcx, L>( lint: &'static rustc_session::lint::Lint, decorator: impl FnOnce(Vec) -> L, ) where - L: for<'a> rustc_errors::LintDiagnostic<'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/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index d2b7e9a6b84f..2dbf2cc91058 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -34,12 +34,12 @@ fn setup_for_eval<'tcx>( cid.promoted.is_some() || matches!( ecx.tcx.def_kind(cid.instance.def_id()), - DefKind::Const + DefKind::Const { .. } | DefKind::Static { .. } | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst - | DefKind::AssocConst + | DefKind::AssocConst { .. } ), "Unexpected DefKind: {:?}", ecx.tcx.def_kind(cid.instance.def_id()) 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 46cdca53ba8c..ad4c9aa5ff9b 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -1,4 +1,3 @@ -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{ Constness, ExprKind, ForeignItemKind, ImplItem, ImplItemImplKind, ImplItemKind, Item, ItemKind, @@ -38,7 +37,7 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Constness { } } Node::TraitItem(ti @ TraitItem { kind: TraitItemKind::Fn(..), .. }) => { - if find_attr!(tcx.hir_attrs(ti.hir_id()), AttributeKind::RustcNonConstTraitMethod) { + if find_attr!(tcx.hir_attrs(ti.hir_id()), RustcNonConstTraitMethod) { Constness::NotConst } else { tcx.trait_def(tcx.local_parent(def_id)).constness diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index e017362b8c4b..e88123334dd5 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -2,18 +2,18 @@ use std::fmt; use std::hash::Hash; -use rustc_abi::{Align, Size}; +use rustc_abi::{Align, FIRST_VARIANT, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_errors::msg; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem}; +use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem, find_attr}; use rustc_middle::mir::AssertMessage; -use rustc_middle::mir::interpret::{Pointer, ReportedErrorInfo}; +use rustc_middle::mir::interpret::ReportedErrorInfo; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement}; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::{bug, mir}; +use rustc_middle::ty::{self, FieldInfo, Ty, TyCtxt}; +use rustc_middle::{bug, mir, span_bug}; use rustc_span::{Span, Symbol, sym}; use rustc_target::callconv::FnAbi; use tracing::debug; @@ -22,9 +22,10 @@ use crate::errors::{LongRunning, LongRunningWarn}; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, - GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, RangeSet, Scalar, - compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub, - throw_ub_custom, throw_unsup, throw_unsup_format, + GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar, + compile_time_machine, ensure_monomorphic_enough, err_inval, interp_ok, throw_exhaust, + throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, + type_implements_dyn_trait, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -235,7 +236,7 @@ fn hook_special_const_fn( if self.tcx.is_lang_item(def_id, LangItem::PanicDisplay) || self.tcx.is_lang_item(def_id, LangItem::BeginPanic) { - let args = self.copy_fn_args(args); + let args = Self::copy_fn_args(args); // &str or &&str assert!(args.len() == 1); @@ -440,7 +441,7 @@ fn find_mir_or_eval_fn( // sensitive check here. But we can at least rule out functions that are not const at // all. That said, we have to allow calling functions inside a `const trait`. These // *are* const-checked! - if !ecx.tcx.is_const_fn(def) || ecx.tcx.has_attr(def, sym::rustc_do_not_const_check) { + if !ecx.tcx.is_const_fn(def) || find_attr!(ecx.tcx, def, RustcDoNotConstCheck) { // We certainly do *not* want to actually call the fn // though, so be sure we return here. throw_unsup_format!("calling non-const function `{}`", instance) @@ -598,11 +599,48 @@ fn call_intrinsic( } } + sym::type_id_vtable => { + let tp_ty = ecx.read_type_id(&args[0])?; + let result_ty = ecx.read_type_id(&args[1])?; + + let (implements_trait, preds) = type_implements_dyn_trait(ecx, tp_ty, result_ty)?; + + if implements_trait { + let vtable_ptr = ecx.get_vtable_ptr(tp_ty, preds)?; + // Writing a non-null pointer into an `Option` will automatically make it `Some`. + ecx.write_pointer(vtable_ptr, dest)?; + } else { + // Write `None` + ecx.write_discriminant(FIRST_VARIANT, dest)?; + } + } + sym::type_of => { let ty = ecx.read_type_id(&args[0])?; ecx.write_type_info(ty, dest)?; } + sym::field_offset => { + let frt_ty = instance.args.type_at(0); + ensure_monomorphic_enough(ecx.tcx.tcx, frt_ty)?; + + let (ty, variant, field) = if let ty::Adt(def, args) = frt_ty.kind() + && let Some(FieldInfo { base, variant_idx, field_idx, .. }) = + def.field_representing_type_info(ecx.tcx.tcx, args) + { + (base, variant_idx, field_idx) + } else { + span_bug!(ecx.cur_span(), "expected field representing type, got {frt_ty}") + }; + let layout = ecx.layout_of(ty)?; + let cx = ty::layout::LayoutCx::new(ecx.tcx.tcx, ecx.typing_env()); + + let layout = layout.for_variant(&cx, variant); + let offset = layout.fields.offset(field.index()).bytes(); + + ecx.write_scalar(Scalar::from_target_usize(offset, ecx), dest)?; + } + _ => { // We haven't handled the intrinsic, let's see if we can use a fallback body. if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { 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 0fd70d784d4f..0ed04a5ab20b 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -2,12 +2,12 @@ use std::borrow::Cow; -use rustc_abi::{FieldIdx, VariantIdx}; +use rustc_abi::{ExternAbi, FieldIdx, VariantIdx}; use rustc_ast::Mutability; use rustc_hir::LangItem; use rustc_middle::span_bug; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{self, Const, ScalarInt, Ty}; +use rustc_middle::ty::{self, Const, FnHeader, FnSigTys, ScalarInt, Ty, TyCtxt}; use rustc_span::{Symbol, sym}; use crate::const_eval::CompileTimeMachine; @@ -188,10 +188,21 @@ pub(crate) fn write_type_info( self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?; variant } + ty::FnPtr(sig, fn_header) => { + let (variant, variant_place) = + self.downcast(&field_dest, sym::FnPtr)?; + let fn_ptr_place = + self.project_field(&variant_place, FieldIdx::ZERO)?; + + // FIXME: handle lifetime bounds + let sig = sig.skip_binder(); + + self.write_fn_ptr_type_info(fn_ptr_place, &sig, fn_header)?; + variant + } ty::Foreign(_) | ty::Pat(_, _) | ty::FnDef(..) - | ty::FnPtr(..) | ty::UnsafeBinder(..) | ty::Closure(..) | ty::CoroutineClosure(..) @@ -402,6 +413,65 @@ pub(crate) fn write_reference_type_info( interp_ok(()) } + pub(crate) fn write_fn_ptr_type_info( + &mut self, + place: impl Writeable<'tcx, CtfeProvenance>, + sig: &FnSigTys>, + fn_header: &FnHeader>, + ) -> InterpResult<'tcx> { + let FnHeader { safety, c_variadic, abi } = fn_header; + + 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::unsafety => { + self.write_scalar(Scalar::from_bool(safety.is_unsafe()), &field_place)?; + } + sym::abi => match abi { + ExternAbi::C { .. } => { + let (rust_variant, _rust_place) = + self.downcast(&field_place, sym::ExternC)?; + self.write_discriminant(rust_variant, &field_place)?; + } + ExternAbi::Rust => { + let (rust_variant, _rust_place) = + self.downcast(&field_place, sym::ExternRust)?; + self.write_discriminant(rust_variant, &field_place)?; + } + other_abi => { + let (variant, variant_place) = self.downcast(&field_place, sym::Named)?; + let str_place = self.allocate_str_dedup(other_abi.as_str())?; + let str_ref = self.mplace_to_ref(&str_place)?; + let payload = self.project_field(&variant_place, FieldIdx::ZERO)?; + self.write_immediate(*str_ref, &payload)?; + self.write_discriminant(variant, &field_place)?; + } + }, + sym::inputs => { + let inputs = sig.inputs(); + self.allocate_fill_and_write_slice_ptr( + field_place, + inputs.len() as _, + |this, i, place| this.write_type_id(inputs[i as usize], &place), + )?; + } + sym::output => { + let output = sig.output(); + self.write_type_id(output, &field_place)?; + } + sym::variadic => { + self.write_scalar(Scalar::from_bool(*c_variadic), &field_place)?; + } + other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), + } + } + + interp_ok(()) + } + pub(crate) fn write_pointer_type_info( &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 b771addb8df5..4323debd8014 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -413,7 +413,7 @@ fn valtree_into_mplace<'tcx>( Some(variant_idx), ) } - _ => (place.clone(), branches, None), + _ => (place.clone(), branches.as_slice(), None), }; debug!(?place_adjusted, ?branches); diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index b477c998a278..07d001e9d847 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -4,12 +4,13 @@ use either::Either; use rustc_abi::WrappingRange; use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ - Diag, DiagArgValue, DiagMessage, Diagnostic, EmissionGuarantee, Level, MultiSpan, - Subdiagnostic, msg, + Diag, DiagArgMap, DiagArgValue, DiagMessage, Diagnostic, EmissionGuarantee, Level, MultiSpan, + Subdiagnostic, format_diag_message, msg, }; use rustc_hir::ConstContext; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::mir::interpret::{ CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, InvalidProgramInfo, Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, @@ -289,31 +290,6 @@ pub(crate) struct UnallowedOpInConstContext { pub msg: String, } -#[derive(Diagnostic)] -#[diag(r#"allocations are not allowed in {$kind -> - [const] constant - [static] static - [const_fn] constant function - *[other] {""} -}s"#, code = E0010)] -pub(crate) struct UnallowedHeapAllocations { - #[primary_span] - #[label( - r#"allocation not allowed in {$kind -> - [const] constant - [static] static - [const_fn] constant function - *[other] {""} - }s"# - )] - pub span: Span, - pub kind: ConstContext, - #[note( - "the runtime heap is not yet available at compile-time, so no runtime heap allocations can be created" - )] - pub teach: bool, -} - #[derive(Diagnostic)] #[diag(r#"inline assembly is not allowed in {$kind -> [const] constant @@ -343,7 +319,7 @@ pub(crate) struct InteriorMutableBorrowEscaping { pub kind: ConstContext, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("constant evaluation is taking a long time")] #[note( "this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval. @@ -384,14 +360,11 @@ pub struct FrameNote { impl Subdiagnostic for FrameNote { fn add_to_diag(self, diag: &mut Diag<'_, G>) { - diag.arg("times", self.times); - diag.arg("where_", self.where_); - diag.arg("instance", self.instance); 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")); } - let msg = diag.eagerly_translate(msg!( + let msg = msg!( r#"{$times -> [0] inside {$where_ -> [closure] closure @@ -404,10 +377,11 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { *[other] {""} } ...] }"# - )); - diag.remove_arg("times"); - diag.remove_arg("where_"); - diag.remove_arg("instance"); + ) + .arg("times", self.times) + .arg("where_", self.where_) + .arg("instance", self.instance) + .format(); diag.span_note(span, msg); } } @@ -531,6 +505,19 @@ pub struct NonConstClosure { pub non_or_conditionally: &'static str, } +#[derive(Diagnostic)] +#[diag(r#"calling const c-variadic functions is unstable in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0015)] +pub struct NonConstCVariadicCall { + #[primary_span] + pub span: Span, + pub kind: ConstContext, +} + #[derive(Subdiagnostic)] pub enum NonConstClosureNote { #[note("function defined here, but it is not `const`")] @@ -546,7 +533,7 @@ pub enum NonConstClosureNote { *[other] {""} }s"# )] - FnPtr, + FnPtr { kind: ConstContext }, #[note( r#"closures need an RFC before allowed to be called in {$kind -> [const] constant @@ -555,7 +542,7 @@ pub enum NonConstClosureNote { *[other] {""} }s"# )] - Closure, + Closure { kind: ConstContext }, } #[derive(Subdiagnostic)] @@ -636,7 +623,7 @@ fn debug(self) -> String let mut diag = dcx.struct_allow(DiagMessage::Str(String::new().into())); let message = self.diagnostic_message(); self.add_args(&mut diag); - let s = dcx.eagerly_translate_to_string(message, diag.args.iter()); + let s = format_diag_message(&message, &diag.args).into_owned(); diag.cancel(); s }) @@ -757,11 +744,13 @@ fn diagnostic_message(&self) -> DiagMessage { WriteToReadOnly(_) => msg!("writing to {$allocation} which is read-only"), DerefFunctionPointer(_) => msg!("accessing {$allocation} which contains a function"), DerefVTablePointer(_) => msg!("accessing {$allocation} which contains a vtable"), + DerefVaListPointer(_) => msg!("accessing {$allocation} which contains a variable argument list"), DerefTypeIdPointer(_) => msg!("accessing {$allocation} which contains a `TypeId`"), InvalidBool(_) => msg!("interpreting an invalid 8-bit value as a bool: 0x{$value}"), InvalidChar(_) => msg!("interpreting an invalid 32-bit value as a char: 0x{$value}"), InvalidTag(_) => msg!("enum value has invalid tag: {$tag}"), InvalidFunctionPointer(_) => msg!("using {$pointer} as function pointer but it does not point to a function"), + InvalidVaListPointer(_) => msg!("using {$pointer} as variable argument list pointer but it does not point to a variable argument list"), InvalidVTablePointer(_) => msg!("using {$pointer} as vtable pointer but it does not point to a vtable"), InvalidVTableTrait { .. } => msg!("using vtable for `{$vtable_dyn_type}` but `{$expected_dyn_type}` was expected"), InvalidStr(_) => msg!("this string is not valid UTF-8: {$err}"), @@ -776,6 +765,9 @@ fn diagnostic_message(&self) -> DiagMessage { } AbiMismatchArgument { .. } => msg!("calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty}"), AbiMismatchReturn { .. } => msg!("calling a function with return type {$callee_ty} passing return place of type {$caller_ty}"), + VaArgOutOfBounds => "more C-variadic arguments read than were passed".into(), + CVariadicMismatch { ..} => "calling a function where the caller and callee disagree on whether the function is C-variadic".into(), + CVariadicFixedCountMismatch { .. } => msg!("calling a C-variadic function with {$caller} fixed arguments, but the function expects {$callee}"), } } @@ -800,6 +792,7 @@ fn add_args(self, diag: &mut Diag<'_, G>) { | InvalidMeta(InvalidMetaKind::TooBig) | InvalidUninitBytes(None) | DeadLocal + | VaArgOutOfBounds | UninhabitedEnumVariantWritten(_) | UninhabitedEnumVariantRead(_) => {} @@ -820,7 +813,10 @@ fn add_args(self, diag: &mut Diag<'_, G>) { diag.arg("len", len); diag.arg("index", index); } - UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => { + UnterminatedCString(ptr) + | InvalidFunctionPointer(ptr) + | InvalidVaListPointer(ptr) + | InvalidVTablePointer(ptr) => { diag.arg("pointer", ptr); } InvalidVTableTrait { expected_dyn_type, vtable_dyn_type } => { @@ -874,6 +870,7 @@ fn add_args(self, diag: &mut Diag<'_, G>) { WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) + | DerefVaListPointer(alloc) | DerefTypeIdPointer(alloc) => { diag.arg("allocation", alloc); } @@ -910,6 +907,14 @@ fn add_args(self, diag: &mut Diag<'_, G>) { diag.arg("caller_ty", caller_ty); diag.arg("callee_ty", callee_ty); } + CVariadicMismatch { caller_is_c_variadic, callee_is_c_variadic } => { + diag.arg("caller_is_c_variadic", caller_is_c_variadic); + diag.arg("callee_is_c_variadic", callee_is_c_variadic); + } + CVariadicFixedCountMismatch { caller, callee } => { + diag.arg("caller", caller); + diag.arg("callee", callee); + } } } } @@ -1080,12 +1085,12 @@ fn add_args(self, err: &mut Diag<'_, G>) { } let message = if let Some(path) = self.path { - err.dcx.eagerly_translate_to_string( - msg!("constructing invalid value at {$path}"), - [("path".into(), DiagArgValue::Str(path.into()))].iter().map(|(a, b)| (a, b)), + format_diag_message( + &msg!("constructing invalid value at {$path}"), + &DiagArgMap::from_iter([("path".into(), DiagArgValue::Str(path.into()))]), ) } else { - err.dcx.eagerly_translate_to_string(msg!("constructing invalid value"), [].into_iter()) + Cow::Borrowed("constructing invalid value") }; err.arg("front_matter", message); @@ -1111,12 +1116,13 @@ fn add_range_arg( msg!("in the range {$lo}..={$hi}") }; - let args = [ - ("lo".into(), DiagArgValue::Str(lo.to_string().into())), - ("hi".into(), DiagArgValue::Str(hi.to_string().into())), - ]; - let args = args.iter().map(|(a, b)| (a, b)); - let message = err.dcx.eagerly_translate_to_string(msg, args); + let message = format_diag_message( + &msg, + &DiagArgMap::from_iter([ + ("lo".into(), DiagArgValue::Str(lo.to_string().into())), + ("hi".into(), DiagArgValue::Str(hi.to_string().into())), + ]), + ); err.arg("in_range", message); } @@ -1126,19 +1132,18 @@ fn add_range_arg( } PointerAsInt { expected } | Uninit { expected } => { let msg = match expected { - ExpectedKind::Reference => msg!("expected a reference"), - ExpectedKind::Box => msg!("expected a box"), - ExpectedKind::RawPtr => msg!("expected a raw pointer"), - ExpectedKind::InitScalar => msg!("expected initialized scalar value"), - ExpectedKind::Bool => msg!("expected a boolean"), - ExpectedKind::Char => msg!("expected a unicode scalar value"), - ExpectedKind::Float => msg!("expected a floating point number"), - ExpectedKind::Int => msg!("expected an integer"), - ExpectedKind::FnPtr => msg!("expected a function pointer"), - ExpectedKind::EnumTag => msg!("expected a valid enum tag"), - ExpectedKind::Str => msg!("expected a string"), + ExpectedKind::Reference => "expected a reference", + ExpectedKind::Box => "expected a box", + ExpectedKind::RawPtr => "expected a raw pointer", + ExpectedKind::InitScalar => "expected initialized scalar value", + ExpectedKind::Bool => "expected a boolean", + ExpectedKind::Char => "expected a unicode scalar value", + ExpectedKind::Float => "expected a floating point number", + ExpectedKind::Int => "expected an integer", + ExpectedKind::FnPtr => "expected a function pointer", + ExpectedKind::EnumTag => "expected a valid enum tag", + ExpectedKind::Str => "expected a string", }; - let msg = err.dcx.eagerly_translate_to_string(msg, [].into_iter()); err.arg("expected", msg); } InvalidEnumTag { value } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index b17740f57f78..0de1fcd7c3a9 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -1,25 +1,25 @@ //! Manages calling a concrete function (with known MIR body) with argument passing, //! and returning the return value to the caller. +use std::assert_matches; use std::borrow::Cow; use either::{Left, Right}; use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx}; -use rustc_data_structures::assert_matches; use rustc_errors::msg; use rustc_hir::def_id::DefId; +use rustc_hir::find_attr; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef}; use rustc_middle::{bug, mir, span_bug}; -use rustc_span::sym; use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; use tracing::field::Empty; use tracing::{info, instrument, trace}; use super::{ CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, - Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo, interp_ok, - throw_ub, throw_ub_custom, throw_unsup_format, + Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, interp_ok, throw_ub, + throw_ub_custom, }; use crate::enter_trace_span; use crate::interpret::EnteredTraceSpan; @@ -42,25 +42,22 @@ pub fn layout(&self) -> &TyAndLayout<'tcx> { FnArg::InPlace(mplace) => &mplace.layout, } } -} -impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Make a copy of the given fn_arg. Any `InPlace` are degenerated to copies, no protection of the /// original memory occurs. - pub fn copy_fn_arg(&self, arg: &FnArg<'tcx, M::Provenance>) -> OpTy<'tcx, M::Provenance> { - match arg { + pub fn copy_fn_arg(&self) -> OpTy<'tcx, Prov> { + match self { FnArg::Copy(op) => op.clone(), FnArg::InPlace(mplace) => mplace.clone().into(), } } +} +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Make a copy of the given fn_args. Any `InPlace` are degenerated to copies, no protection of the /// original memory occurs. - pub fn copy_fn_args( - &self, - args: &[FnArg<'tcx, M::Provenance>], - ) -> Vec> { - args.iter().map(|fn_arg| self.copy_fn_arg(fn_arg)).collect() + pub fn copy_fn_args(args: &[FnArg<'tcx, M::Provenance>]) -> Vec> { + args.iter().map(|fn_arg| fn_arg.copy_fn_arg()).collect() } /// Helper function for argument untupling. @@ -144,9 +141,8 @@ fn unfold_npo(&self, layout: TyAndLayout<'tcx>) -> InterpResult<'tcx, TyAndLayou // Check if the inner type is one of the NPO-guaranteed ones. // For that we first unpeel transparent *structs* (but not unions). - let is_npo = |def: AdtDef<'tcx>| { - self.tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed) - }; + let is_npo = + |def: AdtDef<'tcx>| find_attr!(self.tcx, def.did(), RustcNonnullOptimizationGuaranteed); let inner = self.unfold_transparent(inner, /* may_unfold */ |def| { // Stop at NPO types so that we don't miss that attribute in the check below! def.is_struct() && !is_npo(def) @@ -315,7 +311,7 @@ fn pass_argument<'x, 'y>( // We work with a copy of the argument for now; if this is in-place argument passing, we // will later protect the source it comes from. This means the callee cannot observe if we // did in-place of by-copy argument passing, except for pointer equality tests. - let caller_arg_copy = self.copy_fn_arg(caller_arg); + let caller_arg_copy = caller_arg.copy_fn_arg(); if !already_live { let local = callee_arg.as_local().unwrap(); let meta = caller_arg_copy.meta(); @@ -354,13 +350,18 @@ pub fn init_stack_frame( ) -> InterpResult<'tcx> { let _trace = enter_trace_span!(M, step::init_stack_frame, %instance, tracing_separate_thread = Empty); - // Compute callee information. - // FIXME: for variadic support, do we have to somehow determine callee's extra_args? - let callee_fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; - - if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic { - throw_unsup_format!("calling a c-variadic function is not supported"); - } + // The first order of business is to figure out the callee signature. + // However, that requires the list of variadic arguments. + // We use the *caller* information to determine where to split the list of arguments, + // and then later check that the callee indeed has the same number of fixed arguments. + let extra_tys = if caller_fn_abi.c_variadic { + let fixed_count = usize::try_from(caller_fn_abi.fixed_count).unwrap(); + let extra_tys = args[fixed_count..].iter().map(|arg| arg.layout().ty); + self.tcx.mk_type_list_from_iter(extra_tys) + } else { + ty::List::empty() + }; + let callee_fn_abi = self.fn_abi_of_instance_no_deduced_attrs(instance, extra_tys)?; if caller_fn_abi.conv != callee_fn_abi.conv { throw_ub_custom!( @@ -372,6 +373,19 @@ pub fn init_stack_frame( ) } + if caller_fn_abi.c_variadic != callee_fn_abi.c_variadic { + throw_ub!(CVariadicMismatch { + caller_is_c_variadic: caller_fn_abi.c_variadic, + callee_is_c_variadic: callee_fn_abi.c_variadic, + }); + } + if caller_fn_abi.c_variadic && caller_fn_abi.fixed_count != callee_fn_abi.fixed_count { + throw_ub!(CVariadicFixedCountMismatch { + caller: caller_fn_abi.fixed_count, + callee: callee_fn_abi.fixed_count, + }); + } + // Check that all target features required by the callee (i.e., from // the attribute `#[target_feature(enable = ...)]`) are enabled at // compile time. @@ -444,6 +458,10 @@ pub fn init_stack_frame( // `pass_argument` would be the loop body. It takes care to // not advance `caller_iter` for ignored arguments. let mut callee_args_abis = callee_fn_abi.args.iter().enumerate(); + // Determine whether there is a special VaList argument. This is always the + // last argument, and since arguments start at index 1 that's `arg_count`. + let va_list_arg = + callee_fn_abi.c_variadic.then(|| mir::Local::from_usize(body.arg_count)); for local in body.args_iter() { // Construct the destination place for this argument. At this point all // locals are still dead, so we cannot construct a `PlaceTy`. @@ -452,7 +470,31 @@ pub fn init_stack_frame( // type, but the result gets cached so this avoids calling the instantiation // query *again* the next time this local is accessed. let ty = self.layout_of_local(self.frame(), local, None)?.ty; - if Some(local) == body.spread_arg { + if Some(local) == va_list_arg { + // This is the last callee-side argument of a variadic function. + // This argument is a VaList holding the remaining caller-side arguments. + self.storage_live(local)?; + + let place = self.eval_place(dest)?; + let mplace = self.force_allocation(&place)?; + + // Consume the remaining arguments by putting them into the variable argument + // list. + let varargs = self.allocate_varargs(&mut caller_args, &mut callee_args_abis)?; + // When the frame is dropped, these variable arguments are deallocated. + self.frame_mut().va_list = varargs.clone(); + let key = self.va_list_ptr(varargs.into()); + + // Zero the VaList, so it is fully initialized. + self.write_bytes_ptr( + mplace.ptr(), + (0..mplace.layout.size.bytes()).map(|_| 0u8), + )?; + + // Store the "key" pointer in the right field. + let key_mplace = self.va_list_key_field(&mplace)?; + self.write_pointer(key, &key_mplace)?; + } else if Some(local) == body.spread_arg { // Make the local live once, then fill in the value field by field. self.storage_live(local)?; // Must be a tuple @@ -491,7 +533,7 @@ pub fn init_stack_frame( if instance.def.requires_caller_location(*self.tcx) { callee_args_abis.next().unwrap(); } - // Now we should have no more caller args or callee arg ABIs + // Now we should have no more caller args or callee arg ABIs. assert!( callee_args_abis.next().is_none(), "mismatch between callee ABI and callee body arguments" @@ -566,7 +608,7 @@ pub(super) fn init_fn_call( if let Some(fallback) = M::call_intrinsic( self, instance, - &self.copy_fn_args(args), + &Self::copy_fn_args(args), destination, target, unwind, @@ -653,7 +695,7 @@ pub(super) fn init_fn_call( // An `InPlace` does nothing here, we keep the original receiver intact. We can't // really pass the argument in-place anyway, and we are constructing a new // `Immediate` receiver. - let mut receiver = self.copy_fn_arg(&args[0]); + let mut receiver = args[0].copy_fn_arg(); let receiver_place = loop { match receiver.layout.ty.kind() { ty::Ref(..) | ty::RawPtr(..) => { @@ -774,41 +816,50 @@ pub(super) fn init_fn_tail_call( with_caller_location: bool, ) -> InterpResult<'tcx> { trace!("init_fn_tail_call: {:#?}", fn_val); - // This is the "canonical" implementation of tails calls, // a pop of the current stack frame, followed by a normal call // which pushes a new stack frame, with the return address from // the popped stack frame. // - // Note that we are using `pop_stack_frame_raw` and not `return_from_current_stack_frame`, - // as the latter "executes" the goto to the return block, but we don't want to, + // Note that we cannot use `return_from_current_stack_frame`, + // as that "executes" the goto to the return block, but we don't want to, // only the tail called function should return to the current return block. - let StackPopInfo { return_action, return_cont, return_place } = - self.pop_stack_frame_raw(false, |_this, _return_place| { - // This function's return value is just discarded, the tail-callee will fill in the return place instead. - interp_ok(()) - })?; - assert_eq!(return_action, ReturnAction::Normal); - - // Take the "stack pop cleanup" info, and use that to initiate the next call. - let ReturnContinuation::Goto { ret, unwind } = return_cont else { - bug!("can't tailcall as root"); + // The arguments need to all be copied since the current stack frame will be removed + // before the callee even starts executing. + // FIXME(explicit_tail_calls,#144855): does this match what codegen does? + let args = args.iter().map(|fn_arg| FnArg::Copy(fn_arg.copy_fn_arg())).collect::>(); + // Remove the frame from the stack. + let frame = self.pop_stack_frame_raw()?; + // Remember where this frame would have returned to. + let ReturnContinuation::Goto { ret, unwind } = frame.return_cont() else { + bug!("can't tailcall as root of the stack"); }; - + // There's no return value to deal with! Instead, we forward the old return place + // to the new function. // FIXME(explicit_tail_calls): // we should check if both caller&callee can/n't unwind, // see + // Now push the new stack frame. self.init_fn_call( fn_val, (caller_abi, caller_fn_abi), - args, + &*args, with_caller_location, - &return_place, + frame.return_place(), ret, unwind, - ) + )?; + + // Finally, clear the local variables. Has to be done after pushing to support + // non-scalar arguments. + // FIXME(explicit_tail_calls,#144855): revisit this once codegen supports indirect + // arguments, to ensure the semantics are compatible. + let return_action = self.cleanup_stack_frame(/* unwinding */ false, frame)?; + assert_eq!(return_action, ReturnAction::Normal); + + interp_ok(()) } pub(super) fn init_drop_in_place_call( @@ -848,7 +899,7 @@ pub(super) fn init_drop_in_place_call( enter_trace_span!(M, resolve::resolve_drop_in_place, ty = ?place.layout.ty); ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty) }; - let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; + let fn_abi = self.fn_abi_of_instance_no_deduced_attrs(instance, ty::List::empty())?; let arg = self.mplace_to_ref(&place)?; let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?); @@ -903,14 +954,18 @@ pub(super) fn return_from_current_stack_frame( // local's value out. let return_op = self.local_to_op(mir::RETURN_PLACE, None).expect("return place should always be live"); - // Do the actual pop + copy. - let stack_pop_info = self.pop_stack_frame_raw(unwinding, |this, return_place| { - this.copy_op_allow_transmute(&return_op, return_place)?; - trace!("return value: {:?}", this.dump_place(return_place)); - interp_ok(()) - })?; - - match stack_pop_info.return_action { + // Remove the frame from the stack. + let frame = self.pop_stack_frame_raw()?; + // Copy the return value and remember the return continuation. + if !unwinding { + self.copy_op_allow_transmute(&return_op, frame.return_place())?; + trace!("return value: {:?}", self.dump_place(frame.return_place())); + } + let return_cont = frame.return_cont(); + // Finish popping the stack frame. + let return_action = self.cleanup_stack_frame(unwinding, frame)?; + // Jump to the next block. + match return_action { ReturnAction::Normal => {} ReturnAction::NoJump => { // The hook already did everything. @@ -928,7 +983,7 @@ pub(super) fn return_from_current_stack_frame( // Normal return, figure out where to jump. if unwinding { // Follow the unwind edge. - match stack_pop_info.return_cont { + match return_cont { ReturnContinuation::Goto { unwind, .. } => { // This must be the very last thing that happens, since it can in fact push a new stack frame. self.unwind_to_block(unwind) @@ -939,7 +994,7 @@ pub(super) fn return_from_current_stack_frame( } } else { // Follow the normal return edge. - match stack_pop_info.return_cont { + match return_cont { ReturnContinuation::Goto { ret, .. } => self.return_to_block(ret), ReturnContinuation::Stop { .. } => { assert!( diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 6ea83167157c..6972a79226f3 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -1,7 +1,8 @@ +use std::assert_matches; + use rustc_abi::{FieldIdx, Integer}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::{Float, FloatConvert}; -use rustc_data_structures::assert_matches; use rustc_errors::msg; use rustc_middle::mir::CastKind; use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index c2f772d56075..956be147d748 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1,7 +1,8 @@ +use std::debug_assert_matches; + use either::{Left, Right}; use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; -use rustc_data_structures::debug_assert_matches; -use rustc_errors::{DiagCtxtHandle, msg}; +use rustc_errors::{DiagCtxtHandle, format_diag_message, msg}; use rustc_hir::def_id::DefId; use rustc_hir::limit::Limit; use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; @@ -153,16 +154,16 @@ pub fn fn_abi_of_fn_ptr( } /// This inherent method takes priority over the trait method with the same name in FnAbiOf, - /// and allows wrapping the actual [FnAbiOf::fn_abi_of_instance] with a tracing span. - /// See [FnAbiOf::fn_abi_of_instance] for the original documentation. + /// and allows wrapping the actual [FnAbiOf::fn_abi_of_instance_no_deduced_attrs] with a tracing span. + /// See [FnAbiOf::fn_abi_of_instance_no_deduced_attrs] for the original documentation. #[inline(always)] - pub fn fn_abi_of_instance( + pub fn fn_abi_of_instance_no_deduced_attrs( &self, instance: ty::Instance<'tcx>, extra_args: &'tcx ty::List>, ) -> >::FnAbiOfResult { let _trace = enter_trace_span!(M, layouting::fn_abi_of_instance, ?instance, ?extra_args); - FnAbiOf::fn_abi_of_instance(self, instance, extra_args) + FnAbiOf::fn_abi_of_instance_no_deduced_attrs(self, instance, extra_args) } } @@ -235,9 +236,9 @@ pub fn format_interp_error<'tcx>(dcx: DiagCtxtHandle<'_>, e: InterpErrorInfo<'tc let mut diag = dcx.struct_allow(""); let msg = e.diagnostic_message(); e.add_args(&mut diag); - let s = dcx.eagerly_translate_to_string(msg, diag.args.iter()); + let msg = format_diag_message(&msg, &diag.args).into_owned(); diag.cancel(); - s + msg } impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { @@ -635,7 +636,7 @@ pub fn dump_place(&self, place: &PlaceTy<'tcx, M::Provenance>) -> PlacePrinter<' #[must_use] pub fn generate_stacktrace(&self) -> Vec> { - Frame::generate_stacktrace_from_stack(self.stack()) + Frame::generate_stacktrace_from_stack(self.stack(), *self.tcx) } pub fn adjust_nan(&self, f: F2, inputs: &[F1]) -> F2 diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index c9a3b578e793..c9106d691f7c 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -4,27 +4,25 @@ mod simd; -use rustc_abi::{FIRST_VARIANT, FieldIdx, HasDataLayout, Size, VariantIdx}; +use std::assert_matches; + +use rustc_abi::{FieldIdx, HasDataLayout, Size, VariantIdx}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_data_structures::assert_matches; use rustc_errors::msg; -use rustc_hir::def_id::CRATE_DEF_ID; -use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{FloatTy, PolyExistentialPredicate, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{FloatTy, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug, ty}; use rustc_span::{Symbol, sym}; -use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; use tracing::trace; use super::memory::MemoryKind; use super::util::ensure_monomorphic_enough; use super::{ AllocId, CheckInAllocMsg, ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Pointer, - PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format, interp_ok, throw_inval, - throw_ub_custom, throw_ub_format, + PointerArithmetic, Projectable, Provenance, Scalar, err_ub_custom, err_unsup_format, interp_ok, + throw_inval, throw_ub, throw_ub_custom, throw_ub_format, throw_unsup_format, }; use crate::interpret::Writeable; @@ -227,44 +225,6 @@ pub fn eval_intrinsic( self.write_scalar(Scalar::from_target_usize(offset, self), dest)?; } - sym::vtable_for => { - let tp_ty = instance.args.type_at(0); - let result_ty = instance.args.type_at(1); - - ensure_monomorphic_enough(tcx, tp_ty)?; - ensure_monomorphic_enough(tcx, result_ty)?; - let ty::Dynamic(preds, _) = result_ty.kind() else { - span_bug!( - self.find_closest_untracked_caller_location(), - "Invalid type provided to vtable_for::. U must be dyn Trait, got {result_ty}." - ); - }; - - let (infcx, param_env) = - self.tcx.infer_ctxt().build_with_typing_env(self.typing_env); - - let ocx = ObligationCtxt::new(&infcx); - ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| { - let pred = pred.with_self_ty(tcx, tp_ty); - // Lifetimes can only be 'static because of the bound on T - let pred = ty::fold_regions(tcx, pred, |r, _| { - if r == tcx.lifetimes.re_erased { tcx.lifetimes.re_static } else { r } - }); - Obligation::new(tcx, ObligationCause::dummy(), param_env, pred) - })); - let type_impls_trait = ocx.evaluate_obligations_error_on_ambiguity().is_empty(); - // Since `assumed_wf_tys=[]` the choice of LocalDefId is irrelevant, so using the "default" - let regions_are_valid = ocx.resolve_regions(CRATE_DEF_ID, param_env, []).is_empty(); - - if regions_are_valid && type_impls_trait { - let vtable_ptr = self.get_vtable_ptr(tp_ty, preds)?; - // Writing a non-null pointer into an `Option` will automatically make it `Some`. - self.write_pointer(vtable_ptr, dest)?; - } else { - // Write `None` - self.write_discriminant(FIRST_VARIANT, dest)?; - } - } sym::variant_count => { let tp_ty = instance.args.type_at(0); let ty = match tp_ty.kind() { @@ -750,6 +710,57 @@ pub fn eval_intrinsic( self.float_muladd_intrinsic::(args, dest, MulAddType::Nondeterministic)? } + sym::va_copy => { + let va_list = self.deref_pointer(&args[0])?; + let key_mplace = self.va_list_key_field(&va_list)?; + let key = self.read_pointer(&key_mplace)?; + + let varargs = self.get_ptr_va_list(key)?; + let copy_key = self.va_list_ptr(varargs.clone()); + + let copy_key_mplace = self.va_list_key_field(dest)?; + self.write_pointer(copy_key, ©_key_mplace)?; + } + + sym::va_end => { + let va_list = self.deref_pointer(&args[0])?; + let key_mplace = self.va_list_key_field(&va_list)?; + let key = self.read_pointer(&key_mplace)?; + + self.deallocate_va_list(key)?; + } + + sym::va_arg => { + let va_list = self.deref_pointer(&args[0])?; + let key_mplace = self.va_list_key_field(&va_list)?; + let key = self.read_pointer(&key_mplace)?; + + // Invalidate the old list and get its content. We'll recreate the + // new list (one element shorter) below. + let mut varargs = self.deallocate_va_list(key)?; + + let Some(arg_mplace) = varargs.pop_front() else { + throw_ub!(VaArgOutOfBounds); + }; + + // NOTE: In C some type conversions are allowed (e.g. casting between signed and + // unsigned integers). For now we require c-variadic arguments to be read with the + // exact type they were passed as. + if arg_mplace.layout.ty != dest.layout.ty { + throw_unsup_format!( + "va_arg type mismatch: requested `{}`, but next argument is `{}`", + dest.layout.ty, + arg_mplace.layout.ty + ); + } + // Copy the argument. + self.copy_op(&arg_mplace, dest)?; + + // Update the VaList pointer. + let new_key = self.va_list_ptr(varargs); + self.write_pointer(new_key, &key_mplace)?; + } + // Unsupported intrinsic: skip the return_to_block below. _ => return interp_ok(false), } @@ -1230,4 +1241,26 @@ fn float_to_int_inner<'tcx, F: rustc_apfloat::Float, M: Machine<'tcx>>( interp_ok(Some(ImmTy::from_scalar(val, cast_to))) } } + + /// Get the MPlace of the key from the place storing the VaList. + pub(super) fn va_list_key_field>( + &self, + va_list: &P, + ) -> InterpResult<'tcx, P> { + // The struct wrapped by VaList. + let va_list_inner = self.project_field(va_list, FieldIdx::ZERO)?; + + // Find the first pointer field in this struct. The exact index is target-specific. + let ty::Adt(adt, substs) = va_list_inner.layout().ty.kind() else { + bug!("invalid VaListImpl layout"); + }; + + for (i, field) in adt.non_enum_variant().fields.iter().enumerate() { + if field.ty(*self.tcx, substs).is_raw_ptr() { + return self.project_field(&va_list_inner, FieldIdx::from_usize(i)); + } + } + + bug!("no VaListImpl field is a pointer"); + } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs index 303183fd924d..dc6841bb89dd 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs @@ -2,7 +2,6 @@ use rustc_abi::{BackendRepr, Endian}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::{Float, Round}; -use rustc_data_structures::assert_matches; use rustc_middle::mir::interpret::{InterpErrorKind, Pointer, UndefinedBehaviorInfo}; use rustc_middle::ty::{FloatTy, ScalarInt, SimdAlign}; use rustc_middle::{bug, err_ub_format, mir, span_bug, throw_unsup_format, ty}; @@ -838,7 +837,20 @@ fn check_simd_ptr_alignment( vector_layout: TyAndLayout<'tcx>, alignment: SimdAlign, ) -> InterpResult<'tcx> { - assert_matches!(vector_layout.backend_repr, BackendRepr::SimdVector { .. }); + // Packed SIMD types with non-power-of-two element counts use BackendRepr::Memory + // instead of BackendRepr::SimdVector. We need to handle both cases. + // FIXME: remove the BackendRepr::Memory case when SIMD vectors are always passed as BackendRepr::SimdVector. + assert!(vector_layout.ty.is_simd(), "check_simd_ptr_alignment called on non-SIMD type"); + match vector_layout.backend_repr { + BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => {} + _ => { + span_bug!( + self.cur_span(), + "SIMD type has unexpected backend_repr: {:?}", + vector_layout.backend_repr + ); + } + } let align = match alignment { ty::SimdAlign::Unaligned => { diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 0049feee523c..8a864f372b9c 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -9,11 +9,10 @@ use std::borrow::{Borrow, Cow}; use std::cell::Cell; use std::collections::VecDeque; -use std::{fmt, ptr}; +use std::{assert_matches, fmt, ptr}; use rustc_abi::{Align, HasDataLayout, Size}; use rustc_ast::Mutability; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::msg; use rustc_middle::mir::display_allocation; @@ -23,8 +22,8 @@ use super::{ AllocBytes, AllocId, AllocInit, AllocMap, AllocRange, Allocation, CheckAlignMsg, - CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, - Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, + CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine, + MayLeak, Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; use crate::const_eval::ConstEvalErrKind; @@ -67,6 +66,8 @@ pub enum AllocKind { LiveData, /// A function allocation (that fn ptrs point to). Function, + /// A variable argument list allocation (used by c-variadic functions). + VaList, /// A vtable allocation. VTable, /// A TypeId allocation. @@ -126,6 +127,9 @@ pub struct Memory<'tcx, M: Machine<'tcx>> { /// Map for "extra" function pointers. extra_fn_ptr_map: FxIndexMap, + /// Map storing variable argument lists. + va_list_map: FxIndexMap>>, + /// To be able to compare pointers with null, and to check alignment for accesses /// to ZSTs (where pointers may dangle), we keep track of the size even for allocations /// that do not exist any more. @@ -161,6 +165,7 @@ pub fn new() -> Self { Memory { alloc_map: M::MemoryMap::default(), extra_fn_ptr_map: FxIndexMap::default(), + va_list_map: FxIndexMap::default(), dead_alloc_map: FxIndexMap::default(), validation_in_progress: Cell::new(false), } @@ -199,9 +204,11 @@ pub fn global_root_pointer( return M::extern_static_pointer(self, def_id); } None => { + let is_fn_ptr = self.memory.extra_fn_ptr_map.contains_key(&alloc_id); + let is_va_list = self.memory.va_list_map.contains_key(&alloc_id); assert!( - self.memory.extra_fn_ptr_map.contains_key(&alloc_id), - "{alloc_id:?} is neither global nor a function pointer" + is_fn_ptr || is_va_list, + "{alloc_id:?} is neither global, va_list nor a function pointer" ); } _ => {} @@ -229,6 +236,19 @@ pub fn fn_ptr(&mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>) -> Pointer>, + ) -> Pointer { + let id = self.tcx.reserve_alloc_id(); + let old = self.memory.va_list_map.insert(id, varargs); + assert!(old.is_none()); + // Variable argument lists are global allocations, so make sure we get the right root + // pointer. We know this is not an `extern static` so this cannot fail. + self.global_root_pointer(Pointer::from(id)).unwrap() + } + pub fn allocate_ptr( &mut self, size: Size, @@ -956,6 +976,7 @@ pub fn get_alloc_extra_mut<'a>( pub fn is_alloc_live(&self, id: AllocId) -> bool { self.memory.alloc_map.contains_key_ref(&id) || self.memory.extra_fn_ptr_map.contains_key(&id) + || self.memory.va_list_map.contains_key(&id) // We check `tcx` last as that has to acquire a lock in `many-seeds` mode. // This also matches the order in `get_alloc_info`. || self.tcx.try_get_global_alloc(id).is_some() @@ -995,6 +1016,11 @@ pub fn get_alloc_info(&self, id: AllocId) -> AllocInfo { return AllocInfo::new(Size::ZERO, align, AllocKind::Function, Mutability::Not); } + // # Variable argument lists + if self.memory.va_list_map.contains_key(&id) { + return AllocInfo::new(Size::ZERO, Align::ONE, AllocKind::VaList, Mutability::Not); + } + // # Global allocations if let Some(global_alloc) = self.tcx.try_get_global_alloc(id) { // NOTE: `static` alignment from attributes has already been applied to the allocation. @@ -1069,6 +1095,43 @@ pub fn get_ptr_fn( .into() } + pub fn get_ptr_va_list( + &self, + ptr: Pointer>, + ) -> InterpResult<'tcx, &VecDeque>> { + trace!("get_ptr_va_list({:?})", ptr); + let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?; + if offset.bytes() != 0 { + throw_ub!(InvalidVaListPointer(Pointer::new(alloc_id, offset))) + } + + let Some(va_list) = self.memory.va_list_map.get(&alloc_id) else { + throw_ub!(InvalidVaListPointer(Pointer::new(alloc_id, offset))) + }; + + interp_ok(va_list) + } + + /// Removes this VaList from the global map of variable argument lists. This does not deallocate + /// the VaList elements, that happens when the Frame is popped. + pub fn deallocate_va_list( + &mut self, + ptr: Pointer>, + ) -> InterpResult<'tcx, VecDeque>> { + trace!("deallocate_va_list({:?})", ptr); + let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?; + if offset.bytes() != 0 { + throw_ub!(InvalidVaListPointer(Pointer::new(alloc_id, offset))) + } + + let Some(va_list) = self.memory.va_list_map.swap_remove(&alloc_id) else { + throw_ub!(InvalidVaListPointer(Pointer::new(alloc_id, offset))) + }; + + self.memory.dead_alloc_map.insert(alloc_id, (Size::ZERO, Align::ONE)); + interp_ok(va_list) + } + /// Get the dynamic type of the given vtable pointer. /// If `expected_trait` is `Some`, it must be a vtable for the given trait. pub fn get_ptr_vtable_ty( diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 2f365ec77b33..c1d32e0f7bfb 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -36,8 +36,10 @@ pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable}; use self::place::{MemPlace, Place}; pub use self::projection::{OffsetMode, Projectable}; -pub use self::stack::{Frame, FrameInfo, LocalState, ReturnContinuation, StackPopInfo}; +pub use self::stack::{Frame, FrameInfo, LocalState, ReturnContinuation}; pub use self::util::EnteredTraceSpan; -pub(crate) use self::util::create_static_alloc; +pub(crate) use self::util::{ + create_static_alloc, ensure_monomorphic_enough, type_implements_dyn_trait, +}; pub use self::validity::{CtfeValidationMode, RangeSet, RefTracking}; pub use self::visitor::ValueVisitor; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index e8e77de8eb3e..6c9cd2e608ae 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -1,10 +1,11 @@ //! Functions concerning immediate values and operands, and reading from operands. //! All high-level functions to read from memory work on operands as sources. +use std::assert_matches; + use either::{Either, Left, Right}; use rustc_abi as abi; use rustc_abi::{BackendRepr, HasDataLayout, Size}; -use rustc_data_structures::assert_matches; use rustc_hir::def::Namespace; use rustc_middle::mir::interpret::ScalarSizeMismatch; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, TyAndLayout}; diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index fb07d5f0d0d6..8df284f0028a 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -2,9 +2,10 @@ //! into a place. //! All high-level functions to write to memory work on places as destinations. +use std::assert_matches; + use either::{Either, Left, Right}; use rustc_abi::{BackendRepr, HasDataLayout, Size}; -use rustc_data_structures::assert_matches; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty}; use rustc_middle::{bug, mir, span_bug}; diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 1c1c59da9d88..6da6ed2ec757 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -12,13 +12,14 @@ use rustc_middle::{bug, mir}; use rustc_mir_dataflow::impls::always_storage_live_locals; use rustc_span::Span; +use rustc_target::callconv::ArgAbi; use tracing::field::Empty; use tracing::{info_span, instrument, trace}; use super::{ - AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, Machine, MemPlace, MemPlaceMeta, - MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout, - interp_ok, throw_ub, throw_unsup, + AllocId, CtfeProvenance, FnArg, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, + MemPlaceMeta, MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, + from_known_layout, interp_ok, throw_ub, throw_unsup, }; use crate::{enter_trace_span, errors}; @@ -80,7 +81,7 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { /// and its layout in the caller. This place is to be interpreted relative to the /// *caller's* stack frame. We use a `PlaceTy` instead of an `MPlaceTy` since this /// avoids having to move *all* return places into Miri's memory. - pub return_place: PlaceTy<'tcx, Prov>, + return_place: PlaceTy<'tcx, Prov>, /// The list of locals for this stack frame, stored in order as /// `[return_ptr, arguments..., variables..., temporaries...]`. @@ -91,6 +92,10 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { /// Do *not* access this directly; always go through the machine hook! pub locals: IndexVec>, + /// The complete variable argument list of this frame. Its elements must be dropped when the + /// frame is popped. + pub(super) va_list: Vec>, + /// The span of the `tracing` crate is stored here. /// When the guard is dropped, the span is exited. This gives us /// a full stack trace on all tracing statements. @@ -122,19 +127,6 @@ pub enum ReturnContinuation { Stop { cleanup: bool }, } -/// Return type of [`InterpCx::pop_stack_frame_raw`]. -pub struct StackPopInfo<'tcx, Prov: Provenance> { - /// Additional information about the action to be performed when returning from the popped - /// stack frame. - pub return_action: ReturnAction, - - /// [`return_cont`](Frame::return_cont) of the popped stack frame. - pub return_cont: ReturnContinuation, - - /// [`return_place`](Frame::return_place) of the popped stack frame. - pub return_place: PlaceTy<'tcx, Prov>, -} - /// State of a local variable including a memoized layout #[derive(Clone)] pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> { @@ -259,6 +251,7 @@ pub fn with_extra(self, extra: Extra) -> Frame<'tcx, Prov, Extra> { return_cont: self.return_cont, return_place: self.return_place, locals: self.locals, + va_list: self.va_list, loc: self.loc, extra, tracing_span: self.tracing_span, @@ -286,6 +279,14 @@ pub fn instance(&self) -> ty::Instance<'tcx> { self.instance } + pub fn return_place(&self) -> &PlaceTy<'tcx, Prov> { + &self.return_place + } + + pub fn return_cont(&self) -> ReturnContinuation { + self.return_cont + } + /// Return the `SourceInfo` of the current instruction. pub fn current_source_info(&self) -> Option<&mir::SourceInfo> { self.loc.left().map(|loc| self.body.source_info(loc)) @@ -320,12 +321,15 @@ pub(super) fn locals_addr(&self) -> usize { } #[must_use] - pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec> { + pub fn generate_stacktrace_from_stack( + stack: &[Self], + tcx: TyCtxt<'tcx>, + ) -> Vec> { let mut frames = Vec::new(); // This deliberately does *not* honor `requires_caller_location` since it is used for much // more than just panics. for frame in stack.iter().rev() { - let span = match frame.loc { + let mut span = match frame.loc { Left(loc) => { // If the stacktrace passes through MIR-inlined source scopes, add them. let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc); @@ -339,6 +343,10 @@ pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec> { } Right(span) => span, }; + if span.is_dummy() { + // Some statements lack a proper span; point at the function instead. + span = tcx.def_span(frame.instance.def_id()); + } frames.push(FrameInfo { span, instance: frame.instance }); } trace!("generate stacktrace: {:#?}", frames); @@ -377,6 +385,7 @@ pub(crate) fn push_stack_frame_raw( return_cont, return_place: return_place.clone(), locals, + va_list: vec![], instance, tracing_span: SpanGuard::new(), extra: (), @@ -410,35 +419,26 @@ pub(crate) fn push_stack_frame_raw( interp_ok(()) } - /// Low-level helper that pops a stack frame from the stack and returns some information about - /// it. - /// - /// This also deallocates locals, if necessary. - /// `copy_ret_val` gets called after the frame has been taken from the stack but before the locals have been deallocated. - /// - /// [`M::before_stack_pop`] and [`M::after_stack_pop`] are called by this function - /// automatically. - /// - /// The high-level version of this is `return_from_current_stack_frame`. - /// - /// [`M::before_stack_pop`]: Machine::before_stack_pop - /// [`M::after_stack_pop`]: Machine::after_stack_pop + /// Low-level helper that pops a stack frame from the stack without any cleanup. + /// This invokes `before_stack_pop`. + /// After calling this function, you need to deal with the return value, and then + /// invoke `cleanup_stack_frame`. pub(super) fn pop_stack_frame_raw( &mut self, - unwinding: bool, - copy_ret_val: impl FnOnce(&mut Self, &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx>, - ) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> { + ) -> InterpResult<'tcx, Frame<'tcx, M::Provenance, M::FrameExtra>> { M::before_stack_pop(self)?; let frame = self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); + interp_ok(frame) + } - // Copy return value (unless we are unwinding). - if !unwinding { - copy_ret_val(self, &frame.return_place)?; - } - + /// Deallocate local variables in the stack frame, and invoke `after_stack_pop`. + pub(super) fn cleanup_stack_frame( + &mut self, + unwinding: bool, + frame: Frame<'tcx, M::Provenance, M::FrameExtra>, + ) -> InterpResult<'tcx, ReturnAction> { let return_cont = frame.return_cont; - let return_place = frame.return_place.clone(); // Cleanup: deallocate locals. // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. @@ -448,22 +448,22 @@ pub(super) fn pop_stack_frame_raw( ReturnContinuation::Stop { cleanup, .. } => cleanup, }; - let return_action = if cleanup { - // We need to take the locals out, since we need to mutate while iterating. + if cleanup { for local in &frame.locals { self.deallocate_local(local.value)?; } + // Deallocate any c-variadic arguments. + self.deallocate_varargs(&frame.va_list)?; + // Call the machine hook, which determines the next steps. let return_action = M::after_stack_pop(self, frame, unwinding)?; assert_ne!(return_action, ReturnAction::NoCleanup); - return_action + interp_ok(return_action) } else { // We also skip the machine hook when there's no cleanup. This not a real "pop" anyway. - ReturnAction::NoCleanup - }; - - interp_ok(StackPopInfo { return_action, return_cont, return_place }) + interp_ok(ReturnAction::NoCleanup) + } } /// In the current stack frame, mark all locals as live that are not arguments and don't have @@ -626,6 +626,58 @@ pub fn layout_of_local( } } +impl<'a, 'tcx: 'a, M: Machine<'tcx>> InterpCx<'tcx, M> { + /// Consume the arguments provided by the iterator and store them as a list + /// of variadic arguments. Return a list of the places that hold those arguments. + pub(crate) fn allocate_varargs( + &mut self, + caller_args: &mut I, + callee_abis: &mut J, + ) -> InterpResult<'tcx, Vec>> + where + I: Iterator, &'a ArgAbi<'tcx, Ty<'tcx>>)>, + J: Iterator>)>, + { + // Consume the remaining arguments and store them in fresh allocations. + let mut varargs = Vec::new(); + for (fn_arg, caller_abi) in caller_args { + // The callee ABI is entirely computed based on which arguments the caller has + // provided so it should not be possible to get a mismatch here. + let (_idx, callee_abi) = callee_abis.next().unwrap(); + assert!(self.check_argument_compat(caller_abi, callee_abi)?); + // FIXME: do we have to worry about in-place argument passing? + let op = fn_arg.copy_fn_arg(); + let mplace = self.allocate(op.layout, MemoryKind::Stack)?; + self.copy_op(&op, &mplace)?; + + varargs.push(mplace); + } + assert!(callee_abis.next().is_none()); + + interp_ok(varargs) + } + + /// Deallocate the variadic arguments in the list (that must have been created with `allocate_varargs`). + fn deallocate_varargs( + &mut self, + varargs: &[MPlaceTy<'tcx, M::Provenance>], + ) -> InterpResult<'tcx> { + for vararg in varargs { + let ptr = vararg.ptr(); + + trace!( + "deallocating vararg {:?}: {:?}", + vararg, + // Locals always have a `alloc_id` (they are never the result of a int2ptr). + self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap()) + ); + self.deallocate_ptr(ptr, None, MemoryKind::Stack)?; + } + + interp_ok(()) + } +} + impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { pub(super) fn print( &self, diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 4aa9030cfe61..083fd97aec4d 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -86,7 +86,7 @@ pub fn eval_statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'t span = ?stmt.source_info.span, tracing_separate_thread = Empty, ) - .or_if_tracing_disabled(|| info!(stmt = ?stmt.kind)); + .or_if_tracing_disabled(|| info!("{:?}", stmt.kind)); use rustc_middle::mir::StatementKind::*; @@ -249,12 +249,6 @@ pub fn eval_rvalue_into_place( self.write_immediate(*val, &dest)?; } - ShallowInitBox(ref operand, _) => { - let src = self.eval_operand(operand, None)?; - let v = self.read_immediate(&src)?; - self.write_immediate(*v, &dest)?; - } - Cast(cast_kind, ref operand, cast_ty) => { let src = self.eval_operand(operand, None)?; let cast_ty = @@ -476,7 +470,7 @@ fn eval_callee_and_args( let instance = self.resolve(def_id, args)?; ( FnVal::Instance(instance), - self.fn_abi_of_instance(instance, extra_args)?, + self.fn_abi_of_instance_no_deduced_attrs(instance, extra_args)?, instance.def.requires_caller_location(*self.tcx), ) } @@ -496,7 +490,7 @@ fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResul span = ?terminator.source_info.span, tracing_separate_thread = Empty, ) - .or_if_tracing_disabled(|| info!(terminator = ?terminator.kind)); + .or_if_tracing_disabled(|| info!("{:?}", terminator.kind)); use rustc_middle::mir::TerminatorKind::*; match terminator.kind { diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 1e18a22be81c..57a02769643b 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,12 +1,51 @@ -use rustc_hir::def_id::LocalDefId; -use rustc_middle::mir; +use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_middle::mir::interpret::{AllocInit, Allocation, GlobalAlloc, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::{PolyExistentialPredicate, Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_middle::{mir, span_bug, ty}; +use rustc_trait_selection::traits::ObligationCtxt; use tracing::debug; use super::{InterpCx, MPlaceTy, MemoryKind, interp_ok, throw_inval}; use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationResult}; +use crate::interpret::Machine; + +/// Checks if a type implements predicates. +/// Calls `ensure_monomorphic_enough` on `ty` and `trait_ty` for you. +pub(crate) fn type_implements_dyn_trait<'tcx, M: Machine<'tcx>>( + ecx: &mut InterpCx<'tcx, M>, + ty: Ty<'tcx>, + trait_ty: Ty<'tcx>, +) -> InterpResult<'tcx, (bool, &'tcx ty::List>)> { + ensure_monomorphic_enough(ecx.tcx.tcx, ty)?; + ensure_monomorphic_enough(ecx.tcx.tcx, trait_ty)?; + + let ty::Dynamic(preds, _) = trait_ty.kind() else { + span_bug!( + ecx.find_closest_untracked_caller_location(), + "Invalid type provided to type_implements_predicates. U must be dyn Trait, got {trait_ty}." + ); + }; + + let (infcx, param_env) = ecx.tcx.infer_ctxt().build_with_typing_env(ecx.typing_env); + + let ocx = ObligationCtxt::new(&infcx); + ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| { + let pred = pred.with_self_ty(ecx.tcx.tcx, ty); + // Lifetimes can only be 'static because of the bound on T + let pred = rustc_middle::ty::fold_regions(ecx.tcx.tcx, pred, |r, _| { + if r == ecx.tcx.tcx.lifetimes.re_erased { ecx.tcx.tcx.lifetimes.re_static } else { r } + }); + Obligation::new(ecx.tcx.tcx, ObligationCause::dummy(), param_env, pred) + })); + let type_impls_trait = ocx.evaluate_obligations_error_on_ambiguity().is_empty(); + // Since `assumed_wf_tys=[]` the choice of LocalDefId is irrelevant, so using the "default" + let regions_are_valid = ocx.resolve_regions(CRATE_DEF_ID, param_env, []).is_empty(); + + interp_ok((regions_are_valid && type_impls_trait, preds)) +} /// Checks whether a type contains generic parameters which must be instantiated. /// diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 5d8ae42f5ecc..2cf490350e90 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -647,13 +647,9 @@ fn check_safe_pointer( } } else { // This is not CTFE, so it's Miri with recursive checking. - // FIXME: we do *not* check behind boxes, since creating a new box first creates it uninitialized - // and then puts the value in there, so briefly we have a box with uninit contents. - // FIXME: should we also skip `UnsafeCell` behind shared references? Currently that is not - // needed since validation reads bypass Stacked Borrows and data race checks. - if matches!(ptr_kind, PointerKind::Box) { - return interp_ok(()); - } + // FIXME: should we skip `UnsafeCell` behind shared references? Currently that is + // not needed since validation reads bypass Stacked Borrows and data race checks, + // but is that really coherent? } let path = &self.path; ref_tracking.track(place, || { diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index ead1ee9bf85e..ade6f1c09475 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -1,9 +1,7 @@ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(array_try_map)] #![feature(box_patterns)] #![feature(decl_macro)] -#![feature(if_let_guard)] #![feature(never_type)] #![feature(slice_ptr_get)] #![feature(trait_alias)] diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 954e4116fb55..10abfd7a55ce 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -8,6 +8,7 @@ //! Typical examples would include: minimum element in SCC, maximum element //! reachable from it, etc. +use std::debug_assert_matches; use std::fmt::Debug; use std::marker::PhantomData; use std::ops::Range; @@ -15,7 +16,6 @@ use rustc_index::{Idx, IndexSlice, IndexVec}; use tracing::{debug, instrument, trace}; -use crate::debug_assert_matches; use crate::fx::FxHashSet; use crate::graph::vec_graph::VecGraph; use crate::graph::{DirectedGraph, NumEdges, Successors}; diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 594d6d294d75..d62705120958 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -10,15 +10,12 @@ #![allow(internal_features)] #![allow(rustc::default_hash_types)] #![allow(rustc::potential_query_instability)] -#![cfg_attr(bootstrap, feature(assert_matches))] -#![cfg_attr(bootstrap, feature(cold_path))] #![cfg_attr(test, feature(test))] #![deny(unsafe_op_in_unsafe_fn)] #![feature(allocator_api)] #![feature(ascii_char)] #![feature(ascii_char_variants)] #![feature(auto_traits)] -#![feature(cfg_select)] #![feature(const_default)] #![feature(const_trait_impl)] #![feature(dropck_eyepatch)] @@ -37,15 +34,10 @@ #![feature(unwrap_infallible)] // tidy-alphabetical-end -// Temporarily re-export `assert_matches!`, so that the rest of the compiler doesn't -// have to worry about it being moved to a different module in std during stabilization. -// FIXME(#151359): Remove this when `feature(assert_matches)` is stable in stage0. -// (This doesn't necessarily need to be fixed during the beta bump itself.) -#[cfg(bootstrap)] -pub use std::assert_matches::{assert_matches, debug_assert_matches}; +// This allows derive macros to reference this crate +extern crate self as rustc_data_structures; + use std::fmt; -#[cfg(not(bootstrap))] -pub use std::{assert_matches, debug_assert_matches}; pub use atomic_ref::AtomicRef; pub use ena::{snapshot_vec, undo_log, unify}; diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index 72d5f004194a..e2193a97a0f4 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -59,11 +59,16 @@ macro_rules! already_send { // These structures are already `Send`. already_send!( - [std::backtrace::Backtrace][std::io::Stdout][std::io::Stderr][std::io::Error][std::fs::File][std::panic::Location<'_>] - [rustc_arena::DroplessArena][jobserver_crate::Client][jobserver_crate::HelperThread] - [crate::memmap::Mmap][crate::profiling::SelfProfiler][crate::owned_slice::OwnedSlice] + [std::sync::atomic::AtomicBool][std::sync::atomic::AtomicUsize][std::sync::atomic::AtomicU8] + [std::sync::atomic::AtomicU32][std::backtrace::Backtrace][std::io::Stdout][std::io::Stderr] + [std::io::Error][std::fs::File][std::panic::Location<'_>][rustc_arena::DroplessArena] + [jobserver_crate::Client][jobserver_crate::HelperThread][crate::memmap::Mmap] + [crate::profiling::SelfProfiler][crate::owned_slice::OwnedSlice] ); +#[cfg(target_has_atomic = "64")] +already_send!([std::sync::atomic::AtomicU64]); + macro_rules! impl_dyn_send { ($($($attr: meta)* [$ty: ty where $($generics2: tt)*])*) => { $(unsafe impl<$($generics2)*> DynSend for $ty {})* diff --git a/compiler/rustc_data_structures/src/sorted_map/index_map.rs b/compiler/rustc_data_structures/src/sorted_map/index_map.rs index b38b09d60ebf..30485a289b6b 100644 --- a/compiler/rustc_data_structures/src/sorted_map/index_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map/index_map.rs @@ -3,8 +3,7 @@ use std::hash::{Hash, Hasher}; use rustc_index::{Idx, IndexVec}; - -use crate::stable_hasher::{HashStable, StableHasher}; +use rustc_macros::HashStable_NoContext; /// An indexed multi-map that preserves insertion order while permitting both *O*(log *n*) lookup of /// an item by key and *O*(1) lookup by index. @@ -24,11 +23,13 @@ /// in-place. /// /// [`SortedMap`]: super::SortedMap -#[derive(Clone, Debug)] +#[derive(Clone, Debug, HashStable_NoContext)] pub struct SortedIndexMultiMap { /// The elements of the map in insertion order. items: IndexVec, + // We can ignore this field because it is not observable from the outside. + #[stable_hasher(ignore)] /// Indices of the items in the set, sorted by the item's key. idx_sorted_by_item_key: Vec, } @@ -126,22 +127,6 @@ fn hash(&self, hasher: &mut H) { } } -impl HashStable for SortedIndexMultiMap -where - K: HashStable, - V: HashStable, -{ - fn hash_stable(&self, ctx: &mut C, hasher: &mut StableHasher) { - let SortedIndexMultiMap { - items, - // We can ignore this field because it is not observable from the outside. - idx_sorted_by_item_key: _, - } = self; - - items.hash_stable(ctx, hasher) - } -} - impl FromIterator<(K, V)> for SortedIndexMultiMap { fn from_iter(iter: J) -> Self where diff --git a/compiler/rustc_data_structures/src/svh.rs b/compiler/rustc_data_structures/src/svh.rs index f51fcb8ed22d..68b224676aec 100644 --- a/compiler/rustc_data_structures/src/svh.rs +++ b/compiler/rustc_data_structures/src/svh.rs @@ -7,12 +7,21 @@ use std::fmt; -use rustc_macros::{Decodable_NoContext, Encodable_NoContext}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use crate::fingerprint::Fingerprint; -use crate::stable_hasher; -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable_NoContext, Decodable_NoContext, Hash)] +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Debug, + Encodable_NoContext, + Decodable_NoContext, + Hash, + HashStable_NoContext +)] pub struct Svh { hash: Fingerprint, } @@ -39,11 +48,3 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad(&self.to_hex()) } } - -impl stable_hasher::HashStable for Svh { - #[inline] - fn hash_stable(&self, ctx: &mut T, hasher: &mut stable_hasher::StableHasher) { - let Svh { hash } = *self; - hash.hash_stable(ctx, hasher); - } -} diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs index 23f5cbc43452..2ab4a7f75b6b 100644 --- a/compiler/rustc_data_structures/src/sync/parallel.rs +++ b/compiler/rustc_data_structures/src/sync/parallel.rs @@ -133,7 +133,7 @@ fn par_slice( rustc_thread_pool::scope(|s| { let proof = items.derive(()); let group_size = std::cmp::max(items.len() / 128, 1); - for group in items.chunks_exact_mut(group_size) { + for group in items.chunks_mut(group_size) { let group = proof.derive(group); s.spawn(|_| { let mut group = group; diff --git a/compiler/rustc_data_structures/src/sync/vec.rs b/compiler/rustc_data_structures/src/sync/vec.rs index afbb0cef9f95..d35e2c61f1e1 100644 --- a/compiler/rustc_data_structures/src/sync/vec.rs +++ b/compiler/rustc_data_structures/src/sync/vec.rs @@ -46,20 +46,17 @@ pub fn get(&self, i: usize) -> Option { } pub fn iter_enumerated(&self) -> impl Iterator { - (0..) - .map(|i| (i, self.get(i))) - .take_while(|(_, o)| o.is_some()) - .filter_map(|(i, o)| Some((i, o?))) + (0..).map_while(|i| Some((i, self.get(i)?))) } pub fn iter(&self) -> impl Iterator { - (0..).map(|i| self.get(i)).take_while(|o| o.is_some()).flatten() + (0..).map_while(|i| self.get(i)) } } impl AppendOnlyVec { pub fn contains(&self, val: T) -> bool { - self.iter_enumerated().any(|(_, v)| v == val) + self.iter().any(|v| v == val) } } diff --git a/compiler/rustc_data_structures/src/vec_cache.rs b/compiler/rustc_data_structures/src/vec_cache.rs index f9f5871c8d61..aea5924b8802 100644 --- a/compiler/rustc_data_structures/src/vec_cache.rs +++ b/compiler/rustc_data_structures/src/vec_cache.rs @@ -341,7 +341,7 @@ pub fn complete(&self, key: K, value: V, index: I) { } } - pub fn iter(&self, f: &mut dyn FnMut(&K, &V, I)) { + pub fn for_each(&self, f: &mut dyn FnMut(&K, &V, I)) { for idx in 0..self.len.load(Ordering::Acquire) { let key = SlotIndex::from_index(idx as u32); match unsafe { key.get(&self.present) } { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index a235d3e0aecd..8fec629161ea 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -28,13 +28,12 @@ use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_codegen_ssa::{CodegenErrors, CodegenResults}; +use rustc_codegen_ssa::{CodegenErrors, CompiledModules}; use rustc_data_structures::profiling::{ TimePassesFormat, get_resident_set_size, print_time_passes_entry, }; pub use rustc_errors::catch_fatal_errors; use rustc_errors::emitter::stderr_destination; -use rustc_errors::translation::Translator; use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, PResult, markdown}; use rustc_feature::find_gated_cfg; // This avoids a false positive with `-Wunused_crate_dependencies`. @@ -107,10 +106,6 @@ pub(super) fn install() {} RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, UnstableFeatureUsage, }; -pub fn default_translator() -> Translator { - Translator::new() -} - /// Exit status code used for successful compilation and help output. pub const EXIT_SUCCESS: i32 = 0; @@ -343,7 +338,11 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) } } - Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)) + let linker = Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend); + + tcx.report_unused_features(); + + Some(linker) }); // Linking is done outside the `compiler.enter()` so that the @@ -561,9 +560,11 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { let rlink_data = fs::read(file).unwrap_or_else(|err| { dcx.emit_fatal(RlinkUnableToRead { err }); }); - let (codegen_results, metadata, outputs) = - match CodegenResults::deserialize_rlink(sess, rlink_data) { - Ok((codegen, metadata, outputs)) => (codegen, metadata, outputs), + let (compiled_modules, crate_info, metadata, outputs) = + match CompiledModules::deserialize_rlink(sess, rlink_data) { + Ok((codegen, crate_info, metadata, outputs)) => { + (codegen, crate_info, metadata, outputs) + } Err(err) => { match err { CodegenErrors::WrongFileType => dcx.emit_fatal(RLinkWrongFileType), @@ -588,7 +589,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { }; } }; - compiler.codegen_backend.link(sess, codegen_results, metadata, &outputs); + compiler.codegen_backend.link(sess, compiled_modules, crate_info, metadata, &outputs); } else { dcx.emit_fatal(RlinkNotAFile {}); } @@ -712,8 +713,9 @@ fn print_crate_info( }; let crate_name = passes::get_crate_name(sess, attrs); let lint_store = crate::unerased_lint_store(sess); - let registered_tools = rustc_resolve::registered_tools_ast(sess.dcx(), attrs); let features = rustc_expand::config::features(sess, attrs, crate_name); + let registered_tools = + rustc_resolve::registered_tools_ast(sess.dcx(), attrs, sess, &features); let lint_levels = rustc_lint::LintLevelsBuilder::crate_root( sess, &features, @@ -1525,11 +1527,9 @@ fn report_ice( extra_info: fn(&DiagCtxt), using_internal_features: &AtomicBool, ) { - let translator = Translator::new(); let emitter = Box::new(rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter::new( stderr_destination(rustc_errors::ColorConfig::Auto), - translator, )); let dcx = rustc_errors::DiagCtxt::new(emitter); let dcx = dcx.handle(); diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 962d035db862..79b6fc59978f 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -3,7 +3,9 @@ use std::cell::Cell; use std::fmt::Write; +use rustc_ast as ast; use rustc_ast_pretty::pprust as pprust_ast; +use rustc_hir_pretty as pprust_hir; use rustc_middle::bug; use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty}; use rustc_middle::ty::{self, TyCtxt}; @@ -13,7 +15,6 @@ use rustc_session::config::{OutFileName, PpHirMode, PpMode, PpSourceMode}; use rustc_span::{FileName, Ident}; use tracing::debug; -use {rustc_ast as ast, rustc_hir_pretty as pprust_hir}; pub use self::PpMode::*; pub use self::PpSourceMode::*; diff --git a/compiler/rustc_error_codes/src/error_codes/E0010.md b/compiler/rustc_error_codes/src/error_codes/E0010.md index f03f8a6605f6..3ede08ee8f62 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0010.md +++ b/compiler/rustc_error_codes/src/error_codes/E0010.md @@ -1,9 +1,11 @@ +#### Note: this error code is no longer emitted by the compiler. + The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on the heap at runtime, and therefore cannot be done at compile time. Erroneous code example: -```compile_fail,E0010 +```ignore (no longer emitted) const CON : Vec = vec![1, 2, 3]; ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0208.md b/compiler/rustc_error_codes/src/error_codes/E0208.md index 2b811b4b8500..f7bbeb293cae 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0208.md +++ b/compiler/rustc_error_codes/src/error_codes/E0208.md @@ -1,47 +1,2 @@ #### This error code is internal to the compiler and will not be emitted with normal Rust code. #### Note: this error code is no longer emitted by the compiler. - -This error code shows the variance of a type's generic parameters. - -Erroneous code example: - -```compile_fail -// NOTE: this feature is perma-unstable and should *only* be used for -// testing purposes. -#![allow(internal_features)] -#![feature(rustc_attrs)] - -#[rustc_variance] -struct Foo<'a, T> { // error: deliberate error to display type's variance - t: &'a mut T, -} -``` - -which produces the following error: - -```text -error: [-, o] - --> :4:1 - | -4 | struct Foo<'a, T> { - | ^^^^^^^^^^^^^^^^^ -``` - -*Note that while `#[rustc_variance]` still exists and is used within the* -*compiler, it no longer is marked as `E0208` and instead has no error code.* - -This error is deliberately triggered with the `#[rustc_variance]` attribute -(`#![feature(rustc_attrs)]` must be enabled) and helps to show you the variance -of the type's generic parameters. You can read more about variance and -subtyping in [this section of the Rustonomicon]. For a more in depth look at -variance (including a more complete list of common variances) see -[this section of the Reference]. For information on how variance is implemented -in the compiler, see [this section of `rustc-dev-guide`]. - -This error can be easily fixed by removing the `#[rustc_variance]` attribute, -the compiler's suggestion to comment it out can be applied automatically with -`rustfix`. - -[this section of the Rustonomicon]: https://doc.rust-lang.org/nomicon/subtyping.html -[this section of the Reference]: https://doc.rust-lang.org/reference/subtyping.html#variance -[this section of `rustc-dev-guide`]: https://rustc-dev-guide.rust-lang.org/variance.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0230.md b/compiler/rustc_error_codes/src/error_codes/E0230.md index c30a7e38e9c4..b6dcf4b97774 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0230.md +++ b/compiler/rustc_error_codes/src/error_codes/E0230.md @@ -1,26 +1,4 @@ -The `#[rustc_on_unimplemented]` attribute lets you specify a custom error -message for when a particular trait isn't implemented on a type placed in a -position that needs that trait. For example, when the following code is -compiled: +#### Note: this error code is no longer emitted by the compiler. -```compile_fail,E0230 -#![feature(rustc_attrs)] -#![allow(internal_features)] - -#[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{B}>`"] // error -trait BadAnnotation {} -``` - -There will be an error about `bool` not implementing `Index`, followed by a -note saying "the type `bool` cannot be indexed by `u8`". - -As you can see, you can specify type parameters in curly braces for -instantiation with the actual types (using the regular format string syntax) in -a given situation. Furthermore, `{Self}` will be instantiated to the type (in -this case, `bool`) that we tried to use. - -This error appears when the curly braces contain an identifier which doesn't -match with any of the type parameters or the string `Self`. This might happen -if you misspelled a type parameter, or if you intended to use literal curly -braces. If it is the latter, escape the curly braces with a second curly brace -of the same type; e.g., a literal `{` is `{{`. +The `#[rustc_on_unimplemented]` attribute used to raise this error for various +misuses of the attribute; these are now warnings. diff --git a/compiler/rustc_error_codes/src/error_codes/E0231.md b/compiler/rustc_error_codes/src/error_codes/E0231.md index b22e3c7082a8..5c644c044e41 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0231.md +++ b/compiler/rustc_error_codes/src/error_codes/E0231.md @@ -1,24 +1 @@ -The `#[rustc_on_unimplemented]` attribute lets you specify a custom error -message for when a particular trait isn't implemented on a type placed in a -position that needs that trait. For example, when the following code is -compiled: - -```compile_fail,E0231 -#![feature(rustc_attrs)] -#![allow(internal_features)] - -#[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{}>`"] // error! -trait BadAnnotation {} -``` - -there will be an error about `bool` not implementing `Index`, followed by a -note saying "the type `bool` cannot be indexed by `u8`". - -As you can see, you can specify type parameters in curly braces for -instantiation with the actual types (using the regular format string syntax) in -a given situation. Furthermore, `{Self}` will be instantiated to the type (in -this case, `bool`) that we tried to use. - -This error appears when the curly braces do not contain an identifier. Please -add one of the same name as a type parameter. If you intended to use literal -braces, use `{{` and `}}` to escape them. +#### Note: this error code is no longer emitted by the compiler diff --git a/compiler/rustc_error_codes/src/error_codes/E0430.md b/compiler/rustc_error_codes/src/error_codes/E0430.md index 8cca0f21e594..83b2bebca11b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0430.md +++ b/compiler/rustc_error_codes/src/error_codes/E0430.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + The `self` import appears more than once in the list. Erroneous code example: -```compile_fail,E0430 +```ignore (error is no longer emitted) use something::{self, self}; // error: `self` import can only appear once in // the list ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0431.md b/compiler/rustc_error_codes/src/error_codes/E0431.md index 1b70f5f1d7b7..6689ed0ee4a5 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0431.md +++ b/compiler/rustc_error_codes/src/error_codes/E0431.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + An invalid `self` import was made. Erroneous code example: -```compile_fail,E0431 +```ignore (error is no longer emitted) use {self}; // error: `self` import can only appear in an import list with a // non-empty prefix ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0556.md b/compiler/rustc_error_codes/src/error_codes/E0556.md index 2aac8240d293..d1eeddc3ab10 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0556.md +++ b/compiler/rustc_error_codes/src/error_codes/E0556.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + The `feature` attribute was badly formed. Erroneous code example: -```compile_fail,E0556 +```compile_fail #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)] // error! #![feature] // error! #![feature = "foo"] // error! diff --git a/compiler/rustc_error_codes/src/error_codes/E0570.md b/compiler/rustc_error_codes/src/error_codes/E0570.md index 355e71ffb432..e8a7200c1d05 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0570.md +++ b/compiler/rustc_error_codes/src/error_codes/E0570.md @@ -1,7 +1,7 @@ The requested ABI is unsupported by the current target. -The rust compiler maintains for each target a list of unsupported ABIs on -that target. If an ABI is present in such a list this usually means that the +The Rust compiler maintains a list of unsupported ABIs for each target. +If an ABI is present in such a list, this usually means that the target / ABI combination is currently unsupported by llvm. If necessary, you can circumvent this check using custom target specifications. diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 65f1780885ad..eff9b9d323c2 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -20,7 +20,8 @@ // // Do *not* remove entries from this list. Instead, just add a note to the corresponding markdown // file saying that this error is not emitted by the compiler any more (see E0001.md for an -// example), and remove all code examples that do not build any more. +// example), and remove all code examples that do not build any more by marking them +// with `ignore (no longer emitted)`. #[macro_export] #[rustfmt::skip] macro_rules! error_codes { @@ -333,7 +334,7 @@ macro_rules! error_codes { 0551, 0552, 0554, -0556, +0556, // REMOVED: merged with other attribute error codes 0557, 0559, 0560, diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml index db22e065907e..f280aaff1126 100644 --- a/compiler/rustc_error_messages/Cargo.toml +++ b/compiler/rustc_error_messages/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start fluent-bundle = "0.16" -fluent-syntax = "0.12" icu_list = { version = "2.0", default-features = false, features = ["alloc"] } icu_locale = { version = "2.0", default-features = false } intl-memoizer = "0.5.1" @@ -17,6 +16,5 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } -tracing = "0.1" unic-langid = { version = "0.9.0", features = ["macros"] } # tidy-alphabetical-end diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 0b30102eb992..a2c026706ada 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -4,193 +4,16 @@ // tidy-alphabetical-end use std::borrow::Cow; -use std::error::Error; -use std::path::Path; -use std::sync::{Arc, LazyLock}; -use std::{fmt, fs, io}; -use fluent_bundle::FluentResource; pub use fluent_bundle::types::FluentType; pub use fluent_bundle::{self, FluentArgs, FluentError, FluentValue}; -use fluent_syntax::parser::ParserError; -use intl_memoizer::concurrent::IntlLangMemoizer; -use rustc_data_structures::sync::{DynSend, IntoDynSyncSend}; use rustc_macros::{Decodable, Encodable}; use rustc_span::Span; -use tracing::{instrument, trace}; pub use unic_langid::{LanguageIdentifier, langid}; mod diagnostic_impls; pub use diagnostic_impls::DiagArgFromDisplay; - -pub type FluentBundle = - IntoDynSyncSend>; - -fn new_bundle(locales: Vec) -> FluentBundle { - IntoDynSyncSend(fluent_bundle::bundle::FluentBundle::new_concurrent(locales)) -} - -#[derive(Debug)] -pub enum TranslationBundleError { - /// Failed to read from `.ftl` file. - ReadFtl(io::Error), - /// Failed to parse contents of `.ftl` file. - ParseFtl(ParserError), - /// Failed to add `FluentResource` to `FluentBundle`. - AddResource(FluentError), - /// `$sysroot/share/locale/$locale` does not exist. - MissingLocale, - /// Cannot read directory entries of `$sysroot/share/locale/$locale`. - ReadLocalesDir(io::Error), - /// Cannot read directory entry of `$sysroot/share/locale/$locale`. - ReadLocalesDirEntry(io::Error), - /// `$sysroot/share/locale/$locale` is not a directory. - LocaleIsNotDir, -} - -impl fmt::Display for TranslationBundleError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TranslationBundleError::ReadFtl(e) => write!(f, "could not read ftl file: {e}"), - TranslationBundleError::ParseFtl(e) => { - write!(f, "could not parse ftl file: {e}") - } - TranslationBundleError::AddResource(e) => write!(f, "failed to add resource: {e}"), - TranslationBundleError::MissingLocale => write!(f, "missing locale directory"), - TranslationBundleError::ReadLocalesDir(e) => { - write!(f, "could not read locales dir: {e}") - } - TranslationBundleError::ReadLocalesDirEntry(e) => { - write!(f, "could not read locales dir entry: {e}") - } - TranslationBundleError::LocaleIsNotDir => { - write!(f, "`$sysroot/share/locales/$locale` is not a directory") - } - } - } -} - -impl Error for TranslationBundleError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - TranslationBundleError::ReadFtl(e) => Some(e), - TranslationBundleError::ParseFtl(e) => Some(e), - TranslationBundleError::AddResource(e) => Some(e), - TranslationBundleError::MissingLocale => None, - TranslationBundleError::ReadLocalesDir(e) => Some(e), - TranslationBundleError::ReadLocalesDirEntry(e) => Some(e), - TranslationBundleError::LocaleIsNotDir => None, - } - } -} - -impl From<(FluentResource, Vec)> for TranslationBundleError { - fn from((_, mut errs): (FluentResource, Vec)) -> Self { - TranslationBundleError::ParseFtl(errs.pop().expect("failed ftl parse with no errors")) - } -} - -impl From> for TranslationBundleError { - fn from(mut errs: Vec) -> Self { - TranslationBundleError::AddResource( - errs.pop().expect("failed adding resource to bundle with no errors"), - ) - } -} - -/// Returns Fluent bundle with the user's locale resources from -/// `$sysroot/share/locale/$requested_locale/*.ftl`. -/// -/// If `-Z additional-ftl-path` was provided, load that resource and add it to the bundle -/// (overriding any conflicting messages). -#[instrument(level = "trace")] -pub fn fluent_bundle( - sysroot_candidates: &[&Path], - requested_locale: Option, - additional_ftl_path: Option<&Path>, - with_directionality_markers: bool, -) -> Result>, TranslationBundleError> { - if requested_locale.is_none() && additional_ftl_path.is_none() { - return Ok(None); - } - - let fallback_locale = langid!("en-US"); - let requested_fallback_locale = requested_locale.as_ref() == Some(&fallback_locale); - trace!(?requested_fallback_locale); - if requested_fallback_locale && additional_ftl_path.is_none() { - return Ok(None); - } - // If there is only `-Z additional-ftl-path`, assume locale is "en-US", otherwise use user - // provided locale. - let locale = requested_locale.clone().unwrap_or(fallback_locale); - trace!(?locale); - let mut bundle = new_bundle(vec![locale]); - - // Add convenience functions available to ftl authors. - register_functions(&mut bundle); - - // Fluent diagnostics can insert directionality isolation markers around interpolated variables - // indicating that there may be a shift from right-to-left to left-to-right text (or - // vice-versa). These are disabled because they are sometimes visible in the error output, but - // may be worth investigating in future (for example: if type names are left-to-right and the - // surrounding diagnostic messages are right-to-left, then these might be helpful). - bundle.set_use_isolating(with_directionality_markers); - - // If the user requests the default locale then don't try to load anything. - if let Some(requested_locale) = requested_locale { - let mut found_resources = false; - for sysroot in sysroot_candidates { - let mut sysroot = sysroot.to_path_buf(); - sysroot.push("share"); - sysroot.push("locale"); - sysroot.push(requested_locale.to_string()); - trace!(?sysroot); - - if !sysroot.exists() { - trace!("skipping"); - continue; - } - - if !sysroot.is_dir() { - return Err(TranslationBundleError::LocaleIsNotDir); - } - - for entry in sysroot.read_dir().map_err(TranslationBundleError::ReadLocalesDir)? { - let entry = entry.map_err(TranslationBundleError::ReadLocalesDirEntry)?; - let path = entry.path(); - trace!(?path); - if path.extension().and_then(|s| s.to_str()) != Some("ftl") { - trace!("skipping"); - continue; - } - - let resource_str = - fs::read_to_string(path).map_err(TranslationBundleError::ReadFtl)?; - let resource = - FluentResource::try_new(resource_str).map_err(TranslationBundleError::from)?; - trace!(?resource); - bundle.add_resource(resource).map_err(TranslationBundleError::from)?; - found_resources = true; - } - } - - if !found_resources { - return Err(TranslationBundleError::MissingLocale); - } - } - - if let Some(additional_ftl_path) = additional_ftl_path { - let resource_str = - fs::read_to_string(additional_ftl_path).map_err(TranslationBundleError::ReadFtl)?; - let resource = - FluentResource::try_new(resource_str).map_err(TranslationBundleError::from)?; - trace!(?resource); - bundle.add_resource_overriding(resource); - } - - let bundle = Arc::new(bundle); - Ok(Some(bundle)) -} +use rustc_data_structures::fx::FxIndexMap; pub fn register_functions(bundle: &mut fluent_bundle::bundle::FluentBundle) { bundle @@ -201,35 +24,6 @@ pub fn register_functions(bundle: &mut fluent_bundle::bundle::FluentBundle .expect("Failed to add a function to the bundle."); } -/// Type alias for the result of `fallback_fluent_bundle` - a reference-counted pointer to a lazily -/// evaluated fluent bundle. -pub type LazyFallbackBundle = - Arc FluentBundle + DynSend>>>; - -/// Return the default `FluentBundle` with standard "en-US" diagnostic messages. -#[instrument(level = "trace", skip(resources))] -pub fn fallback_fluent_bundle( - resources: Vec<&'static str>, - with_directionality_markers: bool, -) -> LazyFallbackBundle { - Arc::new(LazyLock::new(Box::new(move || { - let mut fallback_bundle = new_bundle(vec![langid!("en-US")]); - - register_functions(&mut fallback_bundle); - - // See comment in `fluent_bundle`. - fallback_bundle.set_use_isolating(with_directionality_markers); - - for resource in resources { - let resource = FluentResource::try_new(resource.to_string()) - .expect("failed to parse fallback fluent resource"); - fallback_bundle.add_resource_overriding(resource); - } - - fallback_bundle - }))) -} - /// Abstraction over a message in a diagnostic to support both translatable and non-translatable /// diagnostic messages. /// @@ -316,10 +110,18 @@ pub fn from_spans(mut vec: Vec) -> MultiSpan { MultiSpan { primary_spans: vec, span_labels: vec![] } } + pub fn push_primary_span(&mut self, primary_span: Span) { + self.primary_spans.push(primary_span); + } + pub fn push_span_label(&mut self, span: Span, label: impl Into) { self.span_labels.push((span, label.into())); } + pub fn push_span_diag(&mut self, span: Span, diag: DiagMessage) { + self.span_labels.push((span, diag)); + } + /// Selects the first primary span (if any). pub fn primary_span(&self) -> Option { self.primary_spans.first().cloned() @@ -386,6 +188,11 @@ pub fn span_labels(&self) -> Vec { span_labels } + /// Returns the span labels as contained by `MultiSpan`. + pub fn span_labels_raw(&self) -> &[(Span, DiagMessage)] { + &self.span_labels + } + /// Returns `true` if any of the span labels is displayable. pub fn has_span_labels(&self) -> bool { self.span_labels.iter().any(|(sp, _)| !sp.is_dummy()) @@ -504,6 +311,10 @@ pub enum DiagArgValue { StrListSepByAnd(Vec>), } +/// A mapping from diagnostic argument names to their values. +/// This contains all the arguments necessary to format a diagnostic message. +pub type DiagArgMap = FxIndexMap; + /// Converts a value of a type into a `DiagArg` (typically a field of an `Diag` struct). /// Implemented as a custom trait rather than `From` so that it is implemented on the type being /// converted rather than on `DiagArgValue`, which enables types from other `rustc_*` crates to diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index bdd3266adb66..c3c9f26c3157 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -6,7 +6,6 @@ //! [annotate_snippets]: https://docs.rs/crate/annotate-snippets/ use std::borrow::Cow; -use std::error::Report; use std::fmt::Debug; use std::io; use std::io::Write; @@ -17,7 +16,7 @@ use anstream::ColorChoice; use derive_setters::Setters; use rustc_data_structures::sync::IntoDynSyncSend; -use rustc_error_messages::{FluentArgs, SpanLabel}; +use rustc_error_messages::{DiagArgMap, SpanLabel}; use rustc_lint_defs::pluralize; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, FileName, Pos, SourceFile, Span}; @@ -27,7 +26,7 @@ ConfusionType, Destination, MAX_SUGGESTIONS, OutputTheme, detect_confusion_type, is_different, normalize_whitespace, should_show_source_code, }; -use crate::translation::{Translator, to_fluent_args}; +use crate::formatting::{format_diag_message, format_diag_messages}; use crate::{ CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, Level, MultiSpan, Style, Subdiag, SuggestionStyle, TerminalUrl, @@ -39,8 +38,6 @@ pub struct AnnotateSnippetEmitter { #[setters(skip)] dst: IntoDynSyncSend, sm: Option>, - #[setters(skip)] - translator: Translator, short_message: bool, ui_testing: bool, ignored_directories_in_source_blocks: Vec, @@ -73,14 +70,12 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { impl Emitter for AnnotateSnippetEmitter { /// The entry point for the diagnostics generation fn emit_diagnostic(&mut self, mut diag: DiagInner) { - let fluent_args = to_fluent_args(diag.args.iter()); - if self.track_diagnostics && diag.span.has_primary_spans() && !diag.span.is_dummy() { diag.children.insert(0, diag.emitted_at_sub_diag()); } let mut suggestions = diag.suggestions.unwrap_tag(); - self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args); + self.primary_span_formatted(&mut diag.span, &mut suggestions, &diag.args); self.fix_multispans_in_extern_macros_and_render_macro_backtrace( &mut diag.span, @@ -92,7 +87,7 @@ fn emit_diagnostic(&mut self, mut diag: DiagInner) { self.emit_messages_default( &diag.level, &diag.messages, - &fluent_args, + &diag.args, &diag.code, &diag.span, &diag.children, @@ -108,10 +103,6 @@ fn should_show_explain(&self) -> bool { !self.short_message } - fn translator(&self) -> &Translator { - &self.translator - } - fn supports_color(&self) -> bool { false } @@ -133,11 +124,10 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::level::Level<' } impl AnnotateSnippetEmitter { - pub fn new(dst: Destination, translator: Translator) -> Self { + pub fn new(dst: Destination) -> Self { Self { dst: IntoDynSyncSend(dst), sm: None, - translator, short_message: false, ui_testing: false, ignored_directories_in_source_blocks: Vec::new(), @@ -153,7 +143,7 @@ fn emit_messages_default( &mut self, level: &Level, msgs: &[(DiagMessage, Style)], - args: &FluentArgs<'_>, + args: &DiagArgMap, code: &Option, msp: &MultiSpan, children: &[Subdiag], @@ -169,7 +159,7 @@ fn emit_messages_default( .clone() .secondary_title(Cow::Owned(self.pre_style_msgs(msgs, *level, args))) } else { - annotation_level.clone().primary_title(self.translator.translate_messages(msgs, args)) + annotation_level.clone().primary_title(format_diag_messages(msgs, args)) }; if let Some(c) = code { @@ -185,7 +175,7 @@ fn emit_messages_default( // If we don't have span information, emit and exit let Some(sm) = self.sm.as_ref() else { group = group.elements(children.iter().map(|c| { - let msg = self.translator.translate_messages(&c.messages, args).to_string(); + let msg = format_diag_messages(&c.messages, args).to_string(); let level = annotation_level_for_level(c.level); level.message(msg) })); @@ -202,7 +192,7 @@ fn emit_messages_default( return; }; - let mut file_ann = collect_annotations(args, msp, sm, &self.translator); + let mut file_ann = collect_annotations(args, msp, sm); // Make sure our primary file comes first let primary_span = msp.primary_span().unwrap_or_default(); @@ -256,7 +246,7 @@ fn emit_messages_default( let msg = if c.messages.iter().any(|(_, style)| style != &crate::Style::NoStyle) { Cow::Owned(self.pre_style_msgs(&c.messages, c.level, args)) } else { - self.translator.translate_messages(&c.messages, args) + format_diag_messages(&c.messages, args) }; // This is a secondary message with no span info @@ -270,7 +260,7 @@ fn emit_messages_default( Group::with_title(level.clone().secondary_title(msg)), )); - let mut file_ann = collect_annotations(args, &c.span, sm, &self.translator); + let mut file_ann = collect_annotations(args, &c.span, sm); let primary_span = c.span.primary_span().unwrap_or_default(); if !primary_span.is_dummy() { let primary_lo = sm.lookup_char_pos(primary_span.lo()); @@ -308,9 +298,10 @@ fn emit_messages_default( // do not display this suggestion, it is meant only for tools } SuggestionStyle::HideCodeAlways => { - let msg = self - .translator - .translate_messages(&[(suggestion.msg.to_owned(), Style::HeaderMsg)], args); + let msg = format_diag_messages( + &[(suggestion.msg.to_owned(), Style::HeaderMsg)], + args, + ); group = group.element(annotate_snippets::Level::HELP.message(msg)); } SuggestionStyle::HideCodeInline @@ -367,12 +358,7 @@ fn emit_messages_default( if substitutions.is_empty() { continue; } - let mut msg = self - .translator - .translate_message(&suggestion.msg, args) - .map_err(Report::new) - .unwrap() - .to_string(); + let mut msg = format_diag_message(&suggestion.msg, args).to_string(); let lo = substitutions .iter() @@ -551,11 +537,11 @@ fn pre_style_msgs( &self, msgs: &[(DiagMessage, Style)], level: Level, - args: &FluentArgs<'_>, + args: &DiagArgMap, ) -> String { msgs.iter() .filter_map(|(m, style)| { - let text = self.translator.translate_message(m, args).map_err(Report::new).unwrap(); + let text = format_diag_message(m, args); let style = style.anstyle(level); if text.is_empty() { None } else { Some(format!("{style}{text}{style:#}")) } }) @@ -685,10 +671,9 @@ struct Annotation { } fn collect_annotations( - args: &FluentArgs<'_>, + args: &DiagArgMap, msp: &MultiSpan, sm: &Arc, - translator: &Translator, ) -> Vec<(Arc, Vec)> { let mut output: Vec<(Arc, Vec)> = vec![]; @@ -703,11 +688,7 @@ fn collect_annotations( let kind = if is_primary { AnnotationKind::Primary } else { AnnotationKind::Context }; - let label = label.as_ref().map(|m| { - normalize_whitespace( - &translator.translate_message(m, args).map_err(Report::new).unwrap(), - ) - }); + let label = label.as_ref().map(|m| normalize_whitespace(&format_diag_message(m, args))); let ann = Annotation { kind, span, label }; if sm.is_valid_span(ann.span).is_ok() { diff --git a/compiler/rustc_errors/src/decorate_diag.rs b/compiler/rustc_errors/src/decorate_diag.rs index 5aef26ccf973..a11082e29663 100644 --- a/compiler/rustc_errors/src/decorate_diag.rs +++ b/compiler/rustc_errors/src/decorate_diag.rs @@ -1,15 +1,16 @@ /// This module provides types and traits for buffering lints until later in compilation. use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::sync::DynSend; use rustc_error_messages::MultiSpan; use rustc_lint_defs::{BuiltinLintDiag, Lint, LintId}; -use crate::{DynSend, LintDiagnostic, LintDiagnosticBox}; +use crate::{Diag, DiagCtxtHandle, Diagnostic, Level}; -/// We can't implement `LintDiagnostic` for `BuiltinLintDiag`, because decorating some of its +/// We can't implement `Diagnostic` for `BuiltinLintDiag`, because decorating some of its /// variants requires types we don't have yet. So, handle that case separately. pub enum DecorateDiagCompat { - Dynamic(Box LintDiagnosticBox<'a, ()> + DynSend + 'static>), + Dynamic(Box FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + 'static>), Builtin(BuiltinLintDiag), } @@ -19,12 +20,10 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { } } -impl !LintDiagnostic<'_, ()> for BuiltinLintDiag {} - -impl LintDiagnostic<'a, ()> + DynSend + 'static> From for DecorateDiagCompat { +impl Diagnostic<'a, ()> + DynSend + 'static> From for DecorateDiagCompat { #[inline] fn from(d: D) -> Self { - Self::Dynamic(Box::new(d)) + Self::Dynamic(Box::new(|dcx, level| d.into_diag(dcx, level))) } } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 087c5e700df0..631ed54cc024 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -7,8 +7,8 @@ use std::path::PathBuf; use std::thread::panicking; -use rustc_data_structures::fx::FxIndexMap; -use rustc_error_messages::{DiagArgName, DiagArgValue, IntoDiagArg}; +use rustc_data_structures::sync::DynSend; +use rustc_error_messages::{DiagArgMap, DiagArgName, DiagArgValue, IntoDiagArg}; use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; use rustc_span::source_map::Spanned; @@ -20,8 +20,6 @@ MultiSpan, StashKey, Style, Substitution, SubstitutionPart, SuggestionStyle, Suggestions, }; -pub type DiagArgMap = FxIndexMap; - /// Trait for types that `Diag::emit` can return as a "guarantee" (or "proof") /// token that the emission happened. pub trait EmissionGuarantee: Sized { @@ -108,6 +106,7 @@ fn emit_producing_guarantee(diag: Diag<'_, Self>) -> Self::EmitResult { pub trait Diagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> { /// Write out as a diagnostic out of `DiagCtxt`. #[must_use] + #[track_caller] fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G>; } @@ -121,6 +120,14 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { } } +impl<'a> Diagnostic<'a, ()> + for Box FnOnce(DiagCtxtHandle<'b>, Level) -> Diag<'b, ()> + DynSend + 'static> +{ + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + self(dcx, level) + } +} + /// Trait implemented by error types. This should not be implemented manually. Instead, use /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic]. #[rustc_diagnostic_item = "Subdiagnostic"] @@ -132,26 +139,8 @@ pub trait Subdiagnostic fn add_to_diag(self, diag: &mut Diag<'_, G>); } -/// Trait implemented by lint types. This should not be implemented manually. Instead, use -/// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. -#[rustc_diagnostic_item = "LintDiagnostic"] -pub trait LintDiagnostic<'a, G: EmissionGuarantee> { - /// Decorate a lint with the information from this type. - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>); -} - -pub trait LintDiagnosticBox<'a, G: EmissionGuarantee> { - fn decorate_lint_box<'b>(self: Box, diag: &'b mut Diag<'a, G>); -} - -impl<'a, G: EmissionGuarantee, D: LintDiagnostic<'a, G>> LintDiagnosticBox<'a, G> for D { - fn decorate_lint_box<'b>(self: Box, diag: &'b mut Diag<'a, G>) { - self.decorate_lint(diag); - } -} - #[derive(Clone, Debug, Encodable, Decodable)] -pub(crate) struct DiagLocation { +pub struct DiagLocation { file: Cow<'static, str>, line: u32, col: u32, @@ -159,7 +148,7 @@ pub(crate) struct DiagLocation { impl DiagLocation { #[track_caller] - fn caller() -> Self { + pub fn caller() -> Self { let loc = panic::Location::caller(); DiagLocation { file: loc.file().into(), line: loc.line(), col: loc.column() } } @@ -247,9 +236,6 @@ pub struct DiagInner { pub suggestions: Suggestions, pub args: DiagArgMap, - // This is used to store args and restore them after a subdiagnostic is rendered. - pub reserved_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`. @@ -260,7 +246,7 @@ pub struct DiagInner { pub long_ty_path: Option, /// With `-Ztrack_diagnostics` enabled, /// we print where in rustc this error was emitted. - pub(crate) emitted_at: DiagLocation, + pub emitted_at: DiagLocation, } impl DiagInner { @@ -280,7 +266,6 @@ pub fn new_with_messages(level: Level, messages: Vec<(DiagMessage, Style)>) -> S children: vec![], suggestions: Suggestions::Enabled(vec![]), args: Default::default(), - reserved_args: Default::default(), sort_span: DUMMY_SP, is_lint: None, long_ty_path: None, @@ -345,14 +330,6 @@ pub fn remove_arg(&mut self, name: &str) { self.args.swap_remove(name); } - pub fn store_args(&mut self) { - self.reserved_args = self.args.clone(); - } - - pub fn restore_args(&mut self) { - self.args = std::mem::take(&mut self.reserved_args); - } - pub fn emitted_at_sub_diag(&self) -> Subdiag { let track = format!("-Ztrack-diagnostics: created at {}", self.emitted_at); Subdiag { @@ -838,29 +815,13 @@ fn push_suggestion(&mut self, suggestion: CodeSuggestion) { } with_fn! { with_multipart_suggestion, - /// Show a suggestion that has multiple parts to it. + /// Show a suggestion that has multiple parts to it, always as its own subdiagnostic. /// In other words, multiple changes need to be applied as part of this suggestion. pub fn multipart_suggestion( &mut self, msg: impl Into, suggestion: Vec<(Span, String)>, applicability: Applicability, - ) -> &mut Self { - self.multipart_suggestion_with_style( - msg, - suggestion, - applicability, - SuggestionStyle::ShowCode, - ) - } } - - /// Show a suggestion that has multiple parts to it, always as its own subdiagnostic. - /// In other words, multiple changes need to be applied as part of this suggestion. - pub fn multipart_suggestion_verbose( - &mut self, - msg: impl Into, - suggestion: Vec<(Span, String)>, - applicability: Applicability, ) -> &mut Self { self.multipart_suggestion_with_style( msg, @@ -868,7 +829,7 @@ pub fn multipart_suggestion_verbose( applicability, SuggestionStyle::ShowAlways, ) - } + } } /// [`Diag::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. pub fn multipart_suggestion_with_style( @@ -1163,7 +1124,7 @@ pub fn tool_only_span_suggestion( } } /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see - /// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages + /// [rustc_macros::Subdiagnostic]). Performs eager formatting of any messages /// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of /// interpolated variables). pub fn subdiagnostic(&mut self, subdiagnostic: impl Subdiagnostic) -> &mut Self { @@ -1171,16 +1132,6 @@ pub fn subdiagnostic(&mut self, subdiagnostic: impl Subdiagnostic) -> &mut Self self } - /// Fluent variables are not namespaced from each other, so when - /// `Diagnostic`s and `Subdiagnostic`s use the same variable name, - /// one value will clobber the other. Eagerly translating the - /// diagnostic uses the variables defined right then, before the - /// clobbering occurs. - pub fn eagerly_translate(&self, msg: impl Into) -> DiagMessage { - let args = self.args.iter(); - self.dcx.eagerly_translate(msg.into(), args) - } - with_fn! { with_span, /// Add a span. pub fn span(&mut self, sp: impl Into) -> &mut Self { @@ -1340,6 +1291,13 @@ pub fn cancel(mut self) { drop(self); } + /// Cancels this diagnostic and returns its first message, if it exists. + pub fn cancel_into_message(self) -> Option { + let s = self.diag.as_ref()?.messages.get(0)?.0.as_str().map(ToString::to_string); + self.cancel(); + s + } + /// See `DiagCtxt::stash_diagnostic` for details. pub fn stash(mut self, span: Span, key: StashKey) -> Option { let diag = self.take_diag(); @@ -1361,12 +1319,6 @@ pub fn delay_as_bug(mut self) -> G::EmitResult { self.downgrade_to_delayed_bug(); self.emit() } - - pub fn remove_arg(&mut self, name: &str) { - if let Some(diag) = self.diag.as_mut() { - diag.remove_arg(name); - } - } } /// Destructor bomb: every `Diag` must be consumed (emitted, cancelled, etc.) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 4ceb5cf06f93..fa3ff21b2726 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -8,7 +8,6 @@ //! The output types are defined in `rustc_session::config::ErrorOutputType`. use std::borrow::Cow; -use std::error::Report; use std::io::prelude::*; use std::io::{self, IsTerminal}; use std::iter; @@ -18,14 +17,14 @@ use anstyle::{AnsiColor, Effects}; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::sync::DynSend; -use rustc_error_messages::FluentArgs; +use rustc_error_messages::DiagArgMap; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::{FileName, SourceFile, Span}; use tracing::{debug, warn}; +use crate::formatting::format_diag_message; use crate::timings::TimingRecord; -use crate::translation::Translator; use crate::{ CodeSuggestion, DiagInner, DiagMessage, Level, MultiSpan, Style, Subdiag, SuggestionStyle, }; @@ -88,8 +87,6 @@ fn supports_color(&self) -> bool { fn source_map(&self) -> Option<&SourceMap>; - fn translator(&self) -> &Translator; - /// Formats the substitutions of the primary_span /// /// There are a lot of conditions to this method, but in short: @@ -105,14 +102,10 @@ fn primary_span_formatted( &self, primary_span: &mut MultiSpan, suggestions: &mut Vec, - fluent_args: &FluentArgs<'_>, + fluent_args: &DiagArgMap, ) { if let Some((sugg, rest)) = suggestions.split_first() { - let msg = self - .translator() - .translate_message(&sugg.msg, fluent_args) - .map_err(Report::new) - .unwrap(); + let msg = format_diag_message(&sugg.msg, fluent_args); if rest.is_empty() // ^ if there is only one suggestion // don't display multi-suggestions as labels @@ -383,15 +376,9 @@ fn emit_diagnostic(&mut self, mut diag: DiagInner) { diag.sub(Level::Note, self.note.clone(), MultiSpan::new()); self.emitter.emit_diagnostic(diag); } - - fn translator(&self) -> &Translator { - self.emitter.translator() - } } -pub struct SilentEmitter { - pub translator: Translator, -} +pub struct SilentEmitter; impl Emitter for SilentEmitter { fn source_map(&self) -> Option<&SourceMap> { @@ -399,10 +386,6 @@ fn source_map(&self) -> Option<&SourceMap> { } fn emit_diagnostic(&mut self, _diag: DiagInner) {} - - fn translator(&self) -> &Translator { - &self.translator - } } /// Maximum number of suggestions to be shown diff --git a/compiler/rustc_errors/src/error.rs b/compiler/rustc_errors/src/error.rs deleted file mode 100644 index 462467d9fa0b..000000000000 --- a/compiler/rustc_errors/src/error.rs +++ /dev/null @@ -1,139 +0,0 @@ -use std::borrow::Cow; -use std::error::Error; -use std::fmt; - -use rustc_error_messages::fluent_bundle::resolver::errors::{ReferenceKind, ResolverError}; -use rustc_error_messages::{FluentArgs, FluentError}; - -#[derive(Debug)] -pub enum TranslateError<'args> { - One { - id: &'args Cow<'args, str>, - args: &'args FluentArgs<'args>, - kind: TranslateErrorKind<'args>, - }, - Two { - primary: Box>, - fallback: Box>, - }, -} - -impl<'args> TranslateError<'args> { - pub fn message(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self { - Self::One { id, args, kind: TranslateErrorKind::MessageMissing } - } - - pub fn primary(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self { - Self::One { id, args, kind: TranslateErrorKind::PrimaryBundleMissing } - } - - pub fn attribute( - id: &'args Cow<'args, str>, - args: &'args FluentArgs<'args>, - attr: &'args str, - ) -> Self { - Self::One { id, args, kind: TranslateErrorKind::AttributeMissing { attr } } - } - - pub fn value(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self { - Self::One { id, args, kind: TranslateErrorKind::ValueMissing } - } - - pub fn fluent( - id: &'args Cow<'args, str>, - args: &'args FluentArgs<'args>, - errs: Vec, - ) -> Self { - Self::One { id, args, kind: TranslateErrorKind::Fluent { errs } } - } - - pub fn and(self, fallback: TranslateError<'args>) -> TranslateError<'args> { - Self::Two { primary: Box::new(self), fallback: Box::new(fallback) } - } -} - -#[derive(Debug)] -pub enum TranslateErrorKind<'args> { - MessageMissing, - PrimaryBundleMissing, - AttributeMissing { attr: &'args str }, - ValueMissing, - Fluent { errs: Vec }, -} - -impl fmt::Display for TranslateError<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use TranslateErrorKind::*; - - match self { - Self::One { id, args, kind } => { - writeln!(f, "failed while formatting fluent string `{id}`: ")?; - match kind { - MessageMissing => writeln!(f, "message was missing")?, - PrimaryBundleMissing => writeln!(f, "the primary bundle was missing")?, - AttributeMissing { attr } => { - writeln!(f, "the attribute `{attr}` was missing")?; - writeln!(f, "help: add `.{attr} = `")?; - } - ValueMissing => writeln!(f, "the value was missing")?, - Fluent { errs } => { - for err in errs { - match err { - FluentError::ResolverError(ResolverError::Reference( - ReferenceKind::Message { id, .. } - | ReferenceKind::Variable { id, .. }, - )) => { - if args.iter().any(|(arg_id, _)| arg_id == id) { - writeln!( - f, - "argument `{id}` exists but was not referenced correctly" - )?; - writeln!(f, "help: try using `{{${id}}}` instead")?; - } else { - writeln!( - f, - "the fluent string has an argument `{id}` that was not found." - )?; - let vars: Vec<&str> = - args.iter().map(|(a, _v)| a).collect(); - match &*vars { - [] => writeln!(f, "help: no arguments are available")?, - [one] => writeln!( - f, - "help: the argument `{one}` is available" - )?, - [first, middle @ .., last] => { - write!(f, "help: the arguments `{first}`")?; - for a in middle { - write!(f, ", `{a}`")?; - } - writeln!(f, " and `{last}` are available")?; - } - } - } - } - _ => writeln!(f, "{err}")?, - } - } - } - } - } - // If someone cares about primary bundles, they'll probably notice it's missing - // regardless or will be using `debug_assertions` - // so we skip the arm below this one to avoid confusing the regular user. - Self::Two { primary: box Self::One { kind: PrimaryBundleMissing, .. }, fallback } => { - fmt::Display::fmt(fallback, f)?; - } - Self::Two { primary, fallback } => { - writeln!( - f, - "first, fluent formatting using the primary bundle failed:\n {primary}\n \ - while attempting to recover by using the fallback bundle instead, another error occurred:\n{fallback}" - )?; - } - } - Ok(()) - } -} - -impl Error for TranslateError<'_> {} diff --git a/compiler/rustc_errors/src/formatting.rs b/compiler/rustc_errors/src/formatting.rs new file mode 100644 index 000000000000..7b617031d6c8 --- /dev/null +++ b/compiler/rustc_errors/src/formatting.rs @@ -0,0 +1,104 @@ +use std::borrow::Cow; + +pub use rustc_error_messages::FluentArgs; +use rustc_error_messages::{DiagArgMap, DiagArgName, IntoDiagArg, langid, register_functions}; +use tracing::{debug, trace}; + +use crate::fluent_bundle::FluentResource; +use crate::{DiagArg, DiagMessage, Style, fluent_bundle}; + +/// Convert diagnostic arguments (a rustc internal type that exists to implement +/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform formatting. +fn to_fluent_args<'iter>(iter: impl Iterator>) -> FluentArgs<'static> { + let mut args = if let Some(size) = iter.size_hint().1 { + FluentArgs::with_capacity(size) + } else { + FluentArgs::new() + }; + + for (k, v) in iter { + args.set(k.clone(), v.clone()); + } + + args +} + +/// Convert `DiagMessage`s to a string +pub fn format_diag_messages( + messages: &[(DiagMessage, Style)], + args: &DiagArgMap, +) -> Cow<'static, str> { + Cow::Owned(messages.iter().map(|(m, _)| format_diag_message(m, args)).collect::()) +} + +/// Convert a `DiagMessage` to a string +pub fn format_diag_message<'a>(message: &'a DiagMessage, args: &DiagArgMap) -> Cow<'a, str> { + match message { + DiagMessage::Str(msg) => Cow::Borrowed(msg), + DiagMessage::Inline(msg) => format_fluent_str(msg, args), + } +} + +fn format_fluent_str(message: &str, args: &DiagArgMap) -> Cow<'static, str> { + trace!(?message, ?args); + const GENERATED_MSG_ID: &str = "generated_msg"; + let resource = FluentResource::try_new(format!("{GENERATED_MSG_ID} = {message}\n")).unwrap(); + let mut bundle = fluent_bundle::FluentBundle::new(vec![langid!("en-US")]); + bundle.set_use_isolating(false); + bundle.add_resource(resource).unwrap(); + register_functions(&mut bundle); + let message = bundle.get_message(GENERATED_MSG_ID).unwrap(); + let value = message.value().unwrap(); + let args = to_fluent_args(args.iter()); + + let mut errs = vec![]; + let formatted = bundle.format_pattern(value, Some(&args), &mut errs).to_string(); + debug!(?formatted, ?errs); + if errs.is_empty() { + Cow::Owned(formatted) + } else { + panic!("Fluent errors while formatting message: {errs:?}"); + } +} + +pub trait DiagMessageAddArg { + fn arg(self, name: impl Into, arg: impl IntoDiagArg) -> EagerDiagMessageBuilder; +} + +pub struct EagerDiagMessageBuilder { + fluent_str: Cow<'static, str>, + args: DiagArgMap, +} + +impl DiagMessageAddArg for EagerDiagMessageBuilder { + fn arg( + mut self, + name: impl Into, + arg: impl IntoDiagArg, + ) -> EagerDiagMessageBuilder { + let name = name.into(); + let value = arg.into_diag_arg(&mut None); + debug_assert!( + !self.args.contains_key(&name) || self.args.get(&name) == Some(&value), + "arg {} already exists", + name + ); + self.args.insert(name, value); + self + } +} + +impl DiagMessageAddArg for DiagMessage { + fn arg(self, name: impl Into, arg: impl IntoDiagArg) -> EagerDiagMessageBuilder { + let DiagMessage::Inline(fluent_str) = self else { + panic!("Tried to eagerly format an already formatted message") + }; + EagerDiagMessageBuilder { fluent_str, args: Default::default() }.arg(name, arg) + } +} + +impl EagerDiagMessageBuilder { + pub fn format(self) -> DiagMessage { + DiagMessage::Str(format_fluent_str(&self.fluent_str, &self.args)) + } +} diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 310a64745bad..04ac140f3326 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -9,7 +9,6 @@ // FIXME: spec the JSON output properly. -use std::error::Report; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; @@ -18,7 +17,7 @@ use anstream::{AutoStream, ColorChoice}; use derive_setters::Setters; use rustc_data_structures::sync::IntoDynSyncSend; -use rustc_error_messages::FluentArgs; +use rustc_error_messages::DiagArgMap; use rustc_lint_defs::Applicability; use rustc_span::hygiene::ExpnData; use rustc_span::source_map::{FilePathMapping, SourceMap}; @@ -31,8 +30,8 @@ ColorConfig, Destination, Emitter, HumanReadableErrorType, OutputTheme, TimingEvent, should_show_source_code, }; +use crate::formatting::{format_diag_message, format_diag_messages}; use crate::timings::{TimingRecord, TimingSection}; -use crate::translation::{Translator, to_fluent_args}; use crate::{CodeSuggestion, MultiSpan, SpanLabel, Subdiag, Suggestions, TerminalUrl}; #[cfg(test)] @@ -45,8 +44,6 @@ pub struct JsonEmitter { #[setters(skip)] sm: Option>, #[setters(skip)] - translator: Translator, - #[setters(skip)] pretty: bool, ui_testing: bool, ignored_directories_in_source_blocks: Vec, @@ -63,7 +60,6 @@ impl JsonEmitter { pub fn new( dst: Box, sm: Option>, - translator: Translator, pretty: bool, json_rendered: HumanReadableErrorType, color_config: ColorConfig, @@ -71,7 +67,6 @@ pub fn new( JsonEmitter { dst: IntoDynSyncSend(dst), sm, - translator, pretty, ui_testing: false, ignored_directories_in_source_blocks: Vec::new(), @@ -180,10 +175,6 @@ fn source_map(&self) -> Option<&SourceMap> { fn should_show_explain(&self) -> bool { !self.json_rendered.short() } - - fn translator(&self) -> &Translator { - &self.translator - } } // The following data types are provided just for serialisation. @@ -307,15 +298,13 @@ struct UnusedExterns<'a> { impl Diagnostic { /// Converts from `rustc_errors::DiagInner` to `Diagnostic`. fn from_errors_diagnostic(diag: crate::DiagInner, je: &JsonEmitter) -> Diagnostic { - let args = to_fluent_args(diag.args.iter()); let sugg_to_diag = |sugg: &CodeSuggestion| { - let translated_message = - je.translator.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap(); + let formatted_message = format_diag_message(&sugg.msg, &diag.args); Diagnostic { - message: translated_message.to_string(), + message: formatted_message.to_string(), code: None, level: "help", - spans: DiagnosticSpan::from_suggestion(sugg, &args, je), + spans: DiagnosticSpan::from_suggestion(sugg, &diag.args, je), children: vec![], rendered: None, } @@ -341,7 +330,7 @@ fn flush(&mut self) -> io::Result<()> { } } - let translated_message = je.translator.translate_messages(&diag.messages, &args); + let formatted_message = format_diag_messages(&diag.messages, &diag.args); let code = if let Some(code) = diag.code { Some(DiagnosticCode { @@ -354,16 +343,18 @@ fn flush(&mut self) -> io::Result<()> { None }; let level = diag.level.to_str(); - let spans = DiagnosticSpan::from_multispan(&diag.span, &args, je); + let spans = DiagnosticSpan::from_multispan(&diag.span, &diag.args, je); let mut children: Vec = diag .children .iter() - .map(|c| Diagnostic::from_sub_diagnostic(c, &args, je)) + .map(|c| Diagnostic::from_sub_diagnostic(c, &diag.args, je)) .chain(sugg) .collect(); if je.track_diagnostics && diag.span.has_primary_spans() && !diag.span.is_dummy() { - children - .insert(0, Diagnostic::from_sub_diagnostic(&diag.emitted_at_sub_diag(), &args, je)); + children.insert( + 0, + Diagnostic::from_sub_diagnostic(&diag.emitted_at_sub_diag(), &diag.args, je), + ); } let buf = BufWriter(Arc::new(Mutex::new(Vec::new()))); let dst: Destination = AutoStream::new( @@ -373,7 +364,7 @@ fn flush(&mut self) -> io::Result<()> { choice => choice, }, ); - AnnotateSnippetEmitter::new(dst, je.translator.clone()) + AnnotateSnippetEmitter::new(dst) .short_message(je.json_rendered.short) .sm(je.sm.clone()) .diagnostic_width(je.diagnostic_width) @@ -389,7 +380,7 @@ fn flush(&mut self) -> io::Result<()> { let buf = String::from_utf8(buf).unwrap(); Diagnostic { - message: translated_message.to_string(), + message: formatted_message.to_string(), code, level, spans, @@ -398,14 +389,10 @@ fn flush(&mut self) -> io::Result<()> { } } - fn from_sub_diagnostic( - subdiag: &Subdiag, - args: &FluentArgs<'_>, - je: &JsonEmitter, - ) -> Diagnostic { - let translated_message = je.translator.translate_messages(&subdiag.messages, args); + fn from_sub_diagnostic(subdiag: &Subdiag, args: &DiagArgMap, je: &JsonEmitter) -> Diagnostic { + let formatted_message = format_diag_messages(&subdiag.messages, args); Diagnostic { - message: translated_message.to_string(), + message: formatted_message.to_string(), code: None, level: subdiag.level.to_str(), spans: DiagnosticSpan::from_multispan(&subdiag.span, args, je), @@ -419,16 +406,13 @@ impl DiagnosticSpan { fn from_span_label( span: SpanLabel, suggestion: Option<(&String, Applicability)>, - args: &FluentArgs<'_>, + args: &DiagArgMap, je: &JsonEmitter, ) -> DiagnosticSpan { Self::from_span_etc( span.span, span.is_primary, - span.label - .as_ref() - .map(|m| je.translator.translate_message(m, args).unwrap()) - .map(|m| m.to_string()), + span.label.as_ref().map(|m| format_diag_message(m, args)).map(|m| m.to_string()), suggestion, je, ) @@ -522,11 +506,7 @@ fn from_span_full( } } - fn from_multispan( - msp: &MultiSpan, - args: &FluentArgs<'_>, - je: &JsonEmitter, - ) -> Vec { + fn from_multispan(msp: &MultiSpan, args: &DiagArgMap, je: &JsonEmitter) -> Vec { msp.span_labels() .into_iter() .map(|span_str| Self::from_span_label(span_str, None, args, je)) @@ -535,7 +515,7 @@ fn from_multispan( fn from_suggestion( suggestion: &CodeSuggestion, - args: &FluentArgs<'_>, + args: &DiagArgMap, je: &JsonEmitter, ) -> Vec { suggestion diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index ffcf9947a7a8..71b66f2e0223 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -45,13 +45,11 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { rustc_span::create_default_session_globals_then(|| { let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); sm.new_source_file(filename(&sm, "test.rs"), code.to_owned()); - let translator = Translator::new(); let output = Arc::new(Mutex::new(Vec::new())); let je = JsonEmitter::new( Box::new(Shared { data: output.clone() }), Some(sm), - translator, true, // pretty HumanReadableErrorType { short: true, unicode: false }, ColorConfig::Never, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 989d28eac455..0a111538fc89 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -5,11 +5,8 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::direct_use_of_rustc_type_ir)] -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(associated_type_defaults)] -#![feature(box_patterns)] #![feature(default_field_values)] -#![feature(error_reporter)] #![feature(macro_metavar_expr_concat)] #![feature(negative_impls)] #![feature(never_type)] @@ -21,14 +18,13 @@ use std::backtrace::{Backtrace, BacktraceStatus}; use std::borrow::Cow; use std::cell::Cell; -use std::error::Report; use std::ffi::OsStr; use std::hash::Hash; use std::io::Write; use std::num::NonZero; use std::ops::DerefMut; use std::path::{Path, PathBuf}; -use std::{fmt, panic}; +use std::{assert_matches, fmt, panic}; use Level::*; // Used by external projects such as `rust-gpu`. @@ -40,8 +36,8 @@ pub use codes::*; pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; pub use diagnostic::{ - BugAbort, Diag, DiagArgMap, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee, - FatalAbort, LintDiagnostic, LintDiagnosticBox, StringPart, Subdiag, Subdiagnostic, + BugAbort, Diag, DiagInner, DiagLocation, DiagStyledString, Diagnostic, EmissionGuarantee, + FatalAbort, StringPart, Subdiag, Subdiagnostic, }; pub use diagnostic_impls::{ DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, @@ -49,14 +45,13 @@ }; pub use emitter::ColorConfig; use emitter::{DynEmitter, Emitter}; +use rustc_data_structures::AtomicRef; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{DynSend, Lock}; -use rustc_data_structures::{AtomicRef, assert_matches}; pub use rustc_error_messages::{ - DiagArg, DiagArgFromDisplay, DiagArgName, DiagArgValue, DiagMessage, FluentBundle, IntoDiagArg, - LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, fallback_fluent_bundle, - fluent_bundle, into_diag_arg_using_display, + DiagArg, DiagArgFromDisplay, DiagArgMap, DiagArgName, DiagArgValue, DiagMessage, IntoDiagArg, + LanguageIdentifier, MultiSpan, SpanLabel, fluent_bundle, into_diag_arg_using_display, }; use rustc_hashes::Hash128; use rustc_lint_defs::LintExpectationId; @@ -70,6 +65,8 @@ use tracing::debug; use crate::emitter::TimingEvent; +use crate::formatting::DiagMessageAddArg; +pub use crate::formatting::format_diag_message; use crate::timings::TimingRecord; pub mod annotate_snippet_emitter_writer; @@ -78,12 +75,11 @@ mod diagnostic; mod diagnostic_impls; pub mod emitter; -pub mod error; +pub mod formatting; pub mod json; mod lock; pub mod markdown; pub mod timings; -pub mod translation; pub type PResult<'a, T> = Result>; @@ -479,34 +475,13 @@ pub fn new(emitter: Box) -> Self { pub fn make_silent(&self) { let mut inner = self.inner.borrow_mut(); - let translator = inner.emitter.translator().clone(); - inner.emitter = Box::new(emitter::SilentEmitter { translator }); + inner.emitter = Box::new(emitter::SilentEmitter {}); } pub fn set_emitter(&self, emitter: Box) { self.inner.borrow_mut().emitter = emitter; } - /// Translate `message` eagerly with `args` to `DiagMessage::Eager`. - pub fn eagerly_translate<'a>( - &self, - message: DiagMessage, - args: impl Iterator>, - ) -> DiagMessage { - let inner = self.inner.borrow(); - inner.eagerly_translate(message, args) - } - - /// Translate `message` eagerly with `args` to `String`. - pub fn eagerly_translate_to_string<'a>( - &self, - message: DiagMessage, - args: impl Iterator>, - ) -> String { - let inner = self.inner.borrow(); - inner.eagerly_translate_to_string(message, args) - } - // This is here to not allow mutation of flags; // as of this writing it's used in Session::consider_optimizing and // in tests in rustc_interface. @@ -1422,38 +1397,6 @@ fn has_errors_or_delayed_bugs(&self) -> Option { self.has_errors().or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied()) } - /// Translate `message` eagerly with `args` to `DiagMessage::Eager`. - fn eagerly_translate<'a>( - &self, - message: DiagMessage, - args: impl Iterator>, - ) -> DiagMessage { - DiagMessage::Str(Cow::from(self.eagerly_translate_to_string(message, args))) - } - - /// Translate `message` eagerly with `args` to `String`. - fn eagerly_translate_to_string<'a>( - &self, - message: DiagMessage, - args: impl Iterator>, - ) -> String { - let args = crate::translation::to_fluent_args(args); - self.emitter - .translator() - .translate_message(&message, &args) - .map_err(Report::new) - .unwrap() - .to_string() - } - - fn eagerly_translate_for_subdiag( - &self, - diag: &DiagInner, - msg: impl Into, - ) -> DiagMessage { - self.eagerly_translate(msg.into(), diag.args.iter()) - } - fn flush_delayed(&mut self) { // Stashed diagnostics must be emitted before delayed bugs are flushed. // Otherwise, we might ICE prematurely when errors would have @@ -1503,7 +1446,7 @@ fn flush_delayed(&mut self) { ); } - let mut bug = if decorate { bug.decorate(self) } else { bug.inner }; + let mut bug = if decorate { bug.decorate() } else { bug.inner }; // "Undelay" the delayed bugs into plain bugs. if bug.level != DelayedBug { @@ -1513,11 +1456,9 @@ fn flush_delayed(&mut self) { // We are at the `DiagInner`/`DiagCtxtInner` level rather than // the usual `Diag`/`DiagCtxt` level, so we must augment `bug` // in a lower-level fashion. - bug.arg("level", bug.level); let msg = msg!( "`flushed_delayed` got diagnostic with level {$level}, instead of the expected `DelayedBug`" - ); - let msg = self.eagerly_translate_for_subdiag(&bug, msg); // after the `arg` call + ).arg("level", bug.level).format(); bug.sub(Note, msg, bug.span.primary_span().unwrap().into()); } bug.level = Bug; @@ -1552,7 +1493,7 @@ fn with_backtrace(diagnostic: DiagInner, backtrace: Backtrace) -> Self { DelayedDiagInner { inner: diagnostic, note: backtrace } } - fn decorate(self, dcx: &DiagCtxtInner) -> DiagInner { + fn decorate(self) -> DiagInner { // We are at the `DiagInner`/`DiagCtxtInner` level rather than the // usual `Diag`/`DiagCtxt` level, so we must construct `diag` in a // lower-level fashion. @@ -1565,10 +1506,10 @@ fn decorate(self, dcx: &DiagCtxtInner) -> DiagInner { // Avoid the needless newline when no backtrace has been captured, // the display impl should just be a single line. _ => msg!("delayed at {$emitted_at} - {$note}"), - }; - diag.arg("emitted_at", diag.emitted_at.clone()); - diag.arg("note", self.note); - let msg = dcx.eagerly_translate_for_subdiag(&diag, msg); // after the `arg` calls + } + .arg("emitted_at", diag.emitted_at.clone()) + .arg("note", self.note) + .format(); diag.sub(Note, msg, diag.span.primary_span().unwrap_or(DUMMY_SP).into()); diag } diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs deleted file mode 100644 index c6d30032f1af..000000000000 --- a/compiler/rustc_errors/src/translation.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::borrow::Cow; -use std::error::Report; -use std::sync::Arc; - -pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle}; -use rustc_error_messages::{langid, register_functions}; -use tracing::{debug, trace}; - -use crate::error::TranslateError; -use crate::fluent_bundle::FluentResource; -use crate::{DiagArg, DiagMessage, FluentBundle, Style, fluent_bundle}; - -/// Convert diagnostic arguments (a rustc internal type that exists to implement -/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. -/// -/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then -/// passed around as a reference thereafter. -pub fn to_fluent_args<'iter>(iter: impl Iterator>) -> FluentArgs<'static> { - let mut args = if let Some(size) = iter.size_hint().1 { - FluentArgs::with_capacity(size) - } else { - FluentArgs::new() - }; - - for (k, v) in iter { - args.set(k.clone(), v.clone()); - } - - args -} - -#[derive(Clone)] -pub struct Translator { - /// Localized diagnostics for the locale requested by the user. If no language was requested by - /// the user then this will be `None` and `fallback_fluent_bundle` should be used. - pub fluent_bundle: Option>, -} - -impl Translator { - pub fn new() -> Translator { - Translator { fluent_bundle: None } - } - - /// Convert `DiagMessage`s to a string, performing translation if necessary. - pub fn translate_messages( - &self, - messages: &[(DiagMessage, Style)], - args: &FluentArgs<'_>, - ) -> Cow<'_, str> { - Cow::Owned( - messages - .iter() - .map(|(m, _)| self.translate_message(m, args).map_err(Report::new).unwrap()) - .collect::(), - ) - } - - /// Convert a `DiagMessage` to a string, performing translation if necessary. - pub fn translate_message<'a>( - &'a self, - message: &'a DiagMessage, - args: &'a FluentArgs<'_>, - ) -> Result, TranslateError<'a>> { - trace!(?message, ?args); - match message { - DiagMessage::Str(msg) => Ok(Cow::Borrowed(msg)), - // This translates an inline fluent diagnostic message - // It does this by creating a new `FluentBundle` with only one message, - // and then translating using this bundle. - DiagMessage::Inline(msg) => { - const GENERATED_MSG_ID: &str = "generated_msg"; - let resource = - FluentResource::try_new(format!("{GENERATED_MSG_ID} = {msg}\n")).unwrap(); - let mut bundle = fluent_bundle::FluentBundle::new(vec![langid!("en-US")]); - bundle.set_use_isolating(false); - bundle.add_resource(resource).unwrap(); - register_functions(&mut bundle); - let message = bundle.get_message(GENERATED_MSG_ID).unwrap(); - let value = message.value().unwrap(); - - let mut errs = vec![]; - let translated = bundle.format_pattern(value, Some(args), &mut errs).to_string(); - debug!(?translated, ?errs); - if errs.is_empty() { - Ok(Cow::Owned(translated)) - } else { - Err(TranslateError::fluent(&Cow::Borrowed(GENERATED_MSG_ID), args, errs)) - } - } - } - } -} diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 5efaea44b3b9..225906dfba2d 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -2,12 +2,11 @@ use std::default::Default; use std::iter; use std::path::Component::Prefix; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; use rustc_ast::attr::MarkedAttrs; -use rustc_ast::token::MetaVarKind; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind, Safety}; @@ -16,20 +15,20 @@ use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult}; use rustc_feature::Features; use rustc_hir as hir; -use rustc_hir::attrs::{AttributeKind, CfgEntry, CollapseMacroDebuginfo, Deprecation}; +use rustc_hir::attrs::{CfgEntry, CollapseMacroDebuginfo, Deprecation}; use rustc_hir::def::MacroKinds; use rustc_hir::limit::Limit; use rustc_hir::{Stability, find_attr}; use rustc_lint_defs::RegisteredTools; use rustc_parse::MACRO_ARGUMENTS; -use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser}; +use rustc_parse::parser::Parser; use rustc_session::Session; use rustc_session::parse::ParseSess; use rustc_span::def_id::{CrateNum, DefId, LocalDefId}; 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, FileName, Ident, Span, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; @@ -897,14 +896,13 @@ pub fn default(kind: SyntaxExtensionKind, edition: Edition) -> SyntaxExtension { /// | yes | yes | yes | yes | yes | fn get_collapse_debuginfo(sess: &Session, attrs: &[hir::Attribute], ext: bool) -> bool { let flag = sess.opts.cg.collapse_macro_debuginfo; - let attr = - if let Some(info) = find_attr!(attrs, AttributeKind::CollapseDebugInfo(info) => info) { - info.clone() - } else if find_attr!(attrs, AttributeKind::RustcBuiltinMacro { .. }) { - CollapseMacroDebuginfo::Yes - } else { - CollapseMacroDebuginfo::Unspecified - }; + let attr = if let Some(info) = find_attr!(attrs, CollapseDebugInfo(info) => info) { + info.clone() + } else if find_attr!(attrs, RustcBuiltinMacro { .. }) { + CollapseMacroDebuginfo::Yes + } else { + CollapseMacroDebuginfo::Unspecified + }; #[rustfmt::skip] let collapse_table = [ @@ -919,7 +917,7 @@ fn get_collapse_debuginfo(sess: &Session, attrs: &[hir::Attribute], ext: bool) - fn get_hide_backtrace(attrs: &[hir::Attribute]) -> bool { // FIXME(estebank): instead of reusing `#[rustc_diagnostic_item]` as a proxy, introduce a // new attribute purely for this under the `#[diagnostic]` namespace. - find_attr!(attrs, AttributeKind::RustcDiagnosticItem(..)) + find_attr!(attrs, RustcDiagnosticItem(..)) } /// Constructs a syntax extension with the given properties @@ -934,19 +932,17 @@ pub fn new( attrs: &[hir::Attribute], is_local: bool, ) -> SyntaxExtension { - let allow_internal_unstable = - find_attr!(attrs, AttributeKind::AllowInternalUnstable(i, _) => i) - .map(|i| i.as_slice()) - .unwrap_or_default(); - let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe(_)); + let allow_internal_unstable = find_attr!(attrs, AllowInternalUnstable(i, _) => i) + .map(|i| i.as_slice()) + .unwrap_or_default(); + let allow_internal_unsafe = find_attr!(attrs, AllowInternalUnsafe(_)); let local_inner_macros = - *find_attr!(attrs, AttributeKind::MacroExport {local_inner_macros: l, ..} => l) - .unwrap_or(&false); + *find_attr!(attrs, MacroExport {local_inner_macros: l, ..} => l).unwrap_or(&false); let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local); tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); - let (builtin_name, helper_attrs) = match find_attr!(attrs, AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, .. } => (builtin_name, helper_attrs)) + let (builtin_name, helper_attrs) = match find_attr!(attrs, RustcBuiltinMacro { builtin_name, helper_attrs, .. } => (builtin_name, helper_attrs)) { // Override `helper_attrs` passed above if it's a built-in macro, // marking `proc_macro_derive` macros as built-in is not a realistic use case. @@ -960,10 +956,9 @@ pub fn new( }; let hide_backtrace = builtin_name.is_some() || Self::get_hide_backtrace(attrs); - let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability); + let stability = find_attr!(attrs, Stability { stability, .. } => *stability); - if let Some(sp) = find_attr!(attrs, AttributeKind::RustcBodyStability{ span, .. } => *span) - { + if let Some(sp) = find_attr!(attrs, RustcBodyStability{ span, .. } => *span) { sess.dcx().emit_err(errors::MacroBodyStability { span: sp, head_span: sess.source_map().guess_head_span(span), @@ -979,7 +974,7 @@ pub fn new( stability, deprecation: find_attr!( attrs, - AttributeKind::Deprecation { deprecation, .. } => *deprecation + Deprecated { deprecation, .. } => *deprecation ), helper_attrs, edition, @@ -1421,80 +1416,3 @@ pub fn resolve_path(sess: &Session, path: impl Into, span: Span) -> PRe } } } - -/// If this item looks like a specific enums from `rental`, emit a fatal error. -/// See #73345 and #83125 for more details. -/// FIXME(#73933): Remove this eventually. -fn pretty_printing_compatibility_hack(item: &Item, psess: &ParseSess) { - if let ast::ItemKind::Enum(ident, _, enum_def) = &item.kind - && ident.name == sym::ProceduralMasqueradeDummyType - && let [variant] = &*enum_def.variants - && variant.ident.name == sym::Input - && let FileName::Real(real) = psess.source_map().span_to_filename(ident.span) - && let Some(c) = real - .local_path() - .unwrap_or(Path::new("")) - .components() - .flat_map(|c| c.as_os_str().to_str()) - .find(|c| c.starts_with("rental") || c.starts_with("allsorts-rental")) - { - let crate_matches = if c.starts_with("allsorts-rental") { - true - } else { - let mut version = c.trim_start_matches("rental-").split('.'); - version.next() == Some("0") - && version.next() == Some("5") - && version.next().and_then(|c| c.parse::().ok()).is_some_and(|v| v < 6) - }; - - if crate_matches { - psess.dcx().emit_fatal(errors::ProcMacroBackCompat { - crate_name: "rental".to_string(), - fixed_version: "0.5.6".to_string(), - }); - } - } -} - -pub(crate) fn ann_pretty_printing_compatibility_hack(ann: &Annotatable, psess: &ParseSess) { - let item = match ann { - Annotatable::Item(item) => item, - Annotatable::Stmt(stmt) => match &stmt.kind { - ast::StmtKind::Item(item) => item, - _ => return, - }, - _ => return, - }; - pretty_printing_compatibility_hack(item, psess) -} - -pub(crate) fn stream_pretty_printing_compatibility_hack( - kind: MetaVarKind, - stream: &TokenStream, - psess: &ParseSess, -) { - let item = match kind { - MetaVarKind::Item => { - let mut parser = Parser::new(psess, stream.clone(), None); - // No need to collect tokens for this simple check. - parser - .parse_item(ForceCollect::No, AllowConstBlockItems::No) - .expect("failed to reparse item") - .expect("an actual item") - } - MetaVarKind::Stmt => { - let mut parser = Parser::new(psess, stream.clone(), None); - // No need to collect tokens for this simple check. - let stmt = parser - .parse_stmt(ForceCollect::No) - .expect("failed to reparse") - .expect("an actual stmt"); - match &stmt.kind { - ast::StmtKind::Item(item) => item.clone(), - _ => return, - } - } - _ => return, - }; - pretty_printing_compatibility_hack(&item, psess) -} diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 7320ee12abe3..19a2d65762e8 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -729,7 +729,7 @@ pub fn item_const( ty: Box, rhs_kind: ast::ConstItemRhsKind, ) -> Box { - let defaultness = ast::Defaultness::Final; + let defaultness = ast::Defaultness::Implicit; self.item( span, AttrVec::new(), diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index c7d2e273a76f..8e4039b32d94 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -7,8 +7,8 @@ AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree, }; use rustc_ast::{ - self as ast, AttrItemKind, AttrKind, AttrStyle, Attribute, EarlyParsedAttribute, HasAttrs, - HasTokens, MetaItem, MetaItemInner, NodeId, NormalAttr, + self as ast, AttrItemKind, AttrKind, AttrStyle, Attribute, DUMMY_NODE_ID, EarlyParsedAttribute, + HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId, NormalAttr, }; use rustc_attr_parsing as attr; use rustc_attr_parsing::{ @@ -20,18 +20,19 @@ ACCEPTED_LANG_FEATURES, EnabledLangFeature, EnabledLibFeature, Features, REMOVED_LANG_FEATURES, UNSTABLE_LANG_FEATURES, }; -use rustc_hir::Target; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{ + Target, {self as hir}, +}; use rustc_parse::parser::Recovery; use rustc_session::Session; use rustc_session::parse::feature_err; -use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; -use thin_vec::ThinVec; +use rustc_span::{DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym}; use tracing::instrument; use crate::errors::{ CrateNameInCfgAttr, CrateTypeInCfgAttr, FeatureNotAllowed, FeatureRemoved, - FeatureRemovedReason, InvalidCfg, MalformedFeatureAttribute, MalformedFeatureAttributeHelp, - RemoveExprNotSupported, + FeatureRemovedReason, InvalidCfg, RemoveExprNotSupported, }; /// A folder that strips out items that do not belong in the current configuration. @@ -46,54 +47,32 @@ pub struct StripUnconfigured<'a> { } pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -> Features { - fn feature_list(attr: &Attribute) -> ThinVec { - if attr.has_name(sym::feature) - && let Some(list) = attr.meta_item_list() - { - list - } else { - ThinVec::new() - } - } - let mut features = Features::default(); - // Process all features enabled in the code. - for attr in krate_attrs { - for mi in feature_list(attr) { - let name = match mi.ident() { - Some(ident) if mi.is_word() => ident.name, - Some(ident) => { - sess.dcx().emit_err(MalformedFeatureAttribute { - span: mi.span(), - help: MalformedFeatureAttributeHelp::Suggestion { - span: mi.span(), - suggestion: ident.name, - }, - }); - continue; - } - None => { - sess.dcx().emit_err(MalformedFeatureAttribute { - span: mi.span(), - help: MalformedFeatureAttributeHelp::Label { span: mi.span() }, - }); - continue; - } - }; - + if let Some(hir::Attribute::Parsed(AttributeKind::Feature(feature_idents, _))) = + AttributeParser::parse_limited( + sess, + krate_attrs, + sym::feature, + DUMMY_SP, + DUMMY_NODE_ID, + Some(&features), + ) + { + for feature_ident in feature_idents { // If the enabled feature has been removed, issue an error. - if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| name == f.feature.name) { + if let Some(f) = + REMOVED_LANG_FEATURES.iter().find(|f| feature_ident.name == f.feature.name) + { let pull_note = if let Some(pull) = f.pull { format!( - "; see for more information", - pull + "; see for more information", ) } else { "".to_owned() }; sess.dcx().emit_err(FeatureRemoved { - span: mi.span(), + span: feature_ident.span, reason: f.reason.map(|reason| FeatureRemovedReason { reason }), removed_rustc_version: f.feature.since, pull_note, @@ -102,10 +81,10 @@ fn feature_list(attr: &Attribute) -> ThinVec { } // If the enabled feature is stable, record it. - if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| name == f.name) { + if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| feature_ident.name == f.name) { features.set_enabled_lang_feature(EnabledLangFeature { - gate_name: name, - attr_sp: mi.span(), + gate_name: feature_ident.name, + attr_sp: feature_ident.span, stable_since: Some(Symbol::intern(f.since)), }); continue; @@ -115,25 +94,30 @@ fn feature_list(attr: &Attribute) -> ThinVec { // unstable and not also listed as one of the allowed features, // issue an error. if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() { - if allowed.iter().all(|f| name.as_str() != f) { - sess.dcx().emit_err(FeatureNotAllowed { span: mi.span(), name }); + if allowed.iter().all(|f| feature_ident.name.as_str() != f) { + sess.dcx().emit_err(FeatureNotAllowed { + span: feature_ident.span, + name: feature_ident.name, + }); continue; } } // If the enabled feature is unstable, record it. - if UNSTABLE_LANG_FEATURES.iter().find(|f| name == f.name).is_some() { - // When the ICE comes a standard library crate, there's a chance that the person + if UNSTABLE_LANG_FEATURES.iter().find(|f| feature_ident.name == f.name).is_some() { + // When the ICE comes from a standard library crate, there's a chance that the person // hitting the ICE may be using -Zbuild-std or similar with an untested target. // The bug is probably in the standard library and not the compiler in that case, // but that doesn't really matter - we want a bug report. - if features.internal(name) && !STDLIB_STABLE_CRATES.contains(&crate_name) { + if features.internal(feature_ident.name) + && !STDLIB_STABLE_CRATES.contains(&crate_name) + { sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed); } features.set_enabled_lang_feature(EnabledLangFeature { - gate_name: name, - attr_sp: mi.span(), + gate_name: feature_ident.name, + attr_sp: feature_ident.span, stable_since: None, }); continue; @@ -141,12 +125,15 @@ fn feature_list(attr: &Attribute) -> ThinVec { // Otherwise, the feature is unknown. Enable it as a lib feature. // It will be checked later whether the feature really exists. - features - .set_enabled_lib_feature(EnabledLibFeature { gate_name: name, attr_sp: mi.span() }); + features.set_enabled_lib_feature(EnabledLibFeature { + gate_name: feature_ident.name, + attr_sp: feature_ident.span, + }); // Similar to above, detect internal lib features to suppress // the ICE message that asks for a report. - if features.internal(name) && !STDLIB_STABLE_CRATES.contains(&crate_name) { + if features.internal(feature_ident.name) && !STDLIB_STABLE_CRATES.contains(&crate_name) + { sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed); } } diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index b6fcc13321ee..6c5732f497f8 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -3,10 +3,10 @@ use rustc_ast::ast; use rustc_errors::codes::*; use rustc_hir::limit::Limit; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol}; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`#[cfg_attr]` does not expand to any attributes")] pub(crate) struct CfgAttrNoAttributes; @@ -17,6 +17,59 @@ pub(crate) struct NoSyntaxVarsExprRepeat { #[primary_span] pub span: Span, + #[subdiagnostic] + pub typo_repeatable: Option, + #[subdiagnostic] + pub typo_unrepeatable: Option, + #[subdiagnostic] + pub typo_unrepeatable_label: Option, + #[subdiagnostic] + pub var_no_typo: Option, + #[subdiagnostic] + pub no_repeatable_var: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "there's a macro metavariable with a similar name", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct VarTypoSuggestionRepeatable { + #[suggestion_part(code = "{name}")] + pub span: Span, + pub name: Symbol, +} + +#[derive(Subdiagnostic)] +#[label("argument not found")] +pub(crate) struct VarTypoSuggestionUnrepeatable { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +#[label("this similarly named macro metavariable is unrepeatable")] +pub(crate) struct VarTypoSuggestionUnrepeatableLabel { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +#[label("expected a repeatable metavariable: {$msg}")] +pub(crate) struct VarNoTypo { + #[primary_span] + pub span: Span, + pub msg: String, +} + +#[derive(Subdiagnostic)] +#[label( + "this macro metavariable is not repeatable and there are no other repeatable metavariables" +)] +pub(crate) struct NoRepeatableVar { + #[primary_span] + pub span: Span, } #[derive(Diagnostic)] @@ -41,7 +94,7 @@ pub(crate) struct MacroVarStillRepeating { pub ident: MacroRulesNormalizedIdent, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("variable `{$ident}` is still repeating at this depth")] pub(crate) struct MetaVarStillRepeatingLint { #[label("expected repetition")] @@ -49,7 +102,7 @@ pub(crate) struct MetaVarStillRepeatingLint { pub ident: MacroRulesNormalizedIdent, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("meta-variable repeats with different Kleene operator")] pub(crate) struct MetaVariableWrongOperator { #[label("expected repetition")] @@ -66,7 +119,7 @@ pub(crate) struct MetaVarsDifSeqMatchers { pub msg: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unknown macro variable `{$name}`")] pub(crate) struct UnknownMacroVariable { pub name: MacroRulesNormalizedIdent, @@ -130,34 +183,6 @@ pub(crate) struct RecursionLimitReached { pub crate_name: Symbol, } -#[derive(Diagnostic)] -#[diag("malformed `feature` attribute input", code = E0556)] -pub(crate) struct MalformedFeatureAttribute { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub help: MalformedFeatureAttributeHelp, -} - -#[derive(Subdiagnostic)] -pub(crate) enum MalformedFeatureAttributeHelp { - #[label("expected just one word")] - Label { - #[primary_span] - span: Span, - }, - #[suggestion( - "expected just one word", - code = "{suggestion}", - applicability = "maybe-incorrect" - )] - Suggestion { - #[primary_span] - span: Span, - suggestion: Symbol, - }, -} - #[derive(Diagnostic)] #[diag("removing an expression is not supported in this position")] pub(crate) struct RemoveExprNotSupported { @@ -366,7 +391,7 @@ pub(crate) struct DuplicateMatcherBinding { pub prev: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("duplicate matcher binding")] pub(crate) struct DuplicateMatcherBindingLint { #[label("duplicate binding")] @@ -446,18 +471,6 @@ pub(crate) struct GlobDelegationTraitlessQpath { pub span: Span, } -// This used to be the `proc_macro_back_compat` lint (#83125). It was later -// turned into a hard error. -#[derive(Diagnostic)] -#[diag("using an old version of `{$crate_name}`")] -#[note( - "older versions of the `{$crate_name}` crate no longer compile; please update to `{$crate_name}` v{$fixed_version}, or switch to one of the `{$crate_name}` alternatives" -)] -pub(crate) struct ProcMacroBackCompat { - pub crate_name: String, - pub fixed_version: String, -} - pub(crate) use metavar_exprs::*; mod metavar_exprs { use super::*; @@ -556,7 +569,7 @@ pub(crate) struct MacroArgsBadDelimSugg { pub close: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused doc comment")] #[help( "to document an item produced by a macro, the macro must produce the documentation as part of its expansion" @@ -566,7 +579,7 @@ pub(crate) struct MacroCallUnusedDocComment { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro" )] @@ -580,7 +593,7 @@ pub(crate) struct OrPatternsBackCompat { pub suggestion: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("trailing semicolon in macro used in expression position")] pub(crate) struct TrailingMacro { #[note("macro invocations at the end of a block are treated as expressions")] @@ -591,7 +604,7 @@ pub(crate) struct TrailingMacro { pub name: Ident, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused attribute `{$attr_name}`")] pub(crate) struct UnusedBuiltinAttribute { #[note( diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 96f17418b8d2..d2ac7103bccb 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,7 +1,6 @@ // tidy-alphabetical-start #![allow(internal_features)] #![feature(associated_type_defaults)] -#![feature(if_let_guard)] #![feature(macro_metavar_expr)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_internals)] diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index 3082c881a7a6..3bed1f7b8d23 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -21,14 +21,14 @@ /// Contains the sub-token-trees of a "delimited" token tree such as `(a b c)`. /// The delimiters are not represented explicitly in the `tts` vector. #[derive(PartialEq, Encodable, Decodable, Debug)] -struct Delimited { +pub(crate) struct Delimited { delim: Delimiter, /// FIXME: #67062 has details about why this is sub-optimal. tts: Vec, } #[derive(PartialEq, Encodable, Decodable, Debug)] -struct SequenceRepetition { +pub(crate) struct SequenceRepetition { /// The sequence of token trees tts: Vec, /// The optional separator @@ -66,7 +66,7 @@ pub(crate) enum KleeneOp { /// Similar to `tokenstream::TokenTree`, except that `Sequence`, `MetaVar`, `MetaVarDecl`, and /// `MetaVarExpr` are "first-class" token trees. Useful for parsing macros. #[derive(Debug, PartialEq, Encodable, Decodable)] -enum TokenTree { +pub(crate) enum TokenTree { /// A token. Unlike `tokenstream::TokenTree::Token` this lacks a `Spacing`. /// See the comments about `Spacing` in the `transcribe` function. Token(Token), @@ -118,4 +118,24 @@ fn span(&self) -> Span { fn token(kind: TokenKind, span: Span) -> TokenTree { TokenTree::Token(Token::new(kind, span)) } + + // Used only in diagnostics. + fn meta_vars(&self, vars: &mut Vec) { + match self { + Self::Token(_) => {} + Self::MetaVar(_, ident) => vars.push(*ident), + Self::MetaVarDecl { name, .. } => vars.push(*name), + Self::Delimited(_, _, delimited) => { + for tt in &delimited.tts { + tt.meta_vars(vars); + } + } + Self::Sequence(_, sequence) => { + for tt in &sequence.tts { + tt.meta_vars(vars); + } + } + Self::MetaVarExpr(_, _) => {} + } + } } diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index ab8e059b7b77..9c327c26849e 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -388,6 +388,15 @@ pub(crate) enum NamedMatch { MatchedSingle(ParseNtResult), } +impl NamedMatch { + pub(super) fn is_repeatable(&self) -> bool { + match self { + NamedMatch::MatchedSeq(_) => true, + NamedMatch::MatchedSingle(_) => false, + } + } +} + /// Performs a token equality check, ignoring syntax context (that is, an unhygienic comparison) fn token_name_eq(t1: &Token, t2: &Token) -> bool { if let (Some((ident1, is_raw1)), Some((ident2, is_raw2))) = (t1.ident(), t2.ident()) { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 7cd96211de50..7ff49e040f6f 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -14,7 +14,6 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; use rustc_feature::Features; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::MacroKinds; use rustc_hir::find_attr; use rustc_lint_defs::builtin::{ @@ -819,7 +818,7 @@ pub fn compile_declarative_macro( } assert!(!kinds.is_empty()); - let transparency = find_attr!(attrs, AttributeKind::RustcMacroTransparency(x) => *x) + let transparency = find_attr!(attrs, RustcMacroTransparency(x) => *x) .unwrap_or(Transparency::fallback(macro_rules)); if let Some(guar) = guar { diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index d53d180a4ab9..09f006c3de57 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -6,7 +6,7 @@ use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast::{ExprKind, StmtKind, TyKind, UnOp}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Diag, DiagCtxtHandle, PResult, pluralize}; +use rustc_errors::{Diag, DiagCtxtHandle, PResult, listify, pluralize}; use rustc_parse::lexer::nfc_normalize; use rustc_parse::parser::ParseNtResult; use rustc_session::parse::ParseSess; @@ -18,7 +18,8 @@ use crate::errors::{ CountRepetitionMisplaced, MacroVarStillRepeating, MetaVarsDifSeqMatchers, MustRepeatOnce, - MveUnrecognizedVar, NoSyntaxVarsExprRepeat, + MveUnrecognizedVar, NoRepeatableVar, NoSyntaxVarsExprRepeat, VarNoTypo, + VarTypoSuggestionRepeatable, VarTypoSuggestionUnrepeatable, VarTypoSuggestionUnrepeatableLabel, }; use crate::mbe::macro_parser::NamedMatch; use crate::mbe::macro_parser::NamedMatch::*; @@ -246,7 +247,7 @@ pub(super) fn transcribe<'a>( match tree { // Replace the sequence with its expansion. seq @ mbe::TokenTree::Sequence(_, seq_rep) => { - transcribe_sequence(&mut tscx, seq, seq_rep)?; + transcribe_sequence(&mut tscx, seq, seq_rep, interp)?; } // Replace the meta-var with the matched token tree from the invocation. @@ -293,6 +294,8 @@ fn transcribe_sequence<'tx, 'itp>( tscx: &mut TranscrCtx<'tx, 'itp>, seq: &mbe::TokenTree, seq_rep: &'itp mbe::SequenceRepetition, + // Used only for better diagnostics in the face of typos. + interp: &FxHashMap, ) -> PResult<'tx, ()> { let dcx = tscx.psess.dcx(); @@ -301,7 +304,66 @@ fn transcribe_sequence<'tx, 'itp>( // macro writer has made a mistake. match lockstep_iter_size(seq, tscx.interp, &tscx.repeats) { LockstepIterSize::Unconstrained => { - return Err(dcx.create_err(NoSyntaxVarsExprRepeat { span: seq.span() })); + let mut repeatables = Vec::new(); + let mut non_repeatables = Vec::new(); + + #[allow(rustc::potential_query_instability)] + for (name, matcher) in interp.iter() { + if matcher.is_repeatable() { + repeatables.push(name); + } else { + non_repeatables.push(name); + } + } + + let repeatable_names: Vec = + repeatables.iter().map(|&name| name.symbol()).collect(); + let non_repeatable_names: Vec = + non_repeatables.iter().map(|&name| name.symbol()).collect(); + let mut meta_vars = vec![]; + seq.meta_vars(&mut meta_vars); + let mut typo_repeatable = None; + let mut typo_unrepeatable = None; + let mut typo_unrepeatable_label = None; + let mut var_no_typo = None; + let mut no_repeatable_var = None; + + for ident in meta_vars { + if let Some(name) = rustc_span::edit_distance::find_best_match_for_name( + &repeatable_names[..], + ident.name, + None, + ) { + typo_repeatable = Some(VarTypoSuggestionRepeatable { span: ident.span, name }); + } else if let Some(name) = rustc_span::edit_distance::find_best_match_for_name( + &non_repeatable_names[..], + ident.name, + None, + ) { + typo_unrepeatable = Some(VarTypoSuggestionUnrepeatable { span: ident.span }); + if let Some(&orig_ident) = non_repeatables.iter().find(|n| n.symbol() == name) { + typo_unrepeatable_label = Some(VarTypoSuggestionUnrepeatableLabel { + span: orig_ident.ident().span, + }); + } + } else { + if !repeatable_names.is_empty() + && let Some(msg) = listify(&repeatable_names, |s| format!("`${s}`")) + { + var_no_typo = Some(VarNoTypo { span: ident.span, msg }); + } else { + no_repeatable_var = Some(NoRepeatableVar { span: ident.span }); + } + } + } + return Err(dcx.create_err(NoSyntaxVarsExprRepeat { + span: seq.span(), + typo_unrepeatable, + typo_repeatable, + typo_unrepeatable_label, + var_no_typo, + no_repeatable_var, + })); } LockstepIterSize::Contradiction(msg) => { diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index e67855700813..5b2482f5c1db 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -1,12 +1,13 @@ +use rustc_ast as ast; use rustc_ast::tokenstream::TokenStream; use rustc_errors::ErrorGuaranteed; use rustc_middle::ty::{self, TyCtxt}; use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser}; +use rustc_proc_macro as pm; use rustc_session::Session; use rustc_session::config::ProcMacroExecutionStrategy; use rustc_span::profiling::SpannedEventArgRecorder; use rustc_span::{LocalExpnId, Span}; -use {rustc_ast as ast, rustc_proc_macro as pm}; use crate::base::{self, *}; use crate::{errors, proc_macro_server}; @@ -105,11 +106,6 @@ fn expand( // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`) let is_stmt = matches!(item, Annotatable::Stmt(..)); - // We used to have an alternative behaviour for crates that needed it. - // We had a lint for a long time, but now we just emit a hard error. - // Eventually we might remove the special case hard error check - // altogether. See #73345. - crate::base::ann_pretty_printing_compatibility_hack(&item, &ecx.sess.psess); let input = item.to_tokens(); let invoc_id = ecx.current_expansion.id; diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 947b8a6e3e5e..839e68d0bb43 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -10,7 +10,7 @@ use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan}; 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, unwrap_or_emit_fatal}; +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, }; @@ -103,8 +103,8 @@ fn to_internal(self) -> token::LitKind { } } -impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec> { - fn from_internal((stream, rustc): (TokenStream, &mut Rustc<'_, '_>)) -> Self { +impl FromInternal for Vec> { + fn from_internal(stream: TokenStream) -> Self { use rustc_ast::token::*; // Estimate the capacity as `stream.len()` rounded up to the next power @@ -115,22 +115,6 @@ fn from_internal((stream, rustc): (TokenStream, &mut Rustc<'_, '_>)) -> Self { while let Some(tree) = iter.next() { let (Token { kind, span }, joint) = match tree.clone() { tokenstream::TokenTree::Delimited(span, _, mut delim, mut stream) => { - // We used to have an alternative behaviour for crates that - // needed it: a hack used to pass AST fragments to - // attribute and derive macros as a single nonterminal - // token instead of a token stream. Such token needs to be - // "unwrapped" and not represented as a delimited group. We - // had a lint for a long time, but now we just emit a hard - // error. Eventually we might remove the special case hard - // error check altogether. See #73345. - if let Delimiter::Invisible(InvisibleOrigin::MetaVar(kind)) = delim { - crate::base::stream_pretty_printing_compatibility_hack( - kind, - &stream, - rustc.psess(), - ); - } - // In `mk_delimited` we avoid nesting invisible delimited // of the same `MetaVarKind`. Here we do the same but // ignore the `MetaVarKind` because it is discarded when we @@ -431,6 +415,13 @@ fn to_internal(self) -> rustc_errors::Level { } } +fn cancel_diags_into_string(diags: Vec>) -> String { + let mut messages = diags.into_iter().flat_map(Diag::cancel_into_message); + let msg = messages.next().expect("no diagnostic has a message"); + messages.for_each(|_| ()); // consume iterator to cancel the remaining diagnostics + msg +} + pub(crate) struct Rustc<'a, 'b> { ecx: &'a mut ExtCtxt<'b>, def_site: Span, @@ -494,35 +485,32 @@ fn track_path(&mut self, path: &str) { self.psess().file_depinfo.borrow_mut().insert(Symbol::intern(path)); } - fn literal_from_str(&mut self, s: &str) -> Result, ()> { + fn literal_from_str(&mut self, s: &str) -> Result, String> { let name = FileName::proc_macro_source_code(s); - let mut parser = unwrap_or_emit_fatal(new_parser_from_source_str( - self.psess(), - name, - s.to_owned(), - StripTokens::Nothing, - )); + let mut parser = + new_parser_from_source_str(self.psess(), name, s.to_owned(), StripTokens::Nothing) + .map_err(cancel_diags_into_string)?; let first_span = parser.token.span.data(); let minus_present = parser.eat(exp!(Minus)); let lit_span = parser.token.span.data(); let token::Literal(mut lit) = parser.token.kind else { - return Err(()); + return Err("not a literal".to_string()); }; // Check no comment or whitespace surrounding the (possibly negative) // literal, or more tokens after it. if (lit_span.hi.0 - first_span.lo.0) as usize != s.len() { - return Err(()); + return Err("comment or whitespace around literal".to_string()); } if minus_present { // If minus is present, check no comment or whitespace in between it // and the literal token. if first_span.hi.0 != lit_span.lo.0 { - return Err(()); + return Err("comment or whitespace after minus".to_string()); } // Check literal is a kind we allow to be negated in a proc macro token. @@ -536,7 +524,9 @@ fn literal_from_str(&mut self, s: &str) -> Result return Err(()), + | token::LitKind::Err(_) => { + return Err("non-numeric literal may not be negated".to_string()); + } token::LitKind::Integer | token::LitKind::Float => {} } @@ -576,13 +566,14 @@ fn ts_is_empty(&mut self, stream: &Self::TokenStream) -> bool { stream.is_empty() } - fn ts_from_str(&mut self, src: &str) -> Self::TokenStream { - unwrap_or_emit_fatal(source_str_to_stream( + fn ts_from_str(&mut self, src: &str) -> Result { + source_str_to_stream( self.psess(), FileName::proc_macro_source_code(src), src.to_string(), Some(self.call_site), - )) + ) + .map_err(cancel_diags_into_string) } fn ts_to_string(&mut self, stream: &Self::TokenStream) -> String { @@ -687,7 +678,7 @@ fn ts_into_trees( &mut self, stream: Self::TokenStream, ) -> Vec> { - FromInternal::from_internal((stream, self)) + FromInternal::from_internal(stream) } fn span_debug(&mut self, span: Self::Span) -> String { diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 43032bf938c0..54a935d13c8c 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -25,9 +25,6 @@ macro_rules! declare_features { // feature-group-start: for testing purposes // ------------------------------------------------------------------------- - /// A temporary feature gate used to enable parser extensions needed - /// to bootstrap fix for #5723. - (accepted, issue_5723_bootstrap, "1.0.0", None), /// These are used to test this portion of the compiler, /// they don't actually mean anything. (accepted, test_accepted_feature, "1.0.0", None), @@ -105,6 +102,8 @@ macro_rules! declare_features { (accepted, cfg_doctest, "1.40.0", Some(62210)), /// Enables `#[cfg(panic = "...")]` config key. (accepted, cfg_panic, "1.60.0", Some(77443)), + /// Provides a native way to easily manage multiple conditional flags without having to rewrite each clause multiple times. + (accepted, cfg_select, "1.95.0", Some(115585)), /// Allows `cfg(target_abi = "...")`. (accepted, cfg_target_abi, "1.78.0", Some(80970)), /// Allows `cfg(target_feature = "...")`. @@ -243,6 +242,8 @@ macro_rules! declare_features { (accepted, i128_type, "1.26.0", Some(35118)), /// Allows the use of `if let` expressions. (accepted, if_let, "1.0.0", None), + /// Allows `if let` guard in match arms. + (accepted, if_let_guard, "1.95.0", Some(51114)), /// Rescoping temporaries in `if let` to align with Rust 2024. (accepted, if_let_rescope, "1.84.0", Some(124085)), /// Allows top level or-patterns (`p | q`) in `if let` and `while let`. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 2c2dae0fef52..3a2f548902d1 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -473,11 +473,6 @@ pub struct BuiltinAttribute { ), FutureWarnFollowing, EncodeCrossCrate::No, ), - // FIXME(Centril): This can be used on stable but shouldn't. - ungated!( - reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing, - EncodeCrossCrate::No, - ), // Macros: ungated!( @@ -831,6 +826,13 @@ pub struct BuiltinAttribute { EncodeCrossCrate::Yes, custom_test_frameworks, "custom test frameworks are an unstable feature", ), + + gated!( + reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing, + EncodeCrossCrate::No, custom_test_frameworks, + "custom test frameworks are an unstable feature", + ), + // RFC #1268 gated!( marker, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, @@ -1208,7 +1210,7 @@ pub struct BuiltinAttribute { rustc_intrinsic_const_stable_indirect, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, "this is an internal implementation detail", ), - gated!( + rustc_attr!( rustc_allow_const_fn_unstable, Normal, template!(Word, List: &["feat1, feat2, ..."]), DuplicatesOk, EncodeCrossCrate::No, "rustc_allow_const_fn_unstable side-steps feature gating and stability checks" @@ -1330,7 +1332,7 @@ pub struct BuiltinAttribute { safety: AttributeSafety::Normal, template: template!(NameValueStr: "name"), duplicates: ErrorFollowing, - gate: Gated{ + gate: Gated { feature: sym::rustc_attrs, message: "use of an internal attribute", check: Features::rustc_attrs, @@ -1419,7 +1421,7 @@ pub struct BuiltinAttribute { rustc_attr!(TEST, rustc_effective_visibility, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes), rustc_attr!( - TEST, rustc_outlives, Normal, template!(Word), + TEST, rustc_dump_inferred_outlives, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( @@ -1439,11 +1441,11 @@ pub struct BuiltinAttribute { WarnFollowing, EncodeCrossCrate::Yes ), rustc_attr!( - TEST, rustc_variance, Normal, template!(Word), + TEST, rustc_dump_variances, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( - TEST, rustc_variance_of_opaques, Normal, template!(Word), + TEST, rustc_dump_variances_of_opaques, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( @@ -1531,7 +1533,7 @@ pub struct BuiltinAttribute { WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( - TEST, rustc_object_lifetime_default, Normal, template!(Word), + TEST, rustc_dump_object_lifetime_defaults, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 619726f0d5d8..9d046bdef1cf 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -137,5 +137,5 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option = AtomicRef::new(&(default_track_feature as _)); + #[derive(PartialEq)] enum FeatureStatus { Default, @@ -64,8 +71,6 @@ pub struct EnabledLibFeature { } impl Features { - /// `since` should be set for stable features that are nevertheless enabled with a `#[feature]` - /// attribute, indicating since when they are stable. pub fn set_enabled_lang_feature(&mut self, lang_feat: EnabledLangFeature) { self.enabled_lang_features.push(lang_feat); self.enabled_features.insert(lang_feat.gate_name); @@ -105,7 +110,12 @@ pub fn enabled_features_iter_stable_order( /// Is the given feature enabled (via `#[feature(...)]`)? pub fn enabled(&self, feature: Symbol) -> bool { - self.enabled_features.contains(&feature) + if self.enabled_features.contains(&feature) { + TRACK_FEATURE(feature); + true + } else { + false + } } } @@ -126,7 +136,7 @@ macro_rules! declare_features { impl Features { $( pub fn $feature(&self) -> bool { - self.enabled_features.contains(&sym::$feature) + self.enabled(sym::$feature) } )* @@ -223,6 +233,8 @@ pub fn internal(&self, feature: Symbol) -> bool { (internal, custom_mir, "1.65.0", None), /// Implementation details of externally implementable items (internal, eii_internals, "1.94.0", None), + /// Implementation details of field representing types. + (internal, field_representing_type_raw, "CURRENT_RUSTC_VERSION", None), /// Outputs useful `assert!` messages (unstable, generic_assert, "1.63.0", None), /// Allows using the #[rustc_intrinsic] attribute. @@ -289,10 +301,6 @@ pub fn internal(&self, feature: Symbol) -> bool { (internal, panic_runtime, "1.10.0", Some(32837)), /// Allows using pattern types. (internal, pattern_types, "1.79.0", Some(123646)), - /// Allows using `#[rustc_allow_const_fn_unstable]`. - /// This is an attribute on `const fn` for the same - /// purpose as `#[allow_internal_unstable]`. - (internal, rustc_allow_const_fn_unstable, "1.49.0", Some(69399)), /// Allows using compiler's own crates. (unstable, rustc_private, "1.0.0", Some(27812)), /// Allows using internal rustdoc features like `doc(keyword)`. @@ -374,7 +382,7 @@ pub fn internal(&self, feature: Symbol) -> bool { /// Allows `async` trait bound modifier. (unstable, async_trait_bounds, "1.85.0", Some(62290)), /// Target features on avr. - (unstable, avr_target_feature, "CURRENT_RUSTC_VERSION", Some(146889)), + (unstable, avr_target_feature, "1.95.0", Some(146889)), /// Allows using Intel AVX10 target features and intrinsics (unstable, avx10_target_feature, "1.88.0", Some(138843)), /// Target features on bpf. @@ -394,8 +402,6 @@ pub fn internal(&self, feature: Symbol) -> bool { (unstable, cfg_sanitize, "1.41.0", Some(39699)), /// Allows `cfg(sanitizer_cfi_generalize_pointers)` and `cfg(sanitizer_cfi_normalize_integers)`. (unstable, cfg_sanitizer_cfi, "1.77.0", Some(89653)), - /// Provides a native way to easily manage multiple conditional flags without having to rewrite each clause multiple times. - (unstable, cfg_select, "CURRENT_RUSTC_VERSION", Some(115585)), /// Allows `cfg(target(abi = "..."))`. (unstable, cfg_target_compact, "1.63.0", Some(96901)), /// Allows `cfg(target_has_atomic_load_store = "...")`. @@ -419,7 +425,9 @@ pub fn internal(&self, feature: Symbol) -> bool { /// Allows `async {}` expressions in const contexts. (unstable, const_async_blocks, "1.53.0", Some(85368)), /// Allows `const { ... }` as a shorthand for `const _: () = const { ... };` for module items. - (unstable, const_block_items, "CURRENT_RUSTC_VERSION", Some(149226)), + (unstable, const_block_items, "1.95.0", Some(149226)), + /// Allows defining and calling c-variadic functions in const contexts. + (unstable, const_c_variadic, "1.95.0", Some(151787)), /// Allows `const || {}` closures in const contexts. (incomplete, const_closures, "1.68.0", Some(106003)), /// Allows using `[const] Destruct` bounds and calling drop impls in const contexts. @@ -492,6 +500,10 @@ pub fn internal(&self, feature: Symbol) -> bool { (unstable, ffi_const, "1.45.0", Some(58328)), /// Allows the use of `#[ffi_pure]` on foreign functions. (unstable, ffi_pure, "1.45.0", Some(58329)), + /// Experimental field projections. + (incomplete, field_projections, "CURRENT_RUSTC_VERSION", Some(145383)), + /// Allows marking trait functions as `final` to prevent overriding impls + (unstable, final_associated_functions, "1.95.0", Some(131179)), /// Controlling the behavior of fmt::Debug (unstable, fmt_debug, "1.82.0", Some(129709)), /// Allows using `#[align(...)]` on function items @@ -520,8 +532,8 @@ pub fn internal(&self, feature: Symbol) -> bool { (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)), /// Target features on hexagon. (unstable, hexagon_target_feature, "1.27.0", Some(150250)), - /// Allows `if let` guard in match arms. - (unstable, if_let_guard, "1.47.0", Some(51114)), + /// Allows `impl(crate) trait Foo` restrictions. + (incomplete, impl_restriction, "CURRENT_RUSTC_VERSION", Some(105077)), /// Allows `impl Trait` to be used inside associated types (RFC 2515). (unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)), /// Allows `impl Trait` in bindings (`let`). @@ -560,7 +572,7 @@ pub fn internal(&self, feature: Symbol) -> bool { /// Allows `#[marker]` on certain traits allowing overlapping implementations. (unstable, marker_trait_attr, "1.30.0", Some(29864)), /// Enable mgca `type const` syntax before expansion. - (incomplete, mgca_type_const_syntax, "CURRENT_RUSTC_VERSION", Some(132980)), + (incomplete, mgca_type_const_syntax, "1.95.0", Some(132980)), /// Enables the generic const args MVP (only bare paths, not arbitrary computation). (incomplete, min_generic_const_args, "1.84.0", Some(132980)), /// A minimal, sound subset of specialization intended to be used by the @@ -606,7 +618,7 @@ pub fn internal(&self, feature: Symbol) -> bool { /// Allows using fields with slice type in offset_of! (unstable, offset_of_slice, "1.81.0", Some(126151)), /// Allows using generics in more complex const expressions, based on definitional equality. - (unstable, opaque_generic_const_args, "CURRENT_RUSTC_VERSION", Some(151972)), + (unstable, opaque_generic_const_args, "1.95.0", Some(151972)), /// Allows using `#[optimize(X)]`. (unstable, optimize_attribute, "1.34.0", Some(54882)), /// Allows specifying nop padding on functions for dynamic patching. @@ -641,7 +653,7 @@ pub fn internal(&self, feature: Symbol) -> bool { /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), /// Allows `extern "rust-preserve-none"`. - (unstable, rust_preserve_none_cc, "CURRENT_RUSTC_VERSION", Some(151401)), + (unstable, rust_preserve_none_cc, "1.95.0", Some(151401)), /// Target features on s390x. (unstable, s390x_target_feature, "1.82.0", Some(150259)), /// Allows the use of the `sanitize` attribute. @@ -777,8 +789,9 @@ struct FeatureUsage { } } -/// Some features are not allowed to be used together at the same time, if -/// the two are present, produce an error. +/// Some features are not allowed to be used together at the same time. +/// +/// If the two are present, produce an error. pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[ // Experimental match ergonomics rulesets are incompatible with each other, to simplify the // boolean logic required to tell which typing rules to use. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 3798a586c051..e8476c3d8c73 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1,9 +1,12 @@ use std::borrow::Cow; +use std::fmt; use std::path::PathBuf; pub use ReprAttr::*; use rustc_abi::Align; pub use rustc_ast::attr::data_structures::*; +use rustc_ast::expand::autodiff_attrs::{DiffActivity, DiffMode}; +use rustc_ast::expand::typetree::TypeTree; use rustc_ast::token::DocFragmentKind; use rustc_ast::{AttrStyle, Path, ast}; use rustc_data_structures::fx::FxIndexMap; @@ -16,6 +19,7 @@ pub use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; +use crate::attrs::diagnostic::*; use crate::attrs::pretty_printing::PrintAttribute; use crate::limit::Limit; use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability}; @@ -793,6 +797,103 @@ pub struct RustcCleanQueries { pub span: Span, } +#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct RustcAutodiff { + /// Conceptually either forward or reverse mode AD, as described in various autodiff papers and + /// e.g. in the [JAX + /// Documentation](https://jax.readthedocs.io/en/latest/_tutorials/advanced-autodiff.html#how-it-s-made-two-foundational-autodiff-functions). + pub mode: DiffMode, + /// A user-provided, batching width. If not given, we will default to 1 (no batching). + /// Calling a differentiated, non-batched function through a loop 100 times is equivalent to: + /// - Calling the function 50 times with a batch size of 2 + /// - Calling the function 25 times with a batch size of 4, + /// etc. A batched function takes more (or longer) arguments, and might be able to benefit from + /// cache locality, better re-usal of primal values, and other optimizations. + /// We will (before LLVM's vectorizer runs) just generate most LLVM-IR instructions `width` + /// times, so this massively increases code size. As such, values like 1024 are unlikely to + /// work. We should consider limiting this to u8 or u16, but will leave it at u32 for + /// experiments for now and focus on documenting the implications of a large width. + pub width: u32, + pub input_activity: ThinVec, + pub ret_activity: DiffActivity, +} + +impl RustcAutodiff { + pub fn has_primal_ret(&self) -> bool { + matches!(self.ret_activity, DiffActivity::Active | DiffActivity::Dual) + } +} + +impl RustcAutodiff { + pub fn has_ret_activity(&self) -> bool { + self.ret_activity != DiffActivity::None + } + pub fn has_active_only_ret(&self) -> bool { + self.ret_activity == DiffActivity::ActiveOnly + } + + pub fn error() -> Self { + RustcAutodiff { + mode: DiffMode::Error, + width: 0, + ret_activity: DiffActivity::None, + input_activity: ThinVec::new(), + } + } + + pub fn source() -> Self { + RustcAutodiff { + mode: DiffMode::Source, + width: 0, + ret_activity: DiffActivity::None, + input_activity: ThinVec::new(), + } + } + + pub fn is_active(&self) -> bool { + self.mode != DiffMode::Error + } + + pub fn is_source(&self) -> bool { + self.mode == DiffMode::Source + } + pub fn apply_autodiff(&self) -> bool { + !matches!(self.mode, DiffMode::Error | DiffMode::Source) + } + + pub fn into_item( + self, + source: String, + target: String, + inputs: Vec, + output: TypeTree, + ) -> AutoDiffItem { + AutoDiffItem { source, target, inputs, output, attrs: self } + } +} + +/// We generate one of these structs for each `#[autodiff(...)]` attribute. +#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +pub struct AutoDiffItem { + /// The name of the function getting differentiated + pub source: String, + /// The name of the function being generated + pub target: String, + pub attrs: RustcAutodiff, + pub inputs: Vec, + pub output: TypeTree, +} + +impl fmt::Display for AutoDiffItem { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Differentiating {} -> {}", self.source, self.target)?; + write!(f, " with attributes: {:?}", self.attrs)?; + write!(f, " with inputs: {:?}", self.inputs)?; + write!(f, " with output: {:?}", self.output) + } +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -845,10 +946,6 @@ pub struct RustcCleanQueries { #[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] pub enum AttributeKind { // tidy-alphabetical-start - /// Represents `#[align(N)]`. - // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity - Align { align: Align, span: Span }, - /// Represents `#[allow_internal_unsafe]`. AllowInternalUnsafe(Span), @@ -865,7 +962,9 @@ pub enum AttributeKind { CfgTrace(ThinVec<(CfgEntry, Span)>), /// Represents `#[cfi_encoding]` - CfiEncoding { encoding: Symbol }, + CfiEncoding { + encoding: Symbol, + }, /// Represents `#[cold]`. Cold(Span), @@ -886,7 +985,11 @@ pub enum AttributeKind { Coverage(Span, CoverageAttrKind), /// Represents `#[crate_name = ...]` - CrateName { name: Symbol, name_span: Span, attr_span: Span }, + CrateName { + name: Symbol, + name_span: Span, + attr_span: Span, + }, /// Represents `#![crate_type = ...]` CrateType(ThinVec), @@ -897,11 +1000,19 @@ pub enum AttributeKind { /// Represents `#[debugger_visualizer]`. DebuggerVisualizer(ThinVec), + /// Represents `#![default_lib_allocator]` + DefaultLibAllocator, + /// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute). - Deprecation { deprecation: Deprecation, span: Span }, + Deprecated { + deprecation: Deprecation, + span: Span, + }, /// Represents `#[diagnostic::do_not_recommend]`. - DoNotRecommend { attr_span: Span }, + DoNotRecommend { + attr_span: Span, + }, /// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html). /// Represents all other uses of the [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html) @@ -910,14 +1021,16 @@ pub enum AttributeKind { /// Represents specifically [`#[doc = "..."]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html). /// i.e. doc comments. - DocComment { style: AttrStyle, kind: DocFragmentKind, span: Span, comment: Symbol }, + DocComment { + style: AttrStyle, + kind: DocFragmentKind, + span: Span, + comment: Symbol, + }, /// Implementation detail of `#[eii]` EiiDeclaration(EiiDecl), - /// Implementation detail of `#[eii]` - EiiForeignItem, - /// Implementation detail of `#[eii]` EiiImpls(ThinVec), @@ -932,6 +1045,9 @@ pub enum AttributeKind { /// Represents `#[export_stable]`. ExportStable, + /// Represents `#[feature(...)]` + Feature(ThinVec, Span), + /// Represents `#[ffi_const]`. FfiConst(Span), @@ -961,13 +1077,22 @@ pub enum AttributeKind { Link(ThinVec, Span), /// Represents `#[link_name]`. - LinkName { name: Symbol, span: Span }, + LinkName { + name: Symbol, + span: Span, + }, /// Represents `#[link_ordinal]`. - LinkOrdinal { ordinal: u16, span: Span }, + LinkOrdinal { + ordinal: u16, + span: Span, + }, /// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute) - LinkSection { name: Symbol, span: Span }, + LinkSection { + name: Symbol, + span: Span, + }, /// Represents `#[linkage]`. Linkage(Linkage, Span), @@ -979,10 +1104,16 @@ pub enum AttributeKind { MacroEscape(Span), /// Represents [`#[macro_export]`](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.scope.path). - MacroExport { span: Span, local_inner_macros: bool }, + MacroExport { + span: Span, + local_inner_macros: bool, + }, /// Represents `#[macro_use]`. - MacroUse { span: Span, arguments: MacroUseArgs }, + MacroUse { + span: Span, + arguments: MacroUseArgs, + }, /// Represents `#[marker]`. Marker(Span), @@ -991,10 +1122,16 @@ pub enum AttributeKind { MayDangle(Span), /// Represents `#[move_size_limit]` - MoveSizeLimit { attr_span: Span, limit_span: Span, limit: Limit }, + MoveSizeLimit { + attr_span: Span, + limit_span: Span, + limit: Limit, + }, /// Represents `#[must_not_suspend]` - MustNotSupend { reason: Option }, + MustNotSupend { + reason: Option, + }, /// Represents `#[must_use]`. MustUse { @@ -1036,6 +1173,20 @@ pub enum AttributeKind { /// Represents `#[non_exhaustive]` NonExhaustive(Span), + /// Represents `#[diagnostic::on_const]`. + OnConst { + span: Span, + /// None if the directive was malformed in some way. + directive: Option>, + }, + + /// Represents `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`. + OnUnimplemented { + span: Span, + /// None if the directive was malformed in some way. + directive: Option>, + }, + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), @@ -1043,13 +1194,20 @@ pub enum AttributeKind { PanicRuntime, /// Represents `#[patchable_function_entry]` - PatchableFunctionEntry { prefix: u8, entry: u8 }, + PatchableFunctionEntry { + prefix: u8, + entry: u8, + }, /// Represents `#[path]` Path(Symbol, Span), /// Represents `#[pattern_complexity_limit]` - PatternComplexityLimit { attr_span: Span, limit_span: Span, limit: Limit }, + PatternComplexityLimit { + attr_span: Span, + limit_span: Span, + limit: Limit, + }, /// Represents `#[pin_v2]` PinV2(Span), @@ -1067,22 +1225,46 @@ pub enum AttributeKind { ProcMacroAttribute(Span), /// Represents `#[proc_macro_derive]` - ProcMacroDerive { trait_name: Symbol, helper_attrs: ThinVec, span: Span }, + ProcMacroDerive { + trait_name: Symbol, + helper_attrs: ThinVec, + span: Span, + }, /// Represents `#[profiler_runtime]` ProfilerRuntime, /// Represents [`#[recursion_limit]`](https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute) - RecursionLimit { attr_span: Span, limit_span: Span, limit: Limit }, + RecursionLimit { + attr_span: Span, + limit_span: Span, + limit: Limit, + }, /// Represents `#[reexport_test_harness_main]` ReexportTestHarnessMain(Symbol), + /// Represents `#[register_tool]` + RegisterTool(ThinVec, Span), + /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). - Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span }, + Repr { + reprs: ThinVec<(ReprAttr, Span)>, + first_span: Span, + }, /// Represents `#[rustc_abi(..)]` - RustcAbi { attr_span: Span, kind: RustcAbiAttrKind }, + RustcAbi { + attr_span: Span, + kind: RustcAbiAttrKind, + }, + + /// Represents `#[align(N)]`. + // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity + RustcAlign { + align: Align, + span: Span, + }, /// Represents `#[rustc_allocator]` RustcAllocator, @@ -1091,7 +1273,9 @@ pub enum AttributeKind { RustcAllocatorZeroed, /// Represents `#[rustc_allocator_zeroed_variant]` - RustcAllocatorZeroedVariant { name: Symbol }, + RustcAllocatorZeroedVariant { + name: Symbol, + }, /// Represents `#[rustc_allow_const_fn_unstable]`. RustcAllowConstFnUnstable(ThinVec, Span), @@ -1102,6 +1286,9 @@ pub enum AttributeKind { /// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint). RustcAsPtr(Span), + /// Represents `#[rustc_autodiff]`. + RustcAutodiff(Option>), + /// Represents `#[rustc_default_body_unstable]`. RustcBodyStability { stability: DefaultBodyStability, @@ -1109,8 +1296,11 @@ pub enum AttributeKind { span: Span, }, /// Represents `#[rustc_builtin_macro]`. - RustcBuiltinMacro { builtin_name: Option, helper_attrs: ThinVec, span: Span }, - + RustcBuiltinMacro { + builtin_name: Option, + helper_attrs: ThinVec, + span: Span, + }, /// Represents `#[rustc_capture_analysis]` RustcCaptureAnalysis, @@ -1140,7 +1330,7 @@ pub enum AttributeKind { }, /// Represents `#[rustc_const_stable_indirect]`. - RustcConstStabilityIndirect, + RustcConstStableIndirect, /// Represents `#[rustc_conversion_suggestion]` RustcConversionSuggestion, @@ -1158,26 +1348,45 @@ pub enum AttributeKind { RustcDenyExplicitImpl(Span), /// Represents `#[rustc_deprecated_safe_2024]` - RustcDeprecatedSafe2024 { suggestion: Symbol }, - + RustcDeprecatedSafe2024 { + suggestion: Symbol, + }, /// Represents `#[rustc_diagnostic_item]` RustcDiagnosticItem(Symbol), + /// Represents `#[rustc_do_not_const_check]` + RustcDoNotConstCheck, + + /// Represents `#[rustc_doc_primitive = ...]` + RustcDocPrimitive(Span, Symbol), + /// Represents `#[rustc_dummy]`. RustcDummy, /// Represents `#[rustc_dump_def_parents]` RustcDumpDefParents, + /// Represents `#[rustc_dump_inferred_outlives]` + RustcDumpInferredOutlives, + /// Represents `#[rustc_dump_item_bounds]` RustcDumpItemBounds, + /// Represents `#[rustc_dump_object_lifetime_defaults]`. + RustcDumpObjectLifetimeDefaults, + /// Represents `#[rustc_dump_predicates]` RustcDumpPredicates, /// Represents `#[rustc_dump_user_args]` RustcDumpUserArgs, + /// Represents `#[rustc_dump_variances]` + RustcDumpVariances, + + /// Represents `#[rustc_dump_variances_of_opaques]` + RustcDumpVariancesOfOpaques, + /// Represents `#[rustc_dump_vtable]` RustcDumpVtable(Span), @@ -1187,10 +1396,12 @@ pub enum AttributeKind { /// Represents `#[rustc_effective_visibility]`. RustcEffectiveVisibility, + /// Implementation detail of `#[eii]` + RustcEiiForeignItem, + /// Represents `#[rustc_evaluate_where_clauses]` RustcEvaluateWhereClauses, - /// Represents `#[rustc_has_incoherent_inherent_impls]` RustcHasIncoherentInherentImpls, /// Represents `#[rustc_hidden_type_of_opaques]` @@ -1199,6 +1410,9 @@ pub enum AttributeKind { /// Represents `#[rustc_if_this_changed]` RustcIfThisChanged(Span, Option), + /// Represents `#[rustc_inherit_overflow_checks]` + RustcInheritOverflowChecks, + /// Represents `#[rustc_insignificant_dtor]` RustcInsignificantDtor, @@ -1218,10 +1432,15 @@ pub enum AttributeKind { RustcLayoutScalarValidRangeStart(Box, Span), /// Represents `#[rustc_legacy_const_generics]` - RustcLegacyConstGenerics { fn_indexes: ThinVec<(usize, Span)>, attr_span: Span }, + RustcLegacyConstGenerics { + fn_indexes: ThinVec<(usize, Span)>, + attr_span: Span, + }, /// Represents `#[rustc_lint_opt_deny_field_access]` - RustcLintOptDenyFieldAccess { lint_message: Symbol }, + RustcLintOptDenyFieldAccess { + lint_message: Symbol, + }, /// Represents `#[rustc_lint_opt_ty]` RustcLintOptTy, @@ -1242,10 +1461,13 @@ pub enum AttributeKind { RustcMir(ThinVec), /// Represents `#[rustc_must_implement_one_of]` - RustcMustImplementOneOf { attr_span: Span, fn_names: ThinVec }, + RustcMustImplementOneOf { + attr_span: Span, + fn_names: ThinVec, + }, /// Represents `#[rustc_never_returns_null_ptr]` - RustcNeverReturnsNullPointer, + RustcNeverReturnsNullPtr, /// Represents `#[rustc_never_type_options]`. RustcNeverTypeOptions { @@ -1265,24 +1487,27 @@ pub enum AttributeKind { /// Represents `#[rustc_non_const_trait_method]`. RustcNonConstTraitMethod, + /// Represents `#[rustc_nonnull_optimization_guaranteed]`. + RustcNonnullOptimizationGuaranteed, + /// Represents `#[rustc_nounwind]` RustcNounwind, /// Represents `#[rustc_objc_class]` - RustcObjcClass { classname: Symbol, span: Span }, + RustcObjcClass { + classname: Symbol, + span: Span, + }, /// Represents `#[rustc_objc_selector]` - RustcObjcSelector { methname: Symbol, span: Span }, - - /// Represents `#[rustc_object_lifetime_default]`. - RustcObjectLifetimeDefault, + RustcObjcSelector { + methname: Symbol, + span: Span, + }, /// Represents `#[rustc_offload_kernel]` RustcOffloadKernel, - /// Represents `#[rustc_outlives]` - RustcOutlives, - /// Represents `#[rustc_paren_sugar]`. RustcParenSugar(Span), @@ -1325,7 +1550,11 @@ pub enum AttributeKind { RustcSimdMonomorphizeLaneLimit(Limit), /// Represents `#[rustc_skip_during_method_dispatch]`. - RustcSkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span }, + RustcSkipDuringMethodDispatch { + array: bool, + boxed_slice: bool, + span: Span, + }, /// Represents `#[rustc_specialization_trait]`. RustcSpecializationTrait(Span), @@ -1351,12 +1580,6 @@ pub enum AttributeKind { /// Represents `#[rustc_unsafe_specialization_marker]`. RustcUnsafeSpecializationMarker(Span), - /// Represents `#[rustc_variance]` - RustcVariance, - - /// Represents `#[rustc_variance_of_opaques]` - RustcVarianceOfOpaques, - /// Represents `#[sanitize]` /// /// the on set and off set are distjoint since there's a third option: unset. @@ -1370,7 +1593,10 @@ pub enum AttributeKind { }, /// Represents `#[should_panic]` - ShouldPanic { reason: Option, span: Span }, + ShouldPanic { + reason: Option, + span: Span, + }, /// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`. Stability { @@ -1381,7 +1607,11 @@ pub enum AttributeKind { /// Represents `#[target_feature(enable = "...")]` and /// `#[unsafe(force_target_feature(enable = "...")]`. - TargetFeature { features: ThinVec<(Symbol, Span)>, attr_span: Span, was_forced: bool }, + TargetFeature { + features: ThinVec<(Symbol, Span)>, + attr_span: Span, + was_forced: bool, + }, /// Represents `#![test_runner(path)]` TestRunner(Path), @@ -1393,13 +1623,20 @@ pub enum AttributeKind { TrackCaller(Span), /// Represents `#[type_length_limit]` - TypeLengthLimit { attr_span: Span, limit_span: Span, limit: Limit }, + TypeLengthLimit { + attr_span: Span, + limit_span: Span, + limit: Limit, + }, /// Represents `#[unstable_feature_bound]`. UnstableFeatureBound(ThinVec<(Symbol, Span)>), /// Represents `#[used]` - Used { used_by: UsedBy, span: Span }, + Used { + used_by: UsedBy, + span: Span, + }, /// Represents `#[windows_subsystem]`. WindowsSubsystem(WindowsSubsystemKind, Span), diff --git a/compiler/rustc_hir/src/attrs/diagnostic.rs b/compiler/rustc_hir/src/attrs/diagnostic.rs new file mode 100644 index 000000000000..7c66b3f84469 --- /dev/null +++ b/compiler/rustc_hir/src/attrs/diagnostic.rs @@ -0,0 +1,476 @@ +//! Contains the data structures used by the diagnostic attribute family. +use std::fmt; +use std::fmt::Debug; + +pub use rustc_ast::attr::data_structures::*; +use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; +use rustc_span::{DesugaringKind, Span, Symbol, kw}; +use thin_vec::ThinVec; +use tracing::{debug, info}; + +use crate::attrs::PrintAttribute; + +#[derive(Clone, Default, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct Directive { + pub is_rustc_attr: bool, + pub condition: Option, + pub subcommands: ThinVec, + pub message: Option<(Span, FormatString)>, + pub label: Option<(Span, FormatString)>, + pub notes: ThinVec, + pub parent_label: Option, + pub append_const_msg: Option, +} + +impl Directive { + /// Visit all the generic arguments used in the attribute, to see whether they are actually a + /// generic of the item. If not then `visit` must issue a diagnostic. + /// + /// We can't check this while parsing the attribute because `rustc_attr_parsing` doesn't have + /// access to the item an attribute is on. Instead we later call this function in `check_attr`. + pub fn visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) { + if let Some(condition) = &self.condition { + condition.visit_params(visit); + } + + for subcommand in &self.subcommands { + subcommand.visit_params(visit); + } + + if let Some((_, message)) = &self.message { + message.visit_params(visit); + } + if let Some((_, label)) = &self.label { + label.visit_params(visit); + } + + for note in &self.notes { + note.visit_params(visit); + } + + if let Some(parent_label) = &self.parent_label { + parent_label.visit_params(visit); + } + } + + pub fn evaluate_directive( + &self, + trait_name: impl Debug, + condition_options: &ConditionOptions, + args: &FormatArgs, + ) -> OnUnimplementedNote { + let mut message = None; + let mut label = None; + let mut notes = Vec::new(); + let mut parent_label = None; + let mut append_const_msg = None; + info!( + "evaluate_directive({:?}, trait_ref={:?}, options={:?}, args ={:?})", + self, trait_name, condition_options, args + ); + + for command in self.subcommands.iter().chain(Some(self)).rev() { + debug!(?command); + if let Some(ref condition) = command.condition + && !condition.matches_predicate(condition_options) + { + debug!("evaluate_directive: skipping {:?} due to condition", command); + continue; + } + debug!("evaluate_directive: {:?} succeeded", command); + if let Some(ref message_) = command.message { + message = Some(message_.clone()); + } + + if let Some(ref label_) = command.label { + label = Some(label_.clone()); + } + + notes.extend(command.notes.clone()); + + if let Some(ref parent_label_) = command.parent_label { + parent_label = Some(parent_label_.clone()); + } + + append_const_msg = command.append_const_msg; + } + + OnUnimplementedNote { + label: label.map(|l| l.1.format(args)), + message: message.map(|m| m.1.format(args)), + notes: notes.into_iter().map(|n| n.format(args)).collect(), + parent_label: parent_label.map(|e_s| e_s.format(args)), + append_const_msg, + } + } +} + +#[derive(Default, Debug)] +pub struct OnUnimplementedNote { + pub message: Option, + pub label: Option, + pub notes: Vec, + pub parent_label: Option, + // If none, should fall back to a generic message + pub append_const_msg: Option, +} + +/// Append a message for `[const] Trait` errors. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum AppendConstMessage { + #[default] + Default, + Custom(Symbol, Span), +} + +/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces", +/// either as string pieces or dynamic arguments. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct FormatString { + pub input: Symbol, + pub span: Span, + pub pieces: ThinVec, +} +impl FormatString { + pub fn format(&self, args: &FormatArgs) -> String { + let mut ret = String::new(); + for piece in &self.pieces { + match piece { + Piece::Lit(s) | Piece::Arg(FormatArg::AsIs(s)) => ret.push_str(s.as_str()), + + // `A` if we have `trait Trait {}` and `note = "i'm the actual type of {A}"` + Piece::Arg(FormatArg::GenericParam { generic_param, .. }) => { + match args.generic_args.iter().find(|(p, _)| p == generic_param) { + Some((_, val)) => ret.push_str(val.as_str()), + + None => { + // Apparently this was not actually a generic parameter, so lets write + // what the user wrote. + let _ = fmt::write(&mut ret, format_args!("{{{generic_param}}}")); + } + } + } + // `{Self}` + Piece::Arg(FormatArg::SelfUpper) => { + let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) { + Some((_, val)) => val.to_string(), + None => "Self".to_string(), + }; + ret.push_str(&slf); + } + + // It's only `rustc_onunimplemented` from here + Piece::Arg(FormatArg::This) => ret.push_str(&args.this), + Piece::Arg(FormatArg::Trait) => { + let _ = fmt::write(&mut ret, format_args!("{}", &args.trait_sugared)); + } + Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context), + } + } + ret + } + + fn visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) { + for piece in &self.pieces { + if let Piece::Arg(FormatArg::GenericParam { generic_param, span }) = piece { + visit(*generic_param, *span); + } + } + } +} + +/// Arguments to fill a [FormatString] with. +/// +/// For example, given a +/// ```rust,ignore (just an example) +/// +/// #[rustc_on_unimplemented( +/// on(all(from_desugaring = "QuestionMark"), +/// message = "the `?` operator can only be used in {ItemContext} \ +/// that returns `Result` or `Option` \ +/// (or another type that implements `{FromResidual}`)", +/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`", +/// parent_label = "this function should return `Result` or `Option` to accept `?`" +/// ), +/// )] +/// pub trait FromResidual::Residual> { +/// ... +/// } +/// +/// async fn an_async_function() -> u32 { +/// let x: Option = None; +/// x?; //~ ERROR the `?` operator +/// 22 +/// } +/// ``` +/// it will look like this: +/// +/// ```rust,ignore (just an example) +/// FormatArgs { +/// this: "FromResidual", +/// trait_sugared: "FromResidual>", +/// item_context: "an async function", +/// generic_args: [("Self", "u32"), ("R", "Option")], +/// } +/// ``` +#[derive(Debug)] +pub struct FormatArgs { + pub this: String, + pub trait_sugared: String, + pub item_context: &'static str, + pub generic_args: Vec<(Symbol, String)>, +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum Piece { + Lit(Symbol), + Arg(FormatArg), +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum FormatArg { + // A generic parameter, like `{T}` if we're on the `From` trait. + GenericParam { + generic_param: Symbol, + span: Span, + }, + // `{Self}` + SelfUpper, + /// `{This}` or `{TraitName}` + This, + /// The sugared form of the trait + Trait, + /// what we're in, like a function, method, closure etc. + ItemContext, + /// What the user typed, if it doesn't match anything we can use. + AsIs(Symbol), +} + +/// Represents the `on` filter in `#[rustc_on_unimplemented]`. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct OnUnimplementedCondition { + pub span: Span, + pub pred: Predicate, +} +impl OnUnimplementedCondition { + pub fn matches_predicate(self: &OnUnimplementedCondition, options: &ConditionOptions) -> bool { + self.pred.eval(&mut |p| match p { + FlagOrNv::Flag(b) => options.has_flag(*b), + FlagOrNv::NameValue(NameValue { name, value }) => { + let value = value.format(&options.generic_args); + options.contains(*name, value) + } + }) + } + + pub fn visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) { + self.pred.visit_params(self.span, visit); + } +} + +/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`OnUnimplementedCondition`]. +/// +/// It is similar to the predicate in the `cfg` attribute, +/// and may contain nested predicates. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum Predicate { + /// A condition like `on(crate_local)`. + Flag(Flag), + /// A match, like `on(Rhs = "Whatever")`. + Match(NameValue), + /// Negation, like `on(not($pred))`. + Not(Box), + /// True if all predicates are true, like `on(all($a, $b, $c))`. + All(ThinVec), + /// True if any predicate is true, like `on(any($a, $b, $c))`. + Any(ThinVec), +} + +impl Predicate { + pub fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool { + match self { + Predicate::Flag(flag) => eval(FlagOrNv::Flag(flag)), + Predicate::Match(nv) => eval(FlagOrNv::NameValue(nv)), + Predicate::Not(not) => !not.eval(eval), + Predicate::All(preds) => preds.into_iter().all(|pred| pred.eval(eval)), + Predicate::Any(preds) => preds.into_iter().any(|pred| pred.eval(eval)), + } + } + + pub fn visit_params(&self, span: Span, visit: &mut impl FnMut(Symbol, Span)) { + match self { + Predicate::Flag(_) => {} + Predicate::Match(nv) => nv.visit_params(span, visit), + Predicate::Not(not) => not.visit_params(span, visit), + Predicate::All(preds) | Predicate::Any(preds) => { + preds.iter().for_each(|pred| pred.visit_params(span, visit)) + } + } + } +} + +/// Represents a `MetaWord` in an `on`-filter. +#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum Flag { + /// Whether the code causing the trait bound to not be fulfilled + /// is part of the user's crate. + CrateLocal, + /// Whether the obligation is user-specified rather than derived. + Direct, + /// Whether we are in some kind of desugaring like + /// `?` or `try { .. }`. + FromDesugaring, +} + +/// A `MetaNameValueStr` in an `on`-filter. +/// +/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct NameValue { + pub name: Name, + /// Something like `"&str"` or `"alloc::string::String"`, + /// in which case it just contains a single string piece. + /// But if it is something like `"&[{A}]"` then it must be formatted later. + pub value: FilterFormatString, +} + +impl NameValue { + pub fn visit_params(&self, span: Span, visit: &mut impl FnMut(Symbol, Span)) { + if let Name::GenericArg(arg) = self.name { + visit(arg, span); + } + self.value.visit_params(span, visit); + } +} + +/// The valid names of the `on` filter. +#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum Name { + Cause, + FromDesugaring, + SelfUpper, + GenericArg(Symbol), +} + +#[derive(Debug, Clone)] +pub enum FlagOrNv<'p> { + Flag(&'p Flag), + NameValue(&'p NameValue), +} + +/// Represents a value inside an `on` filter. +/// +/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. +/// If it is a simple literal like this then `pieces` will be `[LitOrArg::Lit("value")]`. +/// The `Arg` variant is used when it contains formatting like +/// `#[rustc_on_unimplemented(on(Self = "&[{A}]", message = "hello"))]`. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct FilterFormatString { + pub pieces: ThinVec, +} + +impl FilterFormatString { + fn format(&self, generic_args: &[(Symbol, String)]) -> String { + let mut ret = String::new(); + + for piece in &self.pieces { + match piece { + LitOrArg::Lit(s) => ret.push_str(s.as_str()), + LitOrArg::Arg(s) => match generic_args.iter().find(|(k, _)| k == s) { + Some((_, val)) => ret.push_str(val), + None => { + let _ = std::fmt::write(&mut ret, format_args!("{{{s}}}")); + } + }, + } + } + + ret + } + pub fn visit_params(&self, span: Span, visit: &mut impl FnMut(Symbol, Span)) { + for piece in &self.pieces { + if let LitOrArg::Arg(arg) = piece { + visit(*arg, span); + } + } + } +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum LitOrArg { + Lit(Symbol), + Arg(Symbol), +} + +/// Used with `OnUnimplementedCondition::matches_predicate` to evaluate the +/// [`OnUnimplementedCondition`]. +/// +/// For example, given a +/// ```rust,ignore (just an example) +/// #[rustc_on_unimplemented( +/// on(all(from_desugaring = "QuestionMark"), +/// message = "the `?` operator can only be used in {ItemContext} \ +/// that returns `Result` or `Option` \ +/// (or another type that implements `{FromResidual}`)", +/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`", +/// parent_label = "this function should return `Result` or `Option` to accept `?`" +/// ), +/// )] +/// pub trait FromResidual::Residual> { +/// ... +/// } +/// +/// async fn an_async_function() -> u32 { +/// let x: Option = None; +/// x?; //~ ERROR the `?` operator +/// 22 +/// } +/// ``` +/// it will look like this: +/// +/// ```rust,ignore (just an example) +/// ConditionOptions { +/// self_types: ["u32", "{integral}"], +/// from_desugaring: Some("QuestionMark"), +/// cause: None, +/// crate_local: false, +/// direct: true, +/// generic_args: [("Self","u32"), +/// ("R", "core::option::Option"), +/// ("R", "core::option::Option" ), +/// ], +/// } +/// ``` +#[derive(Debug)] +pub struct ConditionOptions { + /// All the self types that may apply. + pub self_types: Vec, + // The kind of compiler desugaring. + pub from_desugaring: Option, + /// Match on a variant of rustc_infer's `ObligationCauseCode`. + pub cause: Option, + pub crate_local: bool, + /// Is the obligation "directly" user-specified, rather than derived? + pub direct: bool, + // A list of the generic arguments and their reified types. + pub generic_args: Vec<(Symbol, String)>, +} + +impl ConditionOptions { + pub fn has_flag(&self, name: Flag) -> bool { + match name { + Flag::CrateLocal => self.crate_local, + Flag::Direct => self.direct, + Flag::FromDesugaring => self.from_desugaring.is_some(), + } + } + pub fn contains(&self, name: Name, value: String) -> bool { + match name { + Name::SelfUpper => self.self_types.contains(&value), + Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)), + Name::Cause => self.cause == Some(value), + Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)), + } + } +} diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index d8b8ca90660a..27128f699637 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -18,7 +18,6 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { match self { // tidy-alphabetical-start - Align { .. } => No, AllowInternalUnsafe(..) => Yes, AllowInternalUnstable(..) => Yes, AutomaticallyDerived(..) => Yes, @@ -35,15 +34,16 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { CrateType(_) => No, CustomMir(_, _, _) => Yes, DebuggerVisualizer(..) => No, - Deprecation { .. } => Yes, + DefaultLibAllocator => No, + Deprecated { .. } => Yes, DoNotRecommend { .. } => Yes, Doc(_) => Yes, DocComment { .. } => Yes, EiiDeclaration(_) => Yes, - EiiForeignItem => No, EiiImpls(..) => No, ExportName { .. } => Yes, ExportStable => No, + Feature(..) => No, FfiConst(..) => No, FfiPure(..) => No, Fundamental { .. } => Yes, @@ -76,6 +76,8 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { NoMangle(..) => Yes, // Needed for rustdoc NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc + OnConst { .. } => Yes, + OnUnimplemented { .. } => Yes, Optimize(..) => No, PanicRuntime => No, PatchableFunctionEntry { .. } => Yes, @@ -90,14 +92,17 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { ProfilerRuntime => No, RecursionLimit { .. } => No, ReexportTestHarnessMain(..) => No, + RegisterTool(..) => No, Repr { .. } => No, RustcAbi { .. } => No, + RustcAlign { .. } => No, RustcAllocator => No, RustcAllocatorZeroed => No, RustcAllocatorZeroedVariant { .. } => Yes, RustcAllowConstFnUnstable(..) => No, RustcAllowIncoherentImpl(..) => No, RustcAsPtr(..) => Yes, + RustcAutodiff(..) => Yes, RustcBodyStability { .. } => No, RustcBuiltinMacro { .. } => Yes, RustcCaptureAnalysis => No, @@ -107,7 +112,7 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { RustcCoinductive(..) => No, RustcConfusables { .. } => Yes, RustcConstStability { .. } => Yes, - RustcConstStabilityIndirect => No, + RustcConstStableIndirect => No, RustcConversionSuggestion => Yes, RustcDeallocator => No, RustcDefPath(..) => No, @@ -115,18 +120,26 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { RustcDenyExplicitImpl(..) => No, RustcDeprecatedSafe2024 { .. } => Yes, RustcDiagnosticItem(..) => Yes, + RustcDoNotConstCheck => Yes, + RustcDocPrimitive(..) => Yes, RustcDummy => No, RustcDumpDefParents => No, + RustcDumpInferredOutlives => No, RustcDumpItemBounds => No, + RustcDumpObjectLifetimeDefaults => No, RustcDumpPredicates => No, RustcDumpUserArgs => No, + RustcDumpVariances => No, + RustcDumpVariancesOfOpaques => No, RustcDumpVtable(..) => No, RustcDynIncompatibleTrait(..) => No, RustcEffectiveVisibility => Yes, + RustcEiiForeignItem => No, RustcEvaluateWhereClauses => Yes, RustcHasIncoherentInherentImpls => Yes, RustcHiddenTypeOfOpaques => No, RustcIfThisChanged(..) => No, + RustcInheritOverflowChecks => No, RustcInsignificantDtor => Yes, RustcIntrinsic => Yes, RustcIntrinsicConstStableIndirect => No, @@ -142,18 +155,17 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { RustcMain => No, RustcMir(..) => Yes, RustcMustImplementOneOf { .. } => No, - RustcNeverReturnsNullPointer => Yes, + RustcNeverReturnsNullPtr => Yes, RustcNeverTypeOptions { .. } => No, RustcNoImplicitAutorefs => Yes, RustcNoImplicitBounds => No, RustcNoMirInline => Yes, RustcNonConstTraitMethod => No, // should be reported via other queries like `constness` + RustcNonnullOptimizationGuaranteed => Yes, RustcNounwind => No, RustcObjcClass { .. } => No, RustcObjcSelector { .. } => No, - RustcObjectLifetimeDefault => No, RustcOffloadKernel => Yes, - RustcOutlives => No, RustcParenSugar(..) => No, RustcPassByValue(..) => Yes, RustcPassIndirectlyInNonRusticAbis(..) => No, @@ -175,8 +187,6 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { RustcThenThisWouldNeed(..) => No, RustcTrivialFieldReads => Yes, RustcUnsafeSpecializationMarker(..) => No, - RustcVariance => No, - RustcVarianceOfOpaques => No, Sanitize { .. } => No, ShouldPanic { .. } => No, Stability { .. } => Yes, diff --git a/compiler/rustc_hir/src/attrs/mod.rs b/compiler/rustc_hir/src/attrs/mod.rs index 482e6c90739f..09fa144a1604 100644 --- a/compiler/rustc_hir/src/attrs/mod.rs +++ b/compiler/rustc_hir/src/attrs/mod.rs @@ -9,6 +9,7 @@ pub use pretty_printing::PrintAttribute; mod data_structures; +pub mod diagnostic; mod encode_cross_crate; mod pretty_printing; @@ -29,9 +30,41 @@ /// ``` /// /// Often this requires you to first end up with a list of attributes. -/// A common way to get those is through `tcx.get_all_attrs(did)` +/// Often these are available through the `tcx`. +/// +/// As a convenience, this macro can do that for you! +/// +/// Instead of providing an attribute list, provide the `tcx` and a `DefId`. +/// +/// ```rust,ignore (illustrative) +/// find_attr!(tcx, def_id, ) +/// ``` +/// +/// Another common case is finding attributes applied to the root of the current crate. +/// For that, use the shortcut: +/// +/// ```rust, ignore (illustrative) +/// find_attr!(tcx, crate, ) +/// ``` #[macro_export] macro_rules! find_attr { + ($tcx: expr, crate, $pattern: pat $(if $guard: expr)?) => { + $crate::find_attr!($tcx, crate, $pattern $(if $guard)? => ()).is_some() + }; + ($tcx: expr, crate, $pattern: pat $(if $guard: expr)? => $e: expr) => { + $crate::find_attr!($tcx.hir_krate_attrs(), $pattern $(if $guard)? => $e) + }; + + ($tcx: expr, $def_id: expr, $pattern: pat $(if $guard: expr)?) => { + $crate::find_attr!($tcx, $def_id, $pattern $(if $guard)? => ()).is_some() + }; + ($tcx: expr, $def_id: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{ + #[allow(deprecated)] { + $crate::find_attr!($tcx.get_all_attrs($def_id), $pattern $(if $guard)? => $e) + } + }}; + + ($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{ $crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some() }}; @@ -39,11 +72,19 @@ macro_rules! find_attr { ($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{ 'done: { for i in $attributes_list { + #[allow(unused_imports)] + use rustc_hir::attrs::AttributeKind::*; let i: &rustc_hir::Attribute = i; match i { rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => { break 'done Some($e); } + rustc_hir::Attribute::Unparsed(..) => {} + // In lint emitting, there's a specific exception for this warning. + // It's not usually emitted from inside macros from other crates + // (see https://github.com/rust-lang/rust/issues/110613) + // But this one is! + #[deny(unreachable_patterns)] _ => {} } } diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index 8fce52901015..9d14f9de3078 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -6,6 +6,7 @@ use rustc_ast::ast::{Path, join_path_idents}; use rustc_ast::attr::data_structures::CfgEntry; use rustc_ast::attr::version::RustcVersion; +use rustc_ast::expand::autodiff_attrs::{DiffActivity, DiffMode}; use rustc_ast::token::{CommentKind, DocFragmentKind}; use rustc_ast::{AttrId, AttrStyle, IntTy, UintTy}; use rustc_ast_pretty::pp::Printer; @@ -191,7 +192,7 @@ fn print_attribute(&self, p: &mut Printer) { print_tup!(A B C D E F G H); print_skip!(Span, (), ErrorGuaranteed, AttrId); -print_disp!(u8, u16, u128, usize, bool, NonZero, Limit); +print_disp!(u8, u16, u32, u128, usize, bool, NonZero, Limit); print_debug!( Symbol, Ident, @@ -206,4 +207,6 @@ fn print_attribute(&self, p: &mut Printer) { DefId, RustcVersion, CfgEntry, + DiffActivity, + DiffMode, ); diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index dd2cd4939432..3959ee7f9412 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -120,7 +120,9 @@ pub enum DefKind { // Value namespace Fn, - Const, + Const { + is_type_const: bool, + }, /// Constant generic parameter: `struct Foo { ... }` ConstParam, Static { @@ -147,7 +149,9 @@ pub enum DefKind { /// or `trait Foo { fn associated() {} }` AssocFn, /// Associated constant: `trait MyTrait { const ASSOC: usize; }` - AssocConst, + AssocConst { + is_type_const: bool, + }, // Macro namespace Macro(MacroKinds), @@ -222,8 +226,8 @@ pub fn descr(self, def_id: DefId) -> &'static str { DefKind::Trait => "trait", DefKind::ForeignTy => "foreign type", DefKind::AssocFn => "associated function", - DefKind::Const => "constant", - DefKind::AssocConst => "associated constant", + DefKind::Const { .. } => "constant", + DefKind::AssocConst { .. } => "associated constant", DefKind::TyParam => "type parameter", DefKind::ConstParam => "const parameter", DefKind::Macro(kinds) => kinds.descr(), @@ -249,7 +253,7 @@ pub fn descr(self, def_id: DefId) -> &'static str { pub fn article(&self) -> &'static str { match *self { DefKind::AssocTy - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::AssocFn | DefKind::Enum | DefKind::OpaqueTy @@ -277,12 +281,12 @@ pub fn ns(&self) -> Option { | DefKind::TyParam => Some(Namespace::TypeNS), DefKind::Fn - | DefKind::Const + | DefKind::Const { .. } | DefKind::ConstParam | DefKind::Static { .. } | DefKind::Ctor(..) | DefKind::AssocFn - | DefKind::AssocConst => Some(Namespace::ValueNS), + | DefKind::AssocConst { .. } => Some(Namespace::ValueNS), DefKind::Macro(..) => Some(Namespace::MacroNS), @@ -323,11 +327,11 @@ pub fn def_path_data(self, name: Option) -> DefPathData { DefKind::AssocTy => DefPathData::TypeNs(name.unwrap()), DefKind::Fn - | DefKind::Const + | DefKind::Const { .. } | DefKind::ConstParam | DefKind::Static { .. } | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Field => DefPathData::ValueNs(name.unwrap()), DefKind::Macro(..) => DefPathData::MacroNs(name.unwrap()), DefKind::LifetimeParam => DefPathData::LifetimeNs(name.unwrap()), @@ -345,7 +349,7 @@ pub fn def_path_data(self, name: Option) -> DefPathData { } pub fn is_assoc(self) -> bool { - matches!(self, DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy) + matches!(self, DefKind::AssocConst { .. } | DefKind::AssocFn | DefKind::AssocTy) } /// This is a "module" in name resolution sense. @@ -367,15 +371,15 @@ pub fn is_fn_like(self) -> bool { ) } - /// Whether the corresponding item has generic parameters, ie. the `generics_of` query works. + /// Whether the corresponding item has generic parameters, i.e. the `generics_of` query works. pub fn has_generics(self) -> bool { match self { DefKind::AnonConst - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::AssocFn | DefKind::AssocTy | DefKind::Closure - | DefKind::Const + | DefKind::Const { .. } | DefKind::Ctor(..) | DefKind::Enum | DefKind::Field @@ -423,8 +427,8 @@ pub fn has_codegen_attrs(self) -> bool { | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::AssocTy - | DefKind::Const - | DefKind::AssocConst + | DefKind::Const { .. } + | DefKind::AssocConst { .. } | DefKind::Macro(..) | DefKind::Use | DefKind::ForeignMod @@ -459,12 +463,12 @@ pub fn is_typeck_child(self) -> bool { | DefKind::AssocTy | DefKind::TyParam | DefKind::Fn - | DefKind::Const + | DefKind::Const { .. } | DefKind::ConstParam | DefKind::Static { .. } | DefKind::Ctor(_, _) | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Macro(_) | DefKind::ExternCrate | DefKind::Use diff --git a/compiler/rustc_hir/src/diagnostic_items.rs b/compiler/rustc_hir/src/diagnostic_items.rs index 67e9c9eeedc4..5e0915de9d38 100644 --- a/compiler/rustc_hir/src/diagnostic_items.rs +++ b/compiler/rustc_hir/src/diagnostic_items.rs @@ -1,19 +1,13 @@ use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use rustc_span::def_id::DefIdMap; use crate::def_id::DefId; -#[derive(Debug, Default)] +#[derive(Debug, Default, HashStable_Generic)] pub struct DiagnosticItems { + #[stable_hasher(ignore)] pub id_to_name: DefIdMap, pub name_to_id: FxIndexMap, } - -impl HashStable for DiagnosticItems { - #[inline] - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - self.name_to_id.hash_stable(ctx, hasher); - } -} diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f1f7350c1e79..45a363b97722 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -861,6 +861,10 @@ pub fn is_impl_trait(&self) -> bool { pub fn is_elided_lifetime(&self) -> bool { matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(_) }) } + + pub fn is_lifetime(&self) -> bool { + matches!(self.kind, GenericParamKind::Lifetime { .. }) + } } /// Records where the generic parameter originated from. @@ -1378,7 +1382,7 @@ fn span(&self) -> Span { Attribute::Unparsed(u) => u.span, // FIXME: should not be needed anymore when all attrs are parsed Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span, - Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span, + Attribute::Parsed(AttributeKind::Deprecated { span, .. }) => *span, Attribute::Parsed(AttributeKind::CfgTrace(cfgs)) => cfgs[0].1, a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"), } @@ -1420,7 +1424,7 @@ fn doc_str(&self) -> Option { #[inline] fn deprecation_note(&self) -> Option { match &self { - Attribute::Parsed(AttributeKind::Deprecation { deprecation, .. }) => deprecation.note, + Attribute::Parsed(AttributeKind::Deprecated { deprecation, .. }) => deprecation.note, _ => None, } } @@ -1467,6 +1471,10 @@ fn is_doc_hidden(&self) -> bool { fn is_doc_keyword_or_attribute(&self) -> bool { matches!(self, Attribute::Parsed(AttributeKind::Doc(d)) if d.attribute.is_some() || d.keyword.is_some()) } + + fn is_rustc_doc_primitive(&self) -> bool { + matches!(self, Attribute::Parsed(AttributeKind::RustcDocPrimitive(..))) + } } // FIXME(fn_delegation): use function delegation instead of manually forwarding @@ -1625,7 +1633,7 @@ pub struct OwnerInfo<'hir> { pub attrs: AttributeMap<'hir>, /// Map indicating what traits are in scope for places where this /// is relevant; generated by resolve. - pub trait_map: ItemLocalMap>, + pub trait_map: ItemLocalMap<&'hir [TraitCandidate<'hir>]>, /// Lints delayed during ast lowering to be emitted /// after hir has completely built @@ -1737,6 +1745,12 @@ pub fn innermost_block(&self) -> &Block<'hir> { } } +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct TyFieldPath { + pub variant: Option, + pub field: Ident, +} + #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct TyPat<'hir> { #[stable_hasher(ignore)] @@ -2648,7 +2662,9 @@ pub fn can_have_side_effects(&self) -> bool { ExprKind::Struct(_, fields, init) => { let init_side_effects = match init { StructTailExpr::Base(init) => init.can_have_side_effects(), - StructTailExpr::DefaultFields(_) | StructTailExpr::None => false, + StructTailExpr::DefaultFields(_) + | StructTailExpr::None + | StructTailExpr::NoneWithError(_) => false, }; fields.iter().map(|field| field.expr).any(|e| e.can_have_side_effects()) || init_side_effects @@ -2940,6 +2956,12 @@ pub enum StructTailExpr<'hir> { /// fields' default values will be used to populate any fields not explicitly mentioned: /// `Foo { .. }`. DefaultFields(Span), + /// No trailing `..` was written, and also, a parse error occurred inside the struct braces. + /// + /// This struct should be treated similarly to as if it had an `..` in it, + /// in particular rather than reporting missing fields, because the parse error + /// makes which fields the struct was intended to have not fully known. + NoneWithError(ErrorGuaranteed), } /// Represents an optionally `Self`-qualified value/type path or associated extension. @@ -3749,10 +3771,18 @@ pub enum OpaqueTyOrigin { }, } +// Ids of parent (or child) path segment that contains user-specified args #[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable_Generic)] -pub enum InferDelegationKind { +pub struct DelegationGenerics { + pub parent_args_segment_id: Option, + pub child_args_segment_id: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable_Generic)] +pub enum InferDelegationKind<'hir> { Input(usize), - Output, + // Place generics info here, as we always specify output type for delegations. + Output(&'hir DelegationGenerics), } /// The various kinds of types recognized by the compiler. @@ -3764,7 +3794,7 @@ pub enum InferDelegationKind { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum TyKind<'hir, Unambig = ()> { /// Actual type should be inherited from `DefId` signature - InferDelegation(DefId, InferDelegationKind), + InferDelegation(DefId, InferDelegationKind<'hir>), /// A variable length slice (i.e., `[T]`). Slice(&'hir Ty<'hir>), /// A fixed length array (i.e., `[T; n]`). @@ -3800,6 +3830,10 @@ pub enum TyKind<'hir, Unambig = ()> { Err(rustc_span::ErrorGuaranteed), /// Pattern types (`pattern_type!(u32 is 1..)`) Pat(&'hir Ty<'hir>, &'hir TyPat<'hir>), + /// Field representing type (`field_of!(Struct, field)`). + /// + /// The optional ident is the variant when an enum is passed `field_of!(Enum, Variant.field)`. + FieldOf(&'hir Ty<'hir>, &'hir TyFieldPath), /// `TyKind::Infer` means the type should be inferred instead of it having been /// specified. This can appear anywhere in a type. /// @@ -3917,6 +3951,17 @@ pub fn opt_delegation_sig_id(&self) -> Option { } None } + + pub fn opt_delegation_generics(&self) -> Option<&'hir DelegationGenerics> { + if let FnRetTy::Return(ty) = self.output + && let TyKind::InferDelegation(_, kind) = ty.kind + && let InferDelegationKind::Output(generics) = kind + { + return Some(generics); + } + + None + } } /// Represents what type of implicit self a function has, if any. @@ -4638,10 +4683,10 @@ pub struct Upvar { // The TraitCandidate's import_ids is empty if the trait is defined in the same module, and // has length > 0 if the trait is found through an chain of imports, starting with the // import/use statement in the scope where the trait is used. -#[derive(Debug, Clone, HashStable_Generic)] -pub struct TraitCandidate { +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct TraitCandidate<'hir> { pub def_id: DefId, - pub import_ids: SmallVec<[LocalDefId; 1]>, + pub import_ids: &'hir [LocalDefId], // Indicates whether this trait candidate is ambiguously glob imported // in it's scope. Related to the AMBIGUOUS_GLOB_IMPORTED_TRAITS lint. // If this is set to true and the trait is used as a result of method lookup, this diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 21bcf53b5619..25ef56f8b0f2 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -830,7 +830,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) walk_list!(visitor, visit_expr_field, fields); match optional_base { StructTailExpr::Base(base) => try_visit!(visitor.visit_expr(base)), - StructTailExpr::None | StructTailExpr::DefaultFields(_) => {} + StructTailExpr::None + | StructTailExpr::NoneWithError(_) + | StructTailExpr::DefaultFields(_) => {} } } ExprKind::Tup(subexpressions) => { @@ -1047,6 +1049,11 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) - try_visit!(visitor.visit_ty_unambig(ty)); try_visit!(visitor.visit_pattern_type_pattern(pat)); } + TyKind::FieldOf(ty, TyFieldPath { variant, field }) => { + try_visit!(visitor.visit_ty_unambig(ty)); + visit_opt!(visitor, visit_ident, *variant); + try_visit!(visitor.visit_ident(*field)); + } } V::Result::output() } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 0325fd2ceab9..80f0af9d663c 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -320,7 +320,6 @@ fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) { FormatArgument, sym::format_argument, format_argument, Target::Struct, GenericRequirement::None; FormatArguments, sym::format_arguments, format_arguments, Target::Struct, GenericRequirement::None; - ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; @@ -342,7 +341,8 @@ fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) { PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1); - ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None; + ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::Exact(1); + MaybeDangling, sym::maybe_dangling, maybe_dangling, Target::Struct, GenericRequirement::Exact(1); BikeshedGuaranteedNoDrop, sym::bikeshed_guaranteed_no_drop, bikeshed_guaranteed_no_drop, Target::Trait, GenericRequirement::Exact(0); MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None; @@ -436,6 +436,13 @@ fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) { // Reborrowing related lang-items Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0); CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0); + + // Field representing types. + FieldRepresentingType, sym::field_representing_type, field_representing_type, Target::Struct, GenericRequirement::Exact(3); + Field, sym::field, field, Target::Trait, GenericRequirement::Exact(0); + FieldBase, sym::field_base, field_base, Target::AssocTy, GenericRequirement::Exact(0); + FieldType, sym::field_type, field_type, Target::AssocTy, GenericRequirement::Exact(0); + FieldOffset, sym::field_offset, field_offset, Target::AssocConst, GenericRequirement::Exact(0); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index eba2d182d2c4..1589a6de220e 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -1,6 +1,6 @@ use rustc_data_structures::fingerprint::Fingerprint; -pub use rustc_lint_defs::AttributeLintKind; use rustc_lint_defs::LintId; +pub use rustc_lint_defs::{AttributeLintKind, FormatWarning}; use rustc_macros::HashStable_Generic; use rustc_span::Span; diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index b76c48fbe8ac..dd685c44ec47 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -148,7 +148,7 @@ pub fn from_def_kind(def_kind: DefKind) -> Target { DefKind::ExternCrate => Target::ExternCrate, DefKind::Use => Target::Use, DefKind::Static { .. } => Target::Static, - DefKind::Const => Target::Const, + DefKind::Const { .. } => Target::Const, DefKind::Fn => Target::Fn, DefKind::Macro(..) => Target::MacroDef, DefKind::Mod => Target::Mod, diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs index f39d1b7af345..d86bf4cc4e4f 100644 --- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs +++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs @@ -52,7 +52,7 @@ pub(crate) fn check_drop_impl( } } - tcx.ensure_ok().orphan_check_impl(drop_impl_did)?; + tcx.ensure_result().orphan_check_impl(drop_impl_did)?; let self_ty = tcx.type_of(drop_impl_did).instantiate_identity(); @@ -96,7 +96,7 @@ pub(crate) fn check_negative_auto_trait_impl<'tcx>( tcx.dcx().span_delayed_bug(tcx.def_span(impl_def_id), "default impl cannot be negative"); } - tcx.ensure_ok().orphan_check_impl(impl_def_id)?; + tcx.ensure_result().orphan_check_impl(impl_def_id)?; match impl_trait_ref.self_ty().kind() { ty::Adt(adt_def, adt_to_impl_args) => { diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 1bee7a72229a..4157b110fbf6 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -4,15 +4,15 @@ use rustc_abi::{ExternAbi, FieldIdx, ScalableElt}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; -use rustc_errors::{EmissionGuarantee, MultiSpan}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan}; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::attrs::ReprAttr::ReprPacked; use rustc_hir::def::{CtorKind, DefKind}; -use rustc_hir::{LangItem, Node, attrs, find_attr, intravisit}; +use rustc_hir::{LangItem, Node, find_attr, intravisit}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode, WellFormedLoc}; use rustc_lint_defs::builtin::{REPR_TRANSPARENT_NON_ZST_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS}; +use rustc_macros::Diagnostic; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; @@ -27,7 +27,6 @@ use rustc_span::source_map::Spanned; use rustc_target::spec::{AbiMap, AbiMapping}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; -use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use tracing::{debug, instrument}; @@ -55,6 +54,22 @@ fn add_abi_diag_help(abi: ExternAbi, diag: &mut Diag<'_, T } pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: ExternAbi) { + struct UnsupportedCallingConventions { + abi: ExternAbi, + } + + 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, + level, + format!("{abi} is not a supported ABI for the current target"), + ); + add_abi_diag_help(abi, &mut lint); + lint + } + } // FIXME: This should be checked earlier, e.g. in `rustc_ast_lowering`, as this // currently only guards function imports, function definitions, and function pointer types. // Functions in trait declarations can still use "deprecated" ABIs without any warning. @@ -66,12 +81,12 @@ pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: ExternAbi tcx.dcx().span_delayed_bug(span, format!("{abi} should be rejected in ast_lowering")); } AbiMapping::Deprecated(..) => { - tcx.node_span_lint(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| { - lint.primary_message(format!( - "{abi} is not a supported ABI for the current target" - )); - add_abi_diag_help(abi, lint); - }); + tcx.emit_node_span_lint( + UNSUPPORTED_CALLING_CONVENTIONS, + hir_id, + span, + UnsupportedCallingConventions { abi }, + ); } } } @@ -79,7 +94,7 @@ pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: ExternAbi pub fn check_custom_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, fn_sig: FnSig<'_>, fn_sig_span: Span) { if fn_sig.abi == ExternAbi::Custom { // Function definitions that use `extern "custom"` must be naked functions. - if !find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Naked(_)) { + if !find_attr!(tcx, def_id, Naked(_)) { tcx.dcx().emit_err(crate::errors::AbiCustomClothedFunction { span: fn_sig_span, naked_span: tcx.def_span(def_id).shrink_to_lo(), @@ -176,6 +191,11 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b /// Check that a `static` is inhabited. fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { + #[derive(Diagnostic)] + #[diag("static of uninhabited type")] + #[note("uninhabited statics cannot be initialized, and any access would be an immediate error")] + struct StaticOfUninhabitedType; + // Make sure statics are inhabited. // Other parts of the compiler assume that there are no uninhabited places. In principle it // would be enough to check this for `extern` statics, as statics with an initializer will @@ -206,15 +226,11 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { } }; if layout.is_uninhabited() { - tcx.node_span_lint( + tcx.emit_node_span_lint( UNINHABITED_STATIC, tcx.local_def_id_to_hir_id(def_id), span, - |lint| { - lint.primary_message("static of uninhabited type"); - lint - .note("uninhabited statics cannot be initialized, and any access would be an immediate error"); - }, + StaticOfUninhabitedType, ); } } @@ -806,11 +822,10 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); tcx.ensure_ok().associated_items(def_id); - check_diagnostic_attrs(tcx, def_id); if of_trait { let impl_trait_header = tcx.impl_trait_header(def_id); res = res.and( - tcx.ensure_ok() + tcx.ensure_result() .coherent_trait(impl_trait_header.trait_ref.instantiate_identity().def_id), ); @@ -829,7 +844,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().predicates_of(def_id); tcx.ensure_ok().associated_items(def_id); let assoc_items = tcx.associated_items(def_id); - check_diagnostic_attrs(tcx, def_id); for &assoc_item in assoc_items.in_definition_order() { match assoc_item.kind { @@ -901,7 +915,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), // avoids this query from having a direct dependency edge on the HIR return res; } - DefKind::Const => { + DefKind::Const { .. } => { tcx.ensure_ok().generics_of(def_id); tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); @@ -981,12 +995,11 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), (0, _) => ("const", "consts", None), _ => ("type or const", "types or consts", None), }; - let name = - if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::EiiForeignItem) { - "externally implementable items" - } else { - "foreign items" - }; + let name = if find_attr!(tcx, def_id, RustcEiiForeignItem) { + "externally implementable items" + } else { + "foreign items" + }; let span = tcx.def_span(def_id); struct_span_code_err!( @@ -1064,7 +1077,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), // avoids this query from having a direct dependency edge on the HIR return res; } - DefKind::AssocConst => { + DefKind::AssocConst { .. } => { tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); res = res.and(check_associated_item(tcx, def_id)); @@ -1124,11 +1137,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), }) } -pub(super) fn check_diagnostic_attrs(tcx: TyCtxt<'_>, def_id: LocalDefId) { - // an error would be reported if this fails. - let _ = OnUnimplementedDirective::of_item(tcx, def_id.to_def_id()); -} - pub(super) fn check_specialization_validity<'tcx>( tcx: TyCtxt<'tcx>, trait_def: &ty::TraitDef, @@ -1175,13 +1183,35 @@ pub(super) fn check_specialization_validity<'tcx>( if let Err(parent_impl) = result { if !tcx.is_impl_trait_in_trait(impl_item) { - report_forbidden_specialization(tcx, impl_item, parent_impl); + let span = tcx.def_span(impl_item); + let ident = tcx.item_ident(impl_item); + + let err = match tcx.span_of_impl(parent_impl) { + Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp }, + Err(cname) => errors::ImplNotMarkedDefault::Err { span, ident, cname }, + }; + + tcx.dcx().emit_err(err); } else { tcx.dcx().delayed_bug(format!("parent item: {parent_impl:?} not marked as default")); } } } +fn check_overriding_final_trait_item<'tcx>( + tcx: TyCtxt<'tcx>, + trait_item: ty::AssocItem, + impl_item: ty::AssocItem, +) { + if trait_item.defaultness(tcx).is_final() { + tcx.dcx().emit_err(errors::OverridingFinalTraitFunction { + impl_span: tcx.def_span(impl_item.def_id), + trait_span: tcx.def_span(trait_item.def_id), + ident: tcx.item_ident(impl_item.def_id), + }); + } +} + fn check_impl_items_against_trait<'tcx>( tcx: TyCtxt<'tcx>, impl_id: LocalDefId, @@ -1201,7 +1231,7 @@ fn check_impl_items_against_trait<'tcx>( match impl_trait_header.polarity { ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {} ty::ImplPolarity::Negative => { - if let [first_item_ref, ..] = impl_item_refs { + if let [first_item_ref, ..] = *impl_item_refs { let first_item_span = tcx.def_span(first_item_ref); struct_span_code_err!( tcx.dcx(), @@ -1226,8 +1256,7 @@ fn check_impl_items_against_trait<'tcx>( Err(ErrorGuaranteed { .. }) => continue, }; - let res = tcx.ensure_ok().compare_impl_item(impl_item.expect_local()); - + let res = tcx.ensure_result().compare_impl_item(impl_item.expect_local()); if res.is_ok() { match ty_impl_item.kind { ty::AssocKind::Fn { .. } => { @@ -1259,6 +1288,8 @@ fn check_impl_items_against_trait<'tcx>( impl_id.to_def_id(), impl_item, ); + + check_overriding_final_trait_item(tcx, ty_trait_item, ty_impl_item); } if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { @@ -1349,7 +1380,7 @@ fn check_impl_items_against_trait<'tcx>( } if let Some(missing_items) = must_implement_one_of { - let attr_span = find_attr!(tcx.get_all_attrs(trait_ref.def_id), AttributeKind::RustcMustImplementOneOf {attr_span, ..} => *attr_span); + let attr_span = find_attr!(tcx, trait_ref.def_id, RustcMustImplementOneOf {attr_span, ..} => *attr_span); missing_items_must_implement_one_of_err( tcx, @@ -1532,8 +1563,7 @@ fn check_scalable_vector(tcx: TyCtxt<'_>, span: Span, def_id: LocalDefId, scalab pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { let repr = def.repr(); if repr.packed() { - if let Some(reprs) = find_attr!(tcx.get_all_attrs(def.did()), attrs::AttributeKind::Repr { reprs, .. } => reprs) - { + if let Some(reprs) = find_attr!(tcx, def.did(), Repr { reprs, .. } => reprs) { for (r, _) in reprs { if let ReprPacked(pack) = r && let Some(repr_pack) = repr.pack @@ -1624,6 +1654,39 @@ pub(super) fn check_packed_inner( } pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) { + struct ZeroSizedFieldReprTransparentIncompatibility<'tcx> { + unsuited: UnsuitedInfo<'tcx>, + } + + impl<'a, 'tcx> Diagnostic<'a, ()> for ZeroSizedFieldReprTransparentIncompatibility<'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { unsuited } = self; + let (title, note) = match unsuited.reason { + UnsuitedReason::NonExhaustive => ( + "external non-exhaustive types", + "is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.", + ), + UnsuitedReason::PrivateField => ( + "external types with private fields", + "contains private fields, so it could become non-zero-sized in the future.", + ), + UnsuitedReason::ReprC => ( + "`repr(C)` types", + "is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.", + ), + }; + Diag::new( + dcx, + level, + format!("zero-sized fields in `repr(transparent)` cannot contain {title}"), + ) + .with_note(format!( + "this field contains `{field_ty}`, which {note}", + field_ty = unsuited.ty, + )) + } + } + if !adt.repr().transparent() { return; } @@ -1700,12 +1763,7 @@ fn check_unsuited<'tcx>( ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited(tcx, typing_env, t)), ty::Array(ty, _) => check_unsuited(tcx, typing_env, *ty), ty::Adt(def, args) => { - if !def.did().is_local() - && !find_attr!( - tcx.get_all_attrs(def.did()), - AttributeKind::RustcPubTransparent(_) - ) - { + if !def.did().is_local() && !find_attr!(tcx, def.did(), RustcPubTransparent(_)) { let non_exhaustive = def.is_variant_list_non_exhaustive() || def.variants().iter().any(ty::VariantDef::is_field_list_non_exhaustive); let has_priv = def.all_fields().any(|f| !f.vis.is_public()); @@ -1739,29 +1797,11 @@ fn check_unsuited<'tcx>( // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts. // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst. if non_trivial_count > 0 || prev_unsuited_1zst { - tcx.node_span_lint( + tcx.emit_node_span_lint( REPR_TRANSPARENT_NON_ZST_FIELDS, tcx.local_def_id_to_hir_id(adt.did().expect_local()), field.span, - |lint| { - let title = match unsuited.reason { - UnsuitedReason::NonExhaustive => "external non-exhaustive types", - UnsuitedReason::PrivateField => "external types with private fields", - UnsuitedReason::ReprC => "`repr(C)` types", - }; - lint.primary_message( - format!("zero-sized fields in `repr(transparent)` cannot contain {title}"), - ); - let note = match unsuited.reason { - UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.", - UnsuitedReason::PrivateField => "contains private fields, so it could become non-zero-sized in the future.", - UnsuitedReason::ReprC => "is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.", - }; - lint.note(format!( - "this field contains `{field_ty}`, which {note}", - field_ty = unsuited.ty, - )); - }, + ZeroSizedFieldReprTransparentIncompatibility { unsuited }, ); } else { prev_unsuited_1zst = true; @@ -1776,19 +1816,16 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { def.destructor(tcx); // force the destructor to be evaluated if def.variants().is_empty() { - find_attr!( - tcx.get_all_attrs(def_id), - attrs::AttributeKind::Repr { reprs, first_span } => { - struct_span_code_err!( - tcx.dcx(), - reprs.first().map(|repr| repr.1).unwrap_or(*first_span), - E0084, - "unsupported representation for zero-variant enum" - ) - .with_span_label(tcx.def_span(def_id), "zero-variant enum") - .emit(); - } - ); + find_attr!(tcx, def_id, Repr { reprs, first_span } => { + struct_span_code_err!( + tcx.dcx(), + reprs.first().map(|repr| repr.1).unwrap_or(*first_span), + E0084, + "unsupported representation for zero-variant enum" + ) + .with_span_label(tcx.def_span(def_id), "zero-variant enum") + .emit(); + }); } for v in def.variants() { diff --git a/compiler/rustc_hir_analysis/src/check/compare_eii.rs b/compiler/rustc_hir_analysis/src/check/compare_eii.rs index 2beb7eb09c11..9443aaac2258 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_eii.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_eii.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, E0806, struct_span_code_err}; -use rustc_hir::attrs::{AttributeKind, EiiImplResolution}; +use rustc_hir::attrs::EiiImplResolution; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, FnSig, HirId, ItemKind, find_attr}; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; @@ -177,9 +177,7 @@ fn check_no_generics<'tcx>( // since in that case it looks like a duplicate error: the declaration of the EII already can't contain generics. // So, we check here if at least one of the eii impls has ImplResolution::Macro, which indicates it's // not generated as part of the declaration. - && find_attr!( - tcx.get_all_attrs(external_impl), - AttributeKind::EiiImpls(impls) if impls.iter().any(|i| matches!(i.resolution, EiiImplResolution::Macro(_))) + && find_attr!(tcx, external_impl, EiiImpls(impls) if impls.iter().any(|i| matches!(i.resolution, EiiImplResolution::Macro(_))) ) { tcx.dcx().emit_err(EiiWithGenerics { 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 c29ebe67a158..1fc7d221de4d 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1,5 +1,6 @@ use core::ops::ControlFlow; use std::borrow::Cow; +use std::cmp::Ordering; use std::iter; use hir::def_id::{DefId, DefIdMap, LocalDefId}; @@ -18,7 +19,7 @@ Upcast, }; use rustc_middle::{bug, span_bug}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{BytePos, DUMMY_SP, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::regions::InferCtxtRegionExt; @@ -1333,7 +1334,7 @@ fn check_region_late_boundedness<'tcx>( .iter() .map(|param| { let (LateEarlyMismatch::EarlyInImpl(impl_param_def_id, ..) - | LateEarlyMismatch::LateInImpl(impl_param_def_id, ..)) = param; + | LateEarlyMismatch::LateInImpl(impl_param_def_id, ..)) = *param; tcx.def_span(impl_param_def_id) }) .collect(); @@ -1795,6 +1796,105 @@ fn compare_number_of_method_arguments<'tcx>( ), ); + // Only emit verbose suggestions when the trait span isn’t local (e.g., cross-crate). + if !trait_m.def_id.is_local() { + let trait_sig = tcx.fn_sig(trait_m.def_id); + let trait_arg_idents = tcx.fn_arg_idents(trait_m.def_id); + let sm = tcx.sess.source_map(); + // Find the span of the space between the parentheses in a method. + // fn foo(...) {} + // ^^^ + let impl_inputs_span = if let (Some(first), Some(last)) = + (impl_m_sig.decl.inputs.first(), impl_m_sig.decl.inputs.last()) + { + // We have inputs; construct the span from those. + // fn foo( a: i32, b: u32 ) {} + // ^^^^^^^^^^^^^^^^ + let arg_idents = tcx.fn_arg_idents(impl_m.def_id); + let first_lo = arg_idents + .get(0) + .and_then(|id| id.map(|id| id.span.lo())) + .unwrap_or(first.span.lo()); + Some(impl_m_sig.span.with_lo(first_lo).with_hi(last.span.hi())) + } else { + // We have no inputs; construct the span to the left of the last parenthesis + // fn foo( ) {} + // ^ + // FIXME: Keep spans for function parentheses around to make this more robust. + sm.span_to_snippet(impl_m_sig.span).ok().and_then(|s| { + let right_paren = s.as_bytes().iter().rposition(|&b| b == b')')?; + let pos = impl_m_sig.span.lo() + BytePos(right_paren as u32); + Some(impl_m_sig.span.with_lo(pos).with_hi(pos)) + }) + }; + let suggestion = match trait_number_args.cmp(&impl_number_args) { + Ordering::Greater => { + // Span is right before the end parenthesis: + // fn foo(a: i32 ) {} + // ^ + let trait_inputs = trait_sig.skip_binder().inputs().skip_binder(); + let missing = trait_inputs + .iter() + .enumerate() + .skip(impl_number_args) + .map(|(idx, ty)| { + let name = trait_arg_idents + .get(idx) + .and_then(|ident| *ident) + .map(|ident| ident.to_string()) + .unwrap_or_else(|| "_".to_string()); + format!("{name}: {ty}") + }) + .collect::>(); + + if missing.is_empty() { + None + } else { + impl_inputs_span.map(|s| { + let span = s.shrink_to_hi(); + let prefix = if impl_number_args == 0 { "" } else { ", " }; + let replacement = format!("{prefix}{}", missing.join(", ")); + ( + span, + format!( + "add the missing parameter{} from the trait", + pluralize!(trait_number_args - impl_number_args) + ), + replacement, + ) + }) + } + } + Ordering::Less => impl_inputs_span.and_then(|full| { + // Span of the arguments that there are too many of: + // fn foo(a: i32, b: u32) {} + // ^^^^^^^^ + let lo = if trait_number_args == 0 { + full.lo() + } else { + impl_m_sig + .decl + .inputs + .get(trait_number_args - 1) + .map(|arg| arg.span.hi())? + }; + let span = full.with_lo(lo); + Some(( + span, + format!( + "remove the extra parameter{} to match the trait", + pluralize!(impl_number_args - trait_number_args) + ), + String::new(), + )) + }), + Ordering::Equal => unreachable!(), + }; + if let Some((span, msg, replacement)) = suggestion { + err.span_suggestion_verbose(span, msg, replacement, Applicability::MaybeIncorrect); + } + } + return Err(err.emit_unless_delay(delay)); } @@ -2358,7 +2458,7 @@ pub(super) fn check_type_bounds<'tcx>( ) -> Result<(), ErrorGuaranteed> { // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case // other `Foo` impls are incoherent. - tcx.ensure_ok().coherent_trait(impl_trait_ref.def_id)?; + tcx.ensure_result().coherent_trait(impl_trait_ref.def_id)?; let param_env = tcx.param_env(impl_ty.def_id); debug!(?param_env); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index c20e5146546a..31104411ab55 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -6,6 +6,7 @@ use rustc_lint_defs::builtin::{REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE}; use rustc_middle::span_bug; use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::print::{with_no_trimmed_paths, with_types_for_signature}; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, @@ -332,6 +333,17 @@ fn report_mismatched_rpitit_signature<'tcx>( hir::FnRetTy::Return(ty) => ty.span, }); + // Use ForSignature mode to ensure RPITITs are printed as `impl Trait` rather than + // `impl Trait { T::method(..) }` when RTN is enabled. + // + // We use `with_no_trimmed_paths!` to avoid triggering the `trimmed_def_paths` query, + // which requires diagnostic context (via `must_produce_diag`). Since we're formatting + // the type before creating the diagnostic, we need to avoid this query. This is the + // standard approach used elsewhere in the compiler for formatting types in suggestions + // (e.g., see `rustc_hir_typeck/src/demand.rs`). + let return_ty_suggestion = + with_no_trimmed_paths!(with_types_for_signature!(format!("{return_ty}"))); + let span = unmatched_bound.unwrap_or(span); tcx.emit_node_span_lint( if is_internal { REFINING_IMPL_TRAIT_INTERNAL } else { REFINING_IMPL_TRAIT_REACHABLE }, @@ -342,7 +354,7 @@ fn report_mismatched_rpitit_signature<'tcx>( trait_return_span, pre, post, - return_ty, + return_ty: return_ty_suggestion, unmatched_bound, }, ); diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index 207cb83bcc8e..a6dae521db88 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -2,7 +2,6 @@ use rustc_abi::ExternAbi; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::{Node, find_attr}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::span_bug; @@ -99,9 +98,7 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option { error = true; } - if let Some(attr_span) = - find_attr!(tcx.get_all_attrs(main_def_id), AttributeKind::TrackCaller(span) => *span) - { + if let Some(attr_span) = find_attr!(tcx, main_def_id, TrackCaller(span) => *span) { tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span }); error = true; } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 6946d1a70040..a1c8c0150a66 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -77,7 +77,6 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::autodiff | sym::bitreverse | sym::black_box - | sym::box_new | sym::breakpoint | sym::bswap | sym::caller_location @@ -118,6 +117,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::fabsf128 | sym::fadd_algebraic | sym::fdiv_algebraic + | sym::field_offset | sym::floorf16 | sym::floorf32 | sym::floorf64 @@ -214,15 +214,16 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::truncf128 | sym::type_id | sym::type_id_eq + | sym::type_id_vtable | sym::type_name | sym::type_of | sym::ub_checks | sym::va_copy | sym::variant_count - | sym::vtable_for | sym::wrapping_add | sym::wrapping_mul | sym::wrapping_sub + | sym::write_box_via_move // tidy-alphabetical-end => hir::Safety::Safe, _ => hir::Safety::Unsafe, @@ -297,6 +298,7 @@ pub(crate) fn check_intrinsic_type( (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], 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)), sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()), sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => { @@ -323,6 +325,25 @@ pub(crate) fn check_intrinsic_type( let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); (0, 0, vec![type_id, type_id], 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); + let dyn_metadata_args = + tcx.mk_args(&[Ty::new_ptr(tcx, tcx.types.unit, ty::Mutability::Not).into()]); + let dyn_ty = Ty::new_adt(tcx, dyn_metadata_adt_ref, dyn_metadata_args); + + let option_did = tcx.require_lang_item(LangItem::Option, span); + let option_adt_ref = tcx.adt_def(option_did); + let option_args = tcx.mk_args(&[dyn_ty.into()]); + let ret_ty = Ty::new_adt(tcx, option_adt_ref, option_args); + + ( + 0, + 0, + vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); 2], + ret_ty, + ) + } sym::type_of => ( 0, 0, @@ -584,6 +605,13 @@ pub(crate) fn check_intrinsic_type( sym::write_via_move => { (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit) } + sym::write_box_via_move => { + let t = param(0); + let maybe_uninit_t = Ty::new_maybe_uninit(tcx, t); + let box_mu_t = Ty::new_box(tcx, maybe_uninit_t); + + (1, 0, vec![box_mu_t, param(0)], box_mu_t) + } sym::typed_swap_nonoverlapping => { (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)); 2], tcx.types.unit) @@ -668,20 +696,6 @@ pub(crate) fn check_intrinsic_type( (0, 0, vec![Ty::new_imm_ptr(tcx, tcx.types.unit)], tcx.types.usize) } - sym::vtable_for => { - let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span); - let dyn_metadata_adt_ref = tcx.adt_def(dyn_metadata); - let dyn_metadata_args = tcx.mk_args(&[param(1).into()]); - let dyn_ty = Ty::new_adt(tcx, dyn_metadata_adt_ref, dyn_metadata_args); - - let option_did = tcx.require_lang_item(LangItem::Option, span); - let option_adt_ref = tcx.adt_def(option_did); - let option_args = tcx.mk_args(&[dyn_ty.into()]); - let ret_ty = Ty::new_adt(tcx, option_adt_ref, option_args); - - (2, 0, vec![], ret_ty) - } - // This type check is not particularly useful, but the `where` bounds // on the definition in `core` do the heavy lifting for checking it. sym::aggregate_raw_ptr => (3, 0, vec![param(1), param(2)], param(0)), @@ -689,8 +703,6 @@ pub(crate) fn check_intrinsic_type( sym::ub_checks | sym::overflow_checks => (0, 0, Vec::new(), tcx.types.bool), - sym::box_new => (1, 0, vec![param(0)], Ty::new_box(tcx, param(0))), - // contract_check_requires::(C) -> bool, where C: impl Fn() -> bool sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.unit), sym::contract_check_ensures => { diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index d6ae14a7acfe..458bb6ddd211 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -77,7 +77,7 @@ pub use check::{check_abi, check_custom_abi}; use rustc_abi::VariantIdx; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err}; +use rustc_errors::{ErrorGuaranteed, pluralize, struct_span_code_err}; use rustc_hir::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; @@ -133,7 +133,12 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option } fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - tcx.calculate_async_dtor(def_id, always_applicable::check_drop_impl) + 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"); + } + result } /// Given a `DefId` for an opaque type in return position, find its parent item's return @@ -197,18 +202,6 @@ pub(super) fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDef } } -fn report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId) { - let span = tcx.def_span(impl_item); - let ident = tcx.item_ident(impl_item); - - let err = match tcx.span_of_impl(parent_impl) { - Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp }, - Err(cname) => errors::ImplNotMarkedDefault::Err { span, ident, cname }, - }; - - tcx.dcx().emit_err(err); -} - fn missing_items_err( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, @@ -557,7 +550,7 @@ fn suggestion_signature<'tcx>( ); format!("type {}{generics} = /* Type */{where_clauses};", assoc.name()) } - ty::AssocKind::Const { name } => { + ty::AssocKind::Const { name, .. } => { let ty = tcx.type_of(assoc.def_id).instantiate_identity(); let val = tcx .infer_ctxt() diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index b1daf2f2be9c..5656c4566d9f 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -3,10 +3,12 @@ use hir::intravisit::{self, Visitor}; use rustc_abi::{ExternAbi, ScalableElt}; +use rustc_ast as ast; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, msg, pluralize, struct_span_code_err}; -use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl, EiiImplResolution}; +use rustc_hir as hir; +use rustc_hir::attrs::{EiiDecl, EiiImpl, EiiImplResolution}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; @@ -14,7 +16,7 @@ use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, SubregionOrigin, TyCtxtInferExt}; use rustc_lint_defs::builtin::SHADOWING_SUPERTRAIT_ITEMS; -use rustc_macros::LintDiagnostic; +use rustc_macros::Diagnostic; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::traits::solve::NoSolution; use rustc_middle::ty::trait_def::TraitSpecializationKind; @@ -37,7 +39,6 @@ WellFormedLoc, }; use tracing::{debug, instrument}; -use {rustc_ast as ast, rustc_hir as hir}; use super::compare_eii::compare_eii_function_types; use crate::autoderef::Autoderef; @@ -935,7 +936,7 @@ pub(crate) fn check_associated_item( // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case // other `Foo` impls are incoherent. - tcx.ensure_ok().coherent_trait(tcx.parent(item.trait_item_or_self()?))?; + tcx.ensure_result().coherent_trait(tcx.parent(item.trait_item_or_self()?))?; let self_ty = match item.container { ty::AssocContainer::Trait => tcx.types.self_param, @@ -997,7 +998,7 @@ fn check_type_defn<'tcx>( item: &hir::Item<'tcx>, all_sized: bool, ) -> Result<(), ErrorGuaranteed> { - let _ = tcx.representability(item.owner_id.def_id); + let _ = tcx.check_representability(item.owner_id.def_id); let adt_def = tcx.adt_def(item.owner_id); enter_wf_checking_ctxt(tcx, item.owner_id.def_id, |wfcx| { @@ -1197,15 +1198,14 @@ fn check_eiis(tcx: TyCtxt<'_>, def_id: LocalDefId) { // does the function have an EiiImpl attribute? that contains the defid of a *macro* // that was used to mark the implementation. This is a two step process. for EiiImpl { resolution, span, .. } in - find_attr!(tcx.get_all_attrs(def_id), AttributeKind::EiiImpls(impls) => impls) - .into_iter() - .flatten() + find_attr!(tcx, def_id, EiiImpls(impls) => impls).into_iter().flatten() { let (foreign_item, name) = match resolution { EiiImplResolution::Macro(def_id) => { // we expect this macro to have the `EiiMacroFor` attribute, that points to a function // signature that we'd like to compare the function we're currently checking with - if let Some(foreign_item) = find_attr!(tcx.get_all_attrs(*def_id), AttributeKind::EiiDeclaration(EiiDecl {foreign_item: t, ..}) => *t) + if let Some(foreign_item) = + find_attr!(tcx, *def_id, EiiDeclaration(EiiDecl {foreign_item: t, ..}) => *t) { (foreign_item, tcx.item_name(*def_id)) } else { @@ -1328,9 +1328,9 @@ fn check_impl<'tcx>( // therefore don't need to be WF (the trait's `Self: Trait` predicate // won't hold). let trait_ref = tcx.impl_trait_ref(item.owner_id).instantiate_identity(); - // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case - // other `Foo` impls are incoherent. - tcx.ensure_ok().coherent_trait(trait_ref.def_id)?; + // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in + // case other `Foo` impls are incoherent. + tcx.ensure_result().coherent_trait(trait_ref.def_id)?; let trait_span = of_trait.trait_ref.path.span; let trait_ref = wfcx.deeply_normalize( trait_span, @@ -2334,15 +2334,22 @@ fn check_false_global_bounds(&mut self) { pub(super) fn check_type_wf(tcx: TyCtxt<'_>, (): ()) -> Result<(), ErrorGuaranteed> { let items = tcx.hir_crate_items(()); - let res = items - .par_items(|item| tcx.ensure_ok().check_well_formed(item.owner_id.def_id)) - .and(items.par_impl_items(|item| tcx.ensure_ok().check_well_formed(item.owner_id.def_id))) - .and(items.par_trait_items(|item| tcx.ensure_ok().check_well_formed(item.owner_id.def_id))) - .and( - items.par_foreign_items(|item| tcx.ensure_ok().check_well_formed(item.owner_id.def_id)), - ) - .and(items.par_nested_bodies(|item| tcx.ensure_ok().check_well_formed(item))) - .and(items.par_opaques(|item| tcx.ensure_ok().check_well_formed(item))); + let res = + items + .par_items(|item| tcx.ensure_result().check_well_formed(item.owner_id.def_id)) + .and( + items.par_impl_items(|item| { + tcx.ensure_result().check_well_formed(item.owner_id.def_id) + }), + ) + .and(items.par_trait_items(|item| { + tcx.ensure_result().check_well_formed(item.owner_id.def_id) + })) + .and(items.par_foreign_items(|item| { + tcx.ensure_result().check_well_formed(item.owner_id.def_id) + })) + .and(items.par_nested_bodies(|item| tcx.ensure_result().check_well_formed(item))) + .and(items.par_opaques(|item| tcx.ensure_result().check_well_formed(item))); super::entry::check_for_entry_fn(tcx); res @@ -2361,11 +2368,11 @@ fn lint_redundant_lifetimes<'tcx>( | DefKind::Trait | DefKind::TraitAlias | DefKind::Fn - | DefKind::Const + | DefKind::Const { .. } | DefKind::Impl { of_trait: _ } => { // Proceed } - DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { + DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst { .. } => { if tcx.trait_impl_of_assoc(owner_id.to_def_id()).is_some() { // Don't check for redundant lifetimes for associated items of trait // implementations, since the signature is required to be compatible @@ -2470,7 +2477,7 @@ fn lint_redundant_lifetimes<'tcx>( } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unnecessary lifetime parameter `{$victim}`")] #[note("you can use the `{$candidate}` lifetime directly, in place of `{$victim}`")] struct RedundantLifetimeArgsLint<'tcx> { diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs index 3fb33c741c9d..7302913cc1ae 100644 --- a/compiler/rustc_hir_analysis/src/check_unused.rs +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -1,10 +1,28 @@ use rustc_data_structures::unord::{ExtendUnord, UnordSet}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::TyCtxt; use rustc_session::lint; +use rustc_span::Span; use tracing::debug; +struct UnusedImport<'tcx> { + tcx: TyCtxt<'tcx>, + span: Span, +} + +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}`")) + } else { + Diag::new(dcx, level, "unused import") + } + } +} + pub(super) fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) { let mut used_trait_imports = UnordSet::::default(); @@ -31,12 +49,11 @@ pub(super) fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) { continue; } let (path, _) = item.expect_use(); - tcx.node_span_lint(lint::builtin::UNUSED_IMPORTS, item.hir_id(), path.span, |lint| { - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) { - lint.primary_message(format!("unused import: `{snippet}`")); - } else { - lint.primary_message("unused import"); - } - }); + tcx.emit_node_span_lint( + lint::builtin::UNUSED_IMPORTS, + item.hir_id(), + path.span, + UnusedImport { tcx, span: path.span }, + ); } } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 466dc262764c..abd5c024ef79 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -226,7 +226,7 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E // Just compute this for the side-effects, in particular reporting // errors; other parts of the code may demand it for the info of // course. - tcx.ensure_ok().coerce_unsized_info(impl_did) + tcx.ensure_result().coerce_unsized_info(impl_did) } fn is_from_coerce_pointee_derive(tcx: TyCtxt<'_>, span: Span) -> bool { @@ -259,7 +259,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() tcx.require_lang_item(LangItem::CoerceUnsized, span), source, |impl_def_id| { - res = res.and(tcx.ensure_ok().coerce_unsized_info(impl_def_id)); + res = res.and(tcx.ensure_result().coerce_unsized_info(impl_def_id)); }, ); res?; diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 588747f46d17..0050fea988f8 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -8,7 +8,6 @@ //! is computed by selecting an idea from this table. use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::find_attr; @@ -79,20 +78,14 @@ fn check_def_id( } if self.tcx.features().rustc_attrs() { - if !find_attr!( - self.tcx.get_all_attrs(ty_def_id), - AttributeKind::RustcHasIncoherentInherentImpls - ) { + if !find_attr!(self.tcx, ty_def_id, RustcHasIncoherentInherentImpls) { let impl_span = self.tcx.def_span(impl_def_id); return Err(self.tcx.dcx().emit_err(errors::InherentTyOutside { span: impl_span })); } let items = self.tcx.associated_item_def_ids(impl_def_id); for &impl_item in items { - if !find_attr!( - self.tcx.get_all_attrs(impl_item), - AttributeKind::RustcAllowIncoherentImpl(_) - ) { + if !find_attr!(self.tcx, impl_item, RustcAllowIncoherentImpl(_)) { let impl_span = self.tcx.def_span(impl_def_id); return Err(self.tcx.dcx().emit_err(errors::InherentTyOutsideRelevant { span: impl_span, @@ -138,10 +131,7 @@ fn check_primitive_impl( if !self.tcx.hir_rustc_coherence_is_core() { if self.tcx.features().rustc_attrs() { for &impl_item in items { - if !find_attr!( - self.tcx.get_all_attrs(impl_item), - AttributeKind::RustcAllowIncoherentImpl(_) - ) { + if !find_attr!(self.tcx, impl_item, RustcAllowIncoherentImpl(_)) { let span = self.tcx.def_span(impl_def_id); return Err(self.tcx.dcx().emit_err(errors::InherentTyOutsidePrimitive { span, diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 58483dc44fe9..8f83761518bd 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -160,7 +160,7 @@ fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed> } // Trigger building the specialization graph for the trait. This will detect and report any // overlap errors. - let mut res = tcx.ensure_ok().specialization_graph_of(def_id); + let mut res = tcx.ensure_result().specialization_graph_of(def_id); for &impl_def_id in impls { let impl_header = tcx.impl_trait_header(impl_def_id); @@ -171,7 +171,7 @@ fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed> .and(check_impl(tcx, impl_def_id, trait_ref, trait_def, impl_header.polarity)) .and(check_object_overlap(tcx, impl_def_id, trait_ref)) .and(unsafety::check_item(tcx, impl_def_id, impl_header, trait_def)) - .and(tcx.ensure_ok().orphan_check_impl(impl_def_id)) + .and(tcx.ensure_result().orphan_check_impl(impl_def_id)) .and(builtin::check_trait(tcx, def_id, impl_def_id, impl_header)); } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 2fc1f1ab01d2..80ef2001cc72 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -15,15 +15,15 @@ //! crate as a kind of pass. This should eventually be factored away. use std::cell::Cell; -use std::iter; use std::ops::{Bound, ControlFlow}; +use std::{assert_matches, iter}; use rustc_abi::{ExternAbi, Size}; use rustc_ast::Recovered; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_errors::{Applicability, Diag, DiagCtxtHandle, E0228, ErrorGuaranteed, StashKey}; -use rustc_hir::attrs::AttributeKind; +use rustc_errors::{ + Applicability, Diag, DiagCtxtHandle, Diagnostic, E0228, ErrorGuaranteed, Level, StashKey, +}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt}; @@ -93,7 +93,6 @@ pub(crate) fn provide(providers: &mut Providers) { const_param_default, anon_const_kind, const_of_item, - is_rhs_type_const, ..*providers }; } @@ -612,6 +611,19 @@ pub(super) fn lower_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) { } pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: LocalDefId) { + struct ReprCIssue { + msg: &'static str, + } + + 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") + .with_help("use `repr($int_ty)` instead to explicitly set the size of this enum") + } + } + let def = tcx.adt_def(def_id); let repr_type = def.repr().discr_type(); let initial = repr_type.initial_discriminant(tcx); @@ -661,15 +673,11 @@ pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: LocalDefId) { } else { "`repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int`" }; - tcx.node_span_lint( + tcx.emit_node_span_lint( rustc_session::lint::builtin::REPR_C_ENUMS_LARGER_THAN_INT, tcx.local_def_id_to_hir_id(def_id), span, - |d| { - d.primary_message(msg) - .note("`repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C") - .help("use `repr($int_ty)` instead to explicitly set the size of this enum"); - } + ReprCIssue { msg }, ); } } @@ -816,11 +824,9 @@ fn lower_variant<'tcx>( fields, parent_did.to_def_id(), recovered, - adt_kind == AdtKind::Struct - && find_attr!(tcx.get_all_attrs(parent_did), AttributeKind::NonExhaustive(..)) - || variant_did.is_some_and(|variant_did| { - find_attr!(tcx.get_all_attrs(variant_did), AttributeKind::NonExhaustive(..)) - }), + adt_kind == AdtKind::Struct && find_attr!(tcx, parent_did, NonExhaustive(..)) + || variant_did + .is_some_and(|variant_did| find_attr!(tcx, variant_did, NonExhaustive(..))), ) } @@ -895,46 +901,46 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { _ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"), }; + // we do a bunch of find_attr calls here, probably faster to get them from the tcx just once. + #[allow(deprecated)] let attrs = tcx.get_all_attrs(def_id); - let paren_sugar = find_attr!(attrs, AttributeKind::RustcParenSugar(_)); + let paren_sugar = find_attr!(attrs, RustcParenSugar(_)); if paren_sugar && !tcx.features().unboxed_closures() { tcx.dcx().emit_err(errors::ParenSugarAttribute { span: item.span }); } // Only regular traits can be marker. - let is_marker = !is_alias && find_attr!(attrs, AttributeKind::Marker(_)); + let is_marker = !is_alias && find_attr!(attrs, Marker(_)); - let rustc_coinductive = find_attr!(attrs, AttributeKind::RustcCoinductive(_)); - let is_fundamental = find_attr!(attrs, AttributeKind::Fundamental); + let rustc_coinductive = find_attr!(attrs, RustcCoinductive(_)); + let is_fundamental = find_attr!(attrs, Fundamental); let [skip_array_during_method_dispatch, skip_boxed_slice_during_method_dispatch] = find_attr!( attrs, - AttributeKind::RustcSkipDuringMethodDispatch { array, boxed_slice, span: _ } => [*array, *boxed_slice] + RustcSkipDuringMethodDispatch { array, boxed_slice, span: _ } => [*array, *boxed_slice] ) .unwrap_or([false; 2]); - let specialization_kind = - if find_attr!(attrs, AttributeKind::RustcUnsafeSpecializationMarker(_)) { - ty::trait_def::TraitSpecializationKind::Marker - } else if find_attr!(attrs, AttributeKind::RustcSpecializationTrait(_)) { - ty::trait_def::TraitSpecializationKind::AlwaysApplicable - } else { - ty::trait_def::TraitSpecializationKind::None - }; + let specialization_kind = if find_attr!(attrs, RustcUnsafeSpecializationMarker(_)) { + ty::trait_def::TraitSpecializationKind::Marker + } else if find_attr!(attrs, RustcSpecializationTrait(_)) { + ty::trait_def::TraitSpecializationKind::AlwaysApplicable + } else { + ty::trait_def::TraitSpecializationKind::None + }; let must_implement_one_of = find_attr!( attrs, - AttributeKind::RustcMustImplementOneOf { fn_names, .. } => + RustcMustImplementOneOf { fn_names, .. } => fn_names .iter() .cloned() .collect::>() ); - let deny_explicit_impl = find_attr!(attrs, AttributeKind::RustcDenyExplicitImpl(_)); - let force_dyn_incompatible = - find_attr!(attrs, AttributeKind::RustcDynIncompatibleTrait(span) => *span); + let deny_explicit_impl = find_attr!(attrs, RustcDenyExplicitImpl(_)); + let force_dyn_incompatible = find_attr!(attrs, RustcDynIncompatibleTrait(span) => *span); ty::TraitDef { def_id: def_id.to_def_id(), @@ -1355,8 +1361,7 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplTraitHeader .of_trait .unwrap_or_else(|| panic!("expected impl trait, found inherent impl on {def_id:?}")); let selfty = tcx.type_of(def_id).instantiate_identity(); - let is_rustc_reservation = - find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcReservationImpl(..)); + let is_rustc_reservation = find_attr!(tcx, def_id, RustcReservationImpl(..)); check_impl_constness(tcx, impl_.constness, &of_trait.trait_ref); @@ -1706,22 +1711,3 @@ fn const_of_item<'tcx>( ty::EarlyBinder::bind(ct) } } - -/// Check if a Const or AssocConst is a type const (mgca) -fn is_rhs_type_const<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> bool { - match tcx.hir_node_by_def_id(def) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Const(_, _, _, hir::ConstItemRhs::TypeConst(_)), - .. - }) - | hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Const(_, hir::ConstItemRhs::TypeConst(_)), - .. - }) - | hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Const(_, _, hir::IsTypeConst::Yes), - .. - }) => return true, - _ => return false, - } -} diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index bbf912cd4bde..3e9c83b12df0 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -1,13 +1,13 @@ use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::LocalDefId; use rustc_hir::{find_attr, intravisit}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::sym; pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) { - if !find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcHiddenTypeOfOpaques) { + if !find_attr!(tcx, crate, RustcHiddenTypeOfOpaques) { return; } for id in tcx.hir_crate_items(()).opaques() { @@ -28,7 +28,10 @@ pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) { pub(crate) fn predicates_and_item_bounds(tcx: TyCtxt<'_>) { for id in tcx.hir_crate_items(()).owners() { - if find_attr!(tcx.get_all_attrs(id), AttributeKind::RustcDumpPredicates) { + #[expect(deprecated)] // we don't want to unnecessarily retrieve the attrs twice in a row. + let attrs = tcx.get_all_attrs(id); + + if find_attr!(attrs, RustcDumpPredicates) { let preds = tcx.predicates_of(id).instantiate_identity(tcx).predicates; let span = tcx.def_span(id); @@ -38,15 +41,26 @@ pub(crate) fn predicates_and_item_bounds(tcx: TyCtxt<'_>) { } diag.emit(); } - if find_attr!(tcx.get_all_attrs(id), AttributeKind::RustcDumpItemBounds) { - let bounds = tcx.item_bounds(id).instantiate_identity(); - let span = tcx.def_span(id); - let mut diag = tcx.dcx().struct_span_err(span, sym::rustc_dump_item_bounds.as_str()); - for bound in bounds { - diag.note(format!("{bound:?}")); - } - diag.emit(); + if find_attr!(attrs, RustcDumpItemBounds) { + let name = sym::rustc_dump_item_bounds.as_str(); + + match tcx.def_kind(id) { + DefKind::AssocTy => { + let bounds = tcx.item_bounds(id).instantiate_identity(); + let span = tcx.def_span(id); + + let mut diag = tcx.dcx().struct_span_err(span, name); + for bound in bounds { + diag.note(format!("{bound:?}")); + } + diag.emit() + } + kind => tcx.dcx().span_delayed_bug( + tcx.def_span(id), + format!("attr parsing didn't report an error for `#[{name}]` on {kind:?}"), + ), + }; } } } @@ -54,7 +68,7 @@ pub(crate) fn predicates_and_item_bounds(tcx: TyCtxt<'_>) { pub(crate) fn def_parents(tcx: TyCtxt<'_>) { for iid in tcx.hir_free_items() { let did = iid.owner_id.def_id; - if find_attr!(tcx.get_all_attrs(did), AttributeKind::RustcDumpDefParents) { + if find_attr!(tcx, did, RustcDumpDefParents) { struct AnonConstFinder<'tcx> { tcx: TyCtxt<'tcx>, anon_consts: Vec, @@ -102,9 +116,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { for id in tcx.hir_free_items() { let def_id = id.owner_id.def_id; - let Some(&attr_span) = - find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcDumpVtable(span) => span) - else { + let Some(&attr_span) = find_attr!(tcx, def_id, RustcDumpVtable(span) => span) else { continue; }; diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 0723418d8ddf..866787a45718 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -1,6 +1,7 @@ +use std::assert_matches; use std::ops::ControlFlow; -use rustc_data_structures::assert_matches; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; @@ -11,13 +12,23 @@ use rustc_span::{Span, Symbol, kw}; use tracing::{debug, instrument}; -use crate::delegation::inherit_generics_for_delegation_item; use crate::middle::resolve_bound_vars as rbv; #[instrument(level = "debug", skip(tcx), ret)] pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { use rustc_hir::*; + struct GenericParametersForbiddenHere { + msg: &'static str, + } + + 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) + } + } + // For an RPITIT, synthesize generics which are equal to the opaque's generics // and parent fn's generics compressed into one list. if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id }) = @@ -56,13 +67,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } let hir_id = tcx.local_def_id_to_hir_id(def_id); - let node = tcx.hir_node(hir_id); - if let Some(sig) = node.fn_sig() - && let Some(sig_id) = sig.decl.opt_delegation_sig_id() - { - return inherit_generics_for_delegation_item(tcx, def_id, sig_id); - } let parent_def_id = match node { Node::ImplItem(_) @@ -276,13 +281,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { match param_default_policy.expect("no policy for generic param default") { ParamDefaultPolicy::Allowed => {} ParamDefaultPolicy::FutureCompatForbidden => { - tcx.node_span_lint( + tcx.emit_node_span_lint( lint::builtin::INVALID_TYPE_PARAM_DEFAULT, param.hir_id, param.span, - |lint| { - lint.primary_message(MESSAGE); - }, + GenericParametersForbiddenHere { msg: MESSAGE }, ); } ParamDefaultPolicy::Forbidden => { diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index a2236b426305..e16fa5492979 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -1,8 +1,8 @@ +use std::assert_matches; + use hir::Node; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::find_attr; @@ -331,9 +331,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // first we would need a way to let std/core use APIs with unstable feature bounds from // within stable APIs. let allow_unstable_feature_attr = - find_attr!(attrs, AttributeKind::UnstableFeatureBound(i) => i) - .map(|i| i.as_slice()) - .unwrap_or_default(); + find_attr!(attrs, UnstableFeatureBound(i) => i).map(|i| i.as_slice()).unwrap_or_default(); for (feat_name, span) in allow_unstable_feature_attr { predicates.insert((ty::ClauseKind::UnstableFeature(*feat_name).upcast(tcx), *span)); @@ -441,6 +439,11 @@ fn visit_const(&mut self, c: ty::Const<'tcx>) { return; } + // Skip type consts as mGCA doesn't support evaluatable clauses. + if self.tcx.is_type_const(uv.def) { + return; + } + let span = self.tcx.def_span(uv.def); self.preds.insert((ty::ClauseKind::ConstEvaluatable(c).upcast(self.tcx), span)); } 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 ff919bd58ba1..18244d62f2ae 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -2445,12 +2445,12 @@ fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { )) => { // See comments on `ConstrainedCollectorPostHirTyLowering` for why this arm does not // just consider args to be unconstrained. - let generics = self.tcx.generics_of(alias_def); + let generics = self.tcx.generics_of(*alias_def); let mut walker = ConstrainedCollectorPostHirTyLowering { arg_is_constrained: vec![false; generics.own_params.len()] .into_boxed_slice(), }; - walker.visit_ty(self.tcx.type_of(alias_def).instantiate_identity()); + walker.visit_ty(self.tcx.type_of(*alias_def).instantiate_identity()); match segments.last() { Some(hir::PathSegment { args: Some(args), .. }) => { diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index f64341d755f8..ad23868fffd6 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -2,14 +2,19 @@ //! //! For more information about delegation design, see the tracking issue #118212. -use rustc_data_structures::debug_assert_matches; +use std::debug_assert_matches; + use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{HirId, PathSegment}; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + self, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Span, kw}; + +use crate::collect::ItemCtxt; +use crate::hir_ty_lowering::{GenericArgPosition, HirTyLowerer}; type RemapTable = FxHashMap; @@ -59,6 +64,25 @@ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { } } +enum SelfPositionKind { + AfterLifetimes, + Zero, + None, +} + +fn create_self_position_kind(caller_kind: FnKind, callee_kind: FnKind) -> SelfPositionKind { + match (caller_kind, callee_kind) { + (FnKind::AssocInherentImpl, FnKind::AssocTrait) + | (FnKind::AssocTraitImpl, FnKind::AssocTrait) + | (FnKind::AssocTrait, FnKind::AssocTrait) + | (FnKind::AssocTrait, FnKind::Free) => SelfPositionKind::Zero, + + (FnKind::Free, FnKind::AssocTrait) => SelfPositionKind::AfterLifetimes, + + _ => SelfPositionKind::None, + } +} + #[derive(Clone, Copy, Debug, PartialEq)] enum FnKind { Free, @@ -67,7 +91,9 @@ enum FnKind { AssocTraitImpl, } -fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> FnKind { +fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: impl Into) -> FnKind { + let def_id = def_id.into(); + debug_assert_matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn); let parent = tcx.parent(def_id); @@ -99,103 +125,338 @@ enum InheritanceKind { Own, } -fn build_generics<'tcx>( +/// Maps sig generics into generic args of delegation. Delegation generics has the following pattern: +/// +/// [SELF | maybe self in the beginning] +/// [PARENT | args of delegation parent] +/// [SIG PARENT LIFETIMES] +/// [SIG LIFETIMES] +/// [SELF | maybe self after lifetimes, when we reuse trait fn in free context] +/// [SIG PARENT TYPES/CONSTS] +/// [SIG TYPES/CONSTS] +fn create_mapping<'tcx>( tcx: TyCtxt<'tcx>, sig_id: DefId, - parent: Option, - inh_kind: InheritanceKind, -) -> ty::Generics { - let mut own_params = vec![]; + def_id: LocalDefId, + args: &[ty::GenericArg<'tcx>], +) -> FxHashMap { + let mut mapping: FxHashMap = Default::default(); + + let (caller_kind, callee_kind) = (fn_kind(tcx, def_id), fn_kind(tcx, sig_id)); + let self_pos_kind = create_self_position_kind(caller_kind, callee_kind); + let is_self_at_zero = matches!(self_pos_kind, SelfPositionKind::Zero); + + // Is self at zero? If so insert mapping, self in sig parent is always at 0. + if is_self_at_zero { + mapping.insert(0, 0); + } + + let mut args_index = 0; + + args_index += is_self_at_zero as usize; + args_index += get_delegation_parent_args_count_without_self(tcx, def_id, sig_id); let sig_generics = tcx.generics_of(sig_id); - if let InheritanceKind::WithParent(has_self) = inh_kind - && let Some(parent_def_id) = sig_generics.parent - { - let sig_parent_generics = tcx.generics_of(parent_def_id); - own_params.append(&mut sig_parent_generics.own_params.clone()); - if !has_self { - own_params.remove(0); - } - } - own_params.append(&mut sig_generics.own_params.clone()); + let process_sig_parent_generics = matches!(callee_kind, FnKind::AssocTrait); - // Lifetime parameters must be declared before type and const parameters. - // Therefore, When delegating from a free function to a associated function, - // generic parameters need to be reordered: - // - // trait Trait<'a, A> { - // fn foo<'b, B>(...) {...} - // } - // - // reuse Trait::foo; - // desugaring: - // fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) { - // Trait::foo(...) - // } - own_params.sort_by_key(|key| key.kind.is_ty_or_const()); - - let (parent_count, has_self) = if let Some(def_id) = parent { - let parent_generics = tcx.generics_of(def_id); - let parent_kind = tcx.def_kind(def_id); - (parent_generics.count(), parent_kind == DefKind::Trait) - } else { - (0, false) - }; - - for (idx, param) in own_params.iter_mut().enumerate() { - param.index = (idx + parent_count) as u32; - // FIXME(fn_delegation): Default parameters are not inherited, because they are - // not permitted in functions. Therefore, there are 2 options here: - // - // - We can create non-default generic parameters. - // - We can substitute default parameters into the signature. - // - // At the moment, first option has been selected as the most general. - if let ty::GenericParamDefKind::Type { has_default, .. } - | ty::GenericParamDefKind::Const { has_default, .. } = &mut param.kind - { - *has_default = false; + if process_sig_parent_generics { + for i in (sig_generics.has_self as usize)..sig_generics.parent_count { + let param = sig_generics.param_at(i, tcx); + if !param.kind.is_ty_or_const() { + mapping.insert(param.index, args_index as u32); + args_index += 1; + } } } - let param_def_id_to_index = - own_params.iter().map(|param| (param.def_id, param.index)).collect(); + for param in &sig_generics.own_params { + if !param.kind.is_ty_or_const() { + mapping.insert(param.index, args_index as u32); + args_index += 1; + } + } - ty::Generics { - parent, - parent_count, - own_params, - param_def_id_to_index, - has_self, - has_late_bound_regions: sig_generics.has_late_bound_regions, + // If there are still unmapped lifetimes left and we are to map types and maybe self + // then skip them, now it is the case when we generated more lifetimes then needed. + // FIXME(fn_delegation): proper support for late bound lifetimes. + while args_index < args.len() && args[args_index].as_region().is_some() { + args_index += 1; + } + + // If self after lifetimes insert mapping, relying that self is at 0 in sig parent. + if matches!(self_pos_kind, SelfPositionKind::AfterLifetimes) { + mapping.insert(0, args_index as u32); + args_index += 1; + } + + if process_sig_parent_generics { + for i in (sig_generics.has_self as usize)..sig_generics.parent_count { + let param = sig_generics.param_at(i, tcx); + if param.kind.is_ty_or_const() { + mapping.insert(param.index, args_index as u32); + args_index += 1; + } + } + } + + for param in &sig_generics.own_params { + if param.kind.is_ty_or_const() { + mapping.insert(param.index, args_index as u32); + args_index += 1; + } + } + + mapping +} + +fn get_delegation_parent_args_count_without_self<'tcx>( + tcx: TyCtxt<'tcx>, + delegation_id: LocalDefId, + sig_id: DefId, +) -> usize { + let delegation_parent_args_count = tcx.generics_of(delegation_id).parent_count; + + match (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id)) { + (FnKind::Free, FnKind::Free) + | (FnKind::Free, FnKind::AssocTrait) + | (FnKind::AssocTraitImpl, FnKind::AssocTrait) => 0, + + (FnKind::AssocInherentImpl, FnKind::Free) + | (FnKind::AssocInherentImpl, FnKind::AssocTrait) => { + delegation_parent_args_count /* No Self in AssocInherentImpl */ + } + + (FnKind::AssocTrait, FnKind::Free) | (FnKind::AssocTrait, FnKind::AssocTrait) => { + delegation_parent_args_count - 1 /* Without Self */ + } + + // For trait impl's `sig_id` is always equal to the corresponding trait method. + // For inherent methods delegation is not yet supported. + (FnKind::AssocTraitImpl, _) + | (_, FnKind::AssocTraitImpl) + | (_, FnKind::AssocInherentImpl) => unreachable!(), } } -fn build_predicates<'tcx>( +fn get_parent_and_inheritance_kind<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + sig_id: DefId, +) -> (Option, InheritanceKind) { + match (fn_kind(tcx, def_id), fn_kind(tcx, sig_id)) { + (FnKind::Free, FnKind::Free) | (FnKind::Free, FnKind::AssocTrait) => { + (None, InheritanceKind::WithParent(true)) + } + + (FnKind::AssocTraitImpl, FnKind::AssocTrait) => { + (Some(tcx.parent(def_id.to_def_id())), InheritanceKind::Own) + } + + (FnKind::AssocInherentImpl, FnKind::AssocTrait) + | (FnKind::AssocTrait, FnKind::AssocTrait) + | (FnKind::AssocInherentImpl, FnKind::Free) + | (FnKind::AssocTrait, FnKind::Free) => { + (Some(tcx.parent(def_id.to_def_id())), InheritanceKind::WithParent(false)) + } + + // For trait impl's `sig_id` is always equal to the corresponding trait method. + // For inherent methods delegation is not yet supported. + (FnKind::AssocTraitImpl, _) + | (_, FnKind::AssocTraitImpl) + | (_, FnKind::AssocInherentImpl) => unreachable!(), + } +} + +fn get_delegation_self_ty<'tcx>(tcx: TyCtxt<'tcx>, delegation_id: LocalDefId) -> Option> { + let sig_id = tcx.hir_opt_delegation_sig_id(delegation_id).expect("Delegation must have sig_id"); + let (caller_kind, callee_kind) = (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id)); + + match (caller_kind, callee_kind) { + (FnKind::Free, FnKind::AssocTrait) + | (FnKind::AssocInherentImpl, FnKind::Free) + | (FnKind::Free, FnKind::Free) + | (FnKind::AssocTrait, FnKind::Free) + | (FnKind::AssocTrait, FnKind::AssocTrait) => { + match create_self_position_kind(caller_kind, callee_kind) { + SelfPositionKind::None => None, + SelfPositionKind::AfterLifetimes => { + // Both sig parent and child lifetimes are in included in this count. + Some(tcx.generics_of(delegation_id).own_counts().lifetimes) + } + SelfPositionKind::Zero => Some(0), + } + .map(|self_index| Ty::new_param(tcx, self_index as u32, kw::SelfUpper)) + } + + (FnKind::AssocTraitImpl, FnKind::AssocTrait) + | (FnKind::AssocInherentImpl, FnKind::AssocTrait) => { + Some(tcx.type_of(tcx.local_parent(delegation_id)).instantiate_identity()) + } + + // For trait impl's `sig_id` is always equal to the corresponding trait method. + // For inherent methods delegation is not yet supported. + (FnKind::AssocTraitImpl, _) + | (_, FnKind::AssocTraitImpl) + | (_, FnKind::AssocInherentImpl) => unreachable!(), + } +} + +/// Creates generic arguments for further delegation signature and predicates instantiation. +/// Arguments can be user-specified (in this case they are in `parent_args` and `child_args`) +/// or propagated. User can specify either both `parent_args` and `child_args`, one of them or none, +/// that is why we firstly create generic arguments from generic params and then adjust them with +/// user-specified args. +/// +/// The order of produced list is important, it must be of this pattern: +/// +/// [SELF | maybe self in the beginning] +/// [PARENT | args of delegation parent] +/// [SIG PARENT LIFETIMES] <- `lifetimes_end_pos` +/// [SIG LIFETIMES] +/// [SELF | maybe self after lifetimes, when we reuse trait fn in free context] +/// [SIG PARENT TYPES/CONSTS] +/// [SIG TYPES/CONSTS] +fn create_generic_args<'tcx>( tcx: TyCtxt<'tcx>, sig_id: DefId, - parent: Option, - inh_kind: InheritanceKind, - args: ty::GenericArgsRef<'tcx>, + delegation_id: LocalDefId, + mut parent_args: &[ty::GenericArg<'tcx>], + child_args: &[ty::GenericArg<'tcx>], +) -> Vec> { + let (caller_kind, callee_kind) = (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id)); + + let delegation_args = ty::GenericArgs::identity_for_item(tcx, delegation_id); + let delegation_parent_args_count = tcx.generics_of(delegation_id).parent_count; + + let deleg_parent_args_without_self_count = + get_delegation_parent_args_count_without_self(tcx, delegation_id, sig_id); + + let args = match (caller_kind, callee_kind) { + (FnKind::Free, FnKind::Free) + | (FnKind::Free, FnKind::AssocTrait) + | (FnKind::AssocInherentImpl, FnKind::Free) + | (FnKind::AssocTrait, FnKind::Free) + | (FnKind::AssocTrait, FnKind::AssocTrait) => delegation_args, + + (FnKind::AssocTraitImpl, FnKind::AssocTrait) => { + // Special case, as user specifies Trait args in impl trait header, we want to treat + // them as parent args. + let parent = tcx.local_parent(delegation_id); + parent_args = tcx.impl_trait_header(parent).trait_ref.instantiate_identity().args; + tcx.mk_args(&delegation_args[delegation_parent_args_count..]) + } + + (FnKind::AssocInherentImpl, FnKind::AssocTrait) => { + let self_ty = tcx.type_of(tcx.local_parent(delegation_id)).instantiate_identity(); + + tcx.mk_args_from_iter( + std::iter::once(ty::GenericArg::from(self_ty)).chain(delegation_args.iter()), + ) + } + + // For trait impl's `sig_id` is always equal to the corresponding trait method. + // For inherent methods delegation is not yet supported. + (FnKind::AssocTraitImpl, _) + | (_, FnKind::AssocTraitImpl) + | (_, FnKind::AssocInherentImpl) => unreachable!(), + }; + + let mut new_args = vec![]; + + let self_pos_kind = create_self_position_kind(caller_kind, callee_kind); + let mut lifetimes_end_pos; + + if !parent_args.is_empty() { + let parent_args_lifetimes_count = + parent_args.iter().filter(|a| a.as_region().is_some()).count(); + + match self_pos_kind { + SelfPositionKind::AfterLifetimes => { + new_args.extend(&parent_args[1..1 + parent_args_lifetimes_count]); + + lifetimes_end_pos = parent_args_lifetimes_count; + + new_args.push(parent_args[0]); + + new_args.extend(&parent_args[1 + parent_args_lifetimes_count..]); + } + SelfPositionKind::Zero => { + lifetimes_end_pos = 1 /* Self */ + parent_args_lifetimes_count; + new_args.extend_from_slice(parent_args); + + for i in 0..deleg_parent_args_without_self_count { + new_args.insert(1 + i, args[1 + i]); + } + + lifetimes_end_pos += deleg_parent_args_without_self_count; + } + // If we have parent args then we obtained them from trait, then self must be somewhere + SelfPositionKind::None => unreachable!(), + }; + } else { + let self_impact = matches!(self_pos_kind, SelfPositionKind::Zero) as usize; + + lifetimes_end_pos = self_impact + + deleg_parent_args_without_self_count + + &args[self_impact + deleg_parent_args_without_self_count..] + .iter() + .filter(|a| a.as_region().is_some()) + .count(); + + new_args.extend_from_slice(args); + } + + if !child_args.is_empty() { + let child_lifetimes_count = child_args.iter().filter(|a| a.as_region().is_some()).count(); + + for i in 0..child_lifetimes_count { + new_args.insert(lifetimes_end_pos + i, child_args[i]); + } + + new_args.extend_from_slice(&child_args[child_lifetimes_count..]); + } else if !parent_args.is_empty() { + let child_args = &delegation_args[delegation_parent_args_count..]; + + let child_lifetimes_count = + child_args.iter().take_while(|a| a.as_region().is_some()).count(); + + for i in 0..child_lifetimes_count { + new_args.insert(lifetimes_end_pos + i, child_args[i]); + } + + let skip_self = matches!(self_pos_kind, SelfPositionKind::AfterLifetimes); + new_args.extend(&child_args[child_lifetimes_count + skip_self as usize..]); + } + + new_args +} + +pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + sig_id: DefId, ) -> ty::GenericPredicates<'tcx> { struct PredicatesCollector<'tcx> { tcx: TyCtxt<'tcx>, preds: Vec<(ty::Clause<'tcx>, Span)>, - args: ty::GenericArgsRef<'tcx>, + args: Vec>, + folder: ParamIndexRemapper<'tcx>, } impl<'tcx> PredicatesCollector<'tcx> { - fn new(tcx: TyCtxt<'tcx>, args: ty::GenericArgsRef<'tcx>) -> PredicatesCollector<'tcx> { - PredicatesCollector { tcx, preds: vec![], args } - } - fn with_own_preds( mut self, f: impl Fn(DefId) -> ty::GenericPredicates<'tcx>, def_id: DefId, ) -> Self { - let preds = f(def_id).instantiate_own(self.tcx, self.args); - self.preds.extend(preds); + let preds = f(def_id); + let args = self.args.as_slice(); + + for pred in preds.predicates { + let new_pred = pred.0.fold_with(&mut self.folder); + self.preds.push((EarlyBinder::bind(new_pred).instantiate(self.tcx, args), pred.1)); + } + self } @@ -208,10 +469,16 @@ fn with_preds( if let Some(parent_def_id) = preds.parent { self = self.with_own_preds(f, parent_def_id); } + self.with_own_preds(f, def_id) } } - let collector = PredicatesCollector::new(tcx, args); + + let (parent_args, child_args) = get_delegation_user_specified_args(tcx, def_id); + let (folder, args) = create_folder_and_args(tcx, def_id, sig_id, parent_args, child_args); + let collector = PredicatesCollector { tcx, preds: vec![], args, folder }; + + let (parent, inh_kind) = get_parent_and_inheritance_kind(tcx, def_id, sig_id); // `explicit_predicates_of` is used here to avoid copying `Self: Trait` predicate. // Note: `predicates_of` query can also add inferred outlives predicates, but that @@ -232,157 +499,17 @@ fn with_preds( ty::GenericPredicates { parent, predicates: tcx.arena.alloc_from_iter(preds) } } -fn build_generic_args<'tcx>( - tcx: TyCtxt<'tcx>, - sig_id: DefId, - def_id: LocalDefId, - args: ty::GenericArgsRef<'tcx>, -) -> ty::GenericArgsRef<'tcx> { - let caller_generics = tcx.generics_of(def_id); - let callee_generics = tcx.generics_of(sig_id); - - let mut remap_table = FxHashMap::default(); - for caller_param in &caller_generics.own_params { - let callee_index = callee_generics.param_def_id_to_index(tcx, caller_param.def_id).unwrap(); - remap_table.insert(callee_index, caller_param.index); - } - - let mut folder = ParamIndexRemapper { tcx, remap_table }; - args.fold_with(&mut folder) -} - -fn create_generic_args<'tcx>( +fn create_folder_and_args<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, sig_id: DefId, -) -> ty::GenericArgsRef<'tcx> { - let caller_kind = fn_kind(tcx, def_id.into()); - let callee_kind = fn_kind(tcx, sig_id); - match (caller_kind, callee_kind) { - (FnKind::Free, FnKind::Free) - | (FnKind::Free, FnKind::AssocTrait) - | (FnKind::AssocInherentImpl, FnKind::Free) - | (FnKind::AssocTrait, FnKind::Free) - | (FnKind::AssocTrait, FnKind::AssocTrait) => { - let args = ty::GenericArgs::identity_for_item(tcx, sig_id); - build_generic_args(tcx, sig_id, def_id, args) - } + parent_args: &'tcx [ty::GenericArg<'tcx>], + child_args: &'tcx [ty::GenericArg<'tcx>], +) -> (ParamIndexRemapper<'tcx>, Vec>) { + let args = create_generic_args(tcx, sig_id, def_id, parent_args, child_args); + let remap_table = create_mapping(tcx, sig_id, def_id, &args); - (FnKind::AssocTraitImpl, FnKind::AssocTrait) => { - let callee_generics = tcx.generics_of(sig_id); - let parent = tcx.parent(def_id.into()); - let parent_args = tcx.impl_trait_header(parent).trait_ref.instantiate_identity().args; - - let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id); - let method_args = tcx.mk_args(&trait_args[callee_generics.parent_count..]); - let method_args = build_generic_args(tcx, sig_id, def_id, method_args); - - tcx.mk_args_from_iter(parent_args.iter().chain(method_args)) - } - - (FnKind::AssocInherentImpl, FnKind::AssocTrait) => { - let parent = tcx.parent(def_id.into()); - let self_ty = tcx.type_of(parent).instantiate_identity(); - let generic_self_ty = ty::GenericArg::from(self_ty); - - let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id); - let trait_args = build_generic_args(tcx, sig_id, def_id, trait_args); - - let args = std::iter::once(generic_self_ty).chain(trait_args.iter().skip(1)); - tcx.mk_args_from_iter(args) - } - - // For trait impl's `sig_id` is always equal to the corresponding trait method. - // For inherent methods delegation is not yet supported. - (FnKind::AssocTraitImpl, _) - | (_, FnKind::AssocTraitImpl) - | (_, FnKind::AssocInherentImpl) => unreachable!(), - } -} - -// FIXME(fn_delegation): Move generics inheritance to the AST->HIR lowering. -// For now, generic parameters are not propagated to the generated call, -// which leads to inference errors: -// -// fn foo(x: i32) {} -// -// reuse foo as bar; -// desugaring: -// fn bar() { -// foo::<_>() // ERROR: type annotations needed -// } -pub(crate) fn inherit_generics_for_delegation_item<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - sig_id: DefId, -) -> ty::Generics { - let caller_kind = fn_kind(tcx, def_id.into()); - let callee_kind = fn_kind(tcx, sig_id); - match (caller_kind, callee_kind) { - (FnKind::Free, FnKind::Free) | (FnKind::Free, FnKind::AssocTrait) => { - build_generics(tcx, sig_id, None, InheritanceKind::WithParent(true)) - } - - (FnKind::AssocTraitImpl, FnKind::AssocTrait) => { - build_generics(tcx, sig_id, Some(tcx.parent(def_id.into())), InheritanceKind::Own) - } - - (FnKind::AssocInherentImpl, FnKind::AssocTrait) - | (FnKind::AssocTrait, FnKind::AssocTrait) - | (FnKind::AssocInherentImpl, FnKind::Free) - | (FnKind::AssocTrait, FnKind::Free) => build_generics( - tcx, - sig_id, - Some(tcx.parent(def_id.into())), - InheritanceKind::WithParent(false), - ), - - // For trait impl's `sig_id` is always equal to the corresponding trait method. - // For inherent methods delegation is not yet supported. - (FnKind::AssocTraitImpl, _) - | (_, FnKind::AssocTraitImpl) - | (_, FnKind::AssocInherentImpl) => unreachable!(), - } -} - -pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - sig_id: DefId, -) -> ty::GenericPredicates<'tcx> { - let args = create_generic_args(tcx, def_id, sig_id); - let caller_kind = fn_kind(tcx, def_id.into()); - let callee_kind = fn_kind(tcx, sig_id); - match (caller_kind, callee_kind) { - (FnKind::Free, FnKind::Free) | (FnKind::Free, FnKind::AssocTrait) => { - build_predicates(tcx, sig_id, None, InheritanceKind::WithParent(true), args) - } - - (FnKind::AssocTraitImpl, FnKind::AssocTrait) => build_predicates( - tcx, - sig_id, - Some(tcx.parent(def_id.into())), - InheritanceKind::Own, - args, - ), - - (FnKind::AssocInherentImpl, FnKind::AssocTrait) - | (FnKind::AssocTrait, FnKind::AssocTrait) - | (FnKind::AssocInherentImpl, FnKind::Free) - | (FnKind::AssocTrait, FnKind::Free) => build_predicates( - tcx, - sig_id, - Some(tcx.parent(def_id.into())), - InheritanceKind::WithParent(false), - args, - ), - - // For trait impl's `sig_id` is always equal to the corresponding trait method. - // For inherent methods delegation is not yet supported. - (FnKind::AssocTraitImpl, _) - | (_, FnKind::AssocTraitImpl) - | (_, FnKind::AssocInherentImpl) => unreachable!(), - } + (ParamIndexRemapper { tcx, remap_table }, args) } fn check_constraints<'tcx>( @@ -412,18 +539,89 @@ pub(crate) fn inherit_sig_for_delegation_item<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, ) -> &'tcx [Ty<'tcx>] { - let sig_id = tcx.hir_opt_delegation_sig_id(def_id).unwrap(); + let sig_id = tcx.hir_opt_delegation_sig_id(def_id).expect("Delegation must have sig_id"); let caller_sig = tcx.fn_sig(sig_id); + if let Err(err) = check_constraints(tcx, def_id, sig_id) { let sig_len = caller_sig.instantiate_identity().skip_binder().inputs().len() + 1; let err_type = Ty::new_error(tcx, err); return tcx.arena.alloc_from_iter((0..sig_len).map(|_| err_type)); } - let args = create_generic_args(tcx, def_id, sig_id); - // Bound vars are also inherited from `sig_id`. - // They will be rebound later in `lower_fn_ty`. - let sig = caller_sig.instantiate(tcx, args).skip_binder(); + let (parent_args, child_args) = get_delegation_user_specified_args(tcx, def_id); + let (mut folder, args) = create_folder_and_args(tcx, def_id, sig_id, parent_args, child_args); + let caller_sig = EarlyBinder::bind(caller_sig.skip_binder().fold_with(&mut folder)); + + let sig = caller_sig.instantiate(tcx, args.as_slice()).skip_binder(); let sig_iter = sig.inputs().iter().cloned().chain(std::iter::once(sig.output())); tcx.arena.alloc_from_iter(sig_iter) } + +// Creates user-specified generic arguments from delegation path, +// they will be used during delegation signature and predicates inheritance. +// Example: reuse Trait::<'static, i32, 1>::foo:: +// we want to extract [Self, 'static, i32, 1] for parent and [A, B] for child. +fn get_delegation_user_specified_args<'tcx>( + tcx: TyCtxt<'tcx>, + delegation_id: LocalDefId, +) -> (&'tcx [ty::GenericArg<'tcx>], &'tcx [ty::GenericArg<'tcx>]) { + let info = tcx + .hir_node(tcx.local_def_id_to_hir_id(delegation_id)) + .fn_sig() + .expect("Lowering delegation") + .decl + .opt_delegation_generics() + .expect("Lowering delegation"); + + let get_segment = |hir_id: HirId| -> Option<(&'tcx PathSegment<'tcx>, DefId)> { + let segment = tcx.hir_node(hir_id).expect_path_segment(); + segment.res.opt_def_id().map(|def_id| (segment, def_id)) + }; + + let ctx = ItemCtxt::new(tcx, delegation_id); + let lowerer = ctx.lowerer(); + + let parent_args = info.parent_args_segment_id.and_then(get_segment).map(|(segment, def_id)| { + let self_ty = get_delegation_self_ty(tcx, delegation_id); + + lowerer + .lower_generic_args_of_path( + segment.ident.span, + def_id, + &[], + segment, + self_ty, + GenericArgPosition::Type, + ) + .0 + .as_slice() + }); + + let child_args = info.child_args_segment_id.and_then(get_segment).map(|(segment, def_id)| { + let parent_args = if let Some(parent_args) = parent_args { + parent_args + } else { + let parent = tcx.parent(def_id); + if matches!(tcx.def_kind(parent), DefKind::Trait) { + ty::GenericArgs::identity_for_item(tcx, parent).as_slice() + } else { + &[] + } + }; + + let args = lowerer + .lower_generic_args_of_path( + segment.ident.span, + def_id, + parent_args, + segment, + None, + GenericArgPosition::Value, + ) + .0; + + &args[parent_args.len()..] + }); + + (parent_args.unwrap_or_default(), child_args.unwrap_or_default()) +} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 4b0f1016029e..1c999f1ffc93 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -7,7 +7,7 @@ MultiSpan, listify, msg, }; use rustc_hir::limit::Limit; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; use rustc_span::{Ident, Span, Symbol}; pub(crate) mod wrong_number_of_generic_args; @@ -88,6 +88,8 @@ pub(crate) enum AssocItemNotFoundLabel<'a> { NotFound { #[primary_span] span: Span, + assoc_ident: Ident, + assoc_kind: &'static str, }, #[label( "there is {$identically_named -> @@ -149,6 +151,7 @@ pub(crate) enum AssocItemNotFoundSugg<'a> { trait_ref: String, suggested_name: Symbol, identically_named: bool, + assoc_kind: &'static str, #[applicability] applicability: Applicability, }, @@ -732,14 +735,6 @@ pub(crate) enum CannotCaptureLateBound { }, } -#[derive(Diagnostic)] -#[diag("{$variances}")] -pub(crate) struct VariancesOf { - #[primary_span] - pub span: Span, - pub variances: String, -} - #[derive(Diagnostic)] #[diag("{$ty}")] pub(crate) struct TypeOf<'tcx> { @@ -888,10 +883,20 @@ pub(crate) enum ImplNotMarkedDefault { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("this item cannot be used as its where bounds are not satisfied for the `Self` type")] pub(crate) struct UselessImplItem; +#[derive(Diagnostic)] +#[diag("cannot override `{$ident}` because it already has a `final` definition in the trait")] +pub(crate) struct OverridingFinalTraitFunction { + #[primary_span] + pub impl_span: Span, + #[note("`{$ident}` is marked final here")] + pub trait_span: Span, + pub ident: Ident, +} + #[derive(Diagnostic)] #[diag("not all trait items implemented, missing: `{$missing_items_msg}`", code = E0046)] pub(crate) struct MissingTraitItem { @@ -1104,7 +1109,7 @@ pub(crate) enum LateBoundInApit { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unnecessary associated type bound for dyn-incompatible associated type")] #[note( "this associated type has a `where Self: Sized` bound, and while the associated type can be specified, it cannot be used because trait objects are never `Sized`" @@ -1114,7 +1119,7 @@ pub(crate) struct UnusedAssociatedTypeBounds { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("impl trait in impl method signature does not match trait method signature")] #[note( "add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate" @@ -1122,7 +1127,7 @@ pub(crate) struct UnusedAssociatedTypeBounds { #[note( "we are soliciting feedback, see issue #121718 for more information" )] -pub(crate) struct ReturnPositionImplTraitInTraitRefined<'tcx> { +pub(crate) struct ReturnPositionImplTraitInTraitRefined { #[suggestion( "replace the return type so that it matches the trait", applicability = "maybe-incorrect", @@ -1136,10 +1141,10 @@ pub(crate) struct ReturnPositionImplTraitInTraitRefined<'tcx> { pub pre: &'static str, pub post: &'static str, - pub return_ty: Ty<'tcx>, + pub return_ty: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("impl trait in impl method captures fewer lifetimes than in trait")] #[note( "add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate" @@ -1436,6 +1441,15 @@ pub struct NoVariantNamed<'tcx> { pub ty: Ty<'tcx>, } +#[derive(Diagnostic)] +#[diag("no field `{$field}` on type `{$ty}`", code = E0609)] +pub struct NoFieldOnType<'tcx> { + #[primary_span] + pub span: Span, + pub ty: Ty<'tcx>, + pub field: Ident, +} + // FIXME(fmease): Deduplicate: #[derive(Diagnostic)] @@ -1457,7 +1471,7 @@ pub(crate) struct TyParamFirstLocal<'tcx> { pub local_type: Ty<'tcx>, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`)", code = E0210)] #[note( "implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type" @@ -1489,7 +1503,7 @@ pub(crate) struct TyParamSome { pub param: Ident, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("type parameter `{$param}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param}>`)", code = E0210)] #[note( "implementing a foreign trait is only possible if at least one of the types for which it is implemented is local" @@ -1795,7 +1809,7 @@ pub(crate) struct BadReturnTypeNotation { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait")] pub(crate) struct SupertraitItemShadowing { pub item: Symbol, diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 2b7854769b42..615c0a766a63 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -1026,7 +1026,7 @@ fn suggest_removing_args_or_generics(&self, err: &mut Diag<'_, impl EmissionGuar .collect::>(); if !suggestions.is_empty() { - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!( "replace the generic bound{s} with the associated type{s}", s = pluralize!(unbound_types.len()) 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 670312ff1ba1..ca399964fdd9 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -4,9 +4,8 @@ use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; +use rustc_hir::def_id::DefId; use rustc_hir::{PolyTraitRef, find_attr}; use rustc_middle::bug; use rustc_middle::ty::{ @@ -171,7 +170,7 @@ pub(crate) fn add_implicit_sizedness_bounds( let tcx = self.tcx(); // Skip adding any default bounds if `#![rustc_no_implicit_bounds]` - if find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcNoImplicitBounds) { + if find_attr!(tcx, crate, RustcNoImplicitBounds) { return; } @@ -285,8 +284,7 @@ fn should_add_default_traits<'a>( context: ImpliedBoundsContext<'tcx>, ) -> bool { let collected = collect_bounds(hir_bounds, context, trait_def_id); - !find_attr!(self.tcx().get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcNoImplicitBounds) - && !collected.any() + !find_attr!(self.tcx(), crate, RustcNoImplicitBounds) && !collected.any() } fn reject_duplicate_relaxed_bounds(&self, relaxed_bounds: SmallVec<[&PolyTraitRef<'_>; 1]>) { 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 00765498b061..8397ff61a3b3 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 @@ -2,11 +2,12 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, EmissionGuarantee, StashKey, Suggestions, struct_span_code_err, + Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, StashKey, + Suggestions, struct_span_code_err, }; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, HirId, LangItem}; use rustc_lint_defs::builtin::{BARE_TRAIT_OBJECTS, UNUSED_ASSOCIATED_TYPE_BOUNDS}; use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan; use rustc_middle::ty::{ @@ -523,6 +524,30 @@ fn prohibit_or_lint_bare_trait_object_ty( hir_id: hir::HirId, hir_bounds: &[hir::PolyTraitRef<'tcx>], ) -> Option { + struct TraitObjectWithoutDyn<'a, 'tcx> { + span: Span, + hir_id: HirId, + sugg: Vec<(Span, String)>, + 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, ()> { + let Self { span, hir_id, sugg, this } = self; + let mut lint = + Diag::new(dcx, level, "trait objects without an explicit `dyn` are deprecated"); + if span.can_be_used_for_suggestions() { + lint.multipart_suggestion( + "if this is a dyn-compatible trait, use `dyn`", + sugg, + Applicability::MachineApplicable, + ); + } + this.maybe_suggest_blanket_trait_impl(span, hir_id, &mut lint); + lint + } + } + let tcx = self.tcx(); let [poly_trait_ref, ..] = hir_bounds else { return None }; @@ -606,17 +631,12 @@ fn prohibit_or_lint_bare_trait_object_ty( } Some(diag.emit()) } else { - tcx.node_span_lint(BARE_TRAIT_OBJECTS, hir_id, span, |lint| { - lint.primary_message("trait objects without an explicit `dyn` are deprecated"); - if span.can_be_used_for_suggestions() { - lint.multipart_suggestion_verbose( - "if this is a dyn-compatible trait, use `dyn`", - sugg, - Applicability::MachineApplicable, - ); - } - self.maybe_suggest_blanket_trait_impl(span, hir_id, lint); - }); + tcx.emit_node_span_lint( + BARE_TRAIT_OBJECTS, + hir_id, + span, + TraitObjectWithoutDyn { span, hir_id, sugg, this: self }, + ); None } } @@ -674,7 +694,7 @@ fn maybe_suggest_add_generic_impl_trait( } else { sugg.push((generics.where_clause_span, format!("<{param}: {}>", rendered_ty))); } - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( "you might be missing a type parameter", sugg, Applicability::MachineApplicable, @@ -785,7 +805,7 @@ fn maybe_suggest_dyn_trait( } // FIXME: Only emit this suggestion if the trait is dyn-compatible. - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( "you can add the `dyn` keyword if you want a trait object", sugg, Applicability::MachineApplicable, @@ -871,7 +891,7 @@ fn maybe_suggest_impl_trait( single underlying type", ); - diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); + diag.multipart_suggestion(msg, impl_sugg, Applicability::MachineApplicable); // Suggest `Box` for return type if is_dyn_compatible { @@ -887,7 +907,7 @@ fn maybe_suggest_impl_trait( ] }; - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( "alternatively, you can return an owned trait object", suggestion, Applicability::MachineApplicable, @@ -902,12 +922,12 @@ fn maybe_suggest_impl_trait( continue; } let sugg = self.add_generic_param_suggestion(generics, span, &trait_name); - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( format!("use a new generic type parameter, constrained by `{trait_name}`"), sugg, Applicability::MachineApplicable, ); - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( "you can also use an opaque type, but users won't be able to specify the type \ parameter when calling the `fn`, having to rely exclusively on type inference", impl_sugg, @@ -931,7 +951,7 @@ fn maybe_suggest_impl_trait( } else { vec![(span.shrink_to_lo(), dyn_str.to_string())] }; - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( format!( "alternatively, use a trait object to accept any type that implements \ `{trait_name}`, accessing its methods at runtime using dynamic dispatch", 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 975f8ab4e42f..6bcc2e40e524 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -141,7 +141,7 @@ pub(super) fn report_unresolved_assoc_item( ); } - let assoc_kind_str = assoc_tag_str(assoc_tag); + let assoc_kind = assoc_tag_str(assoc_tag); let qself_str = qself.to_string(tcx); // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a @@ -151,7 +151,7 @@ pub(super) fn report_unresolved_assoc_item( let mut err = errors::AssocItemNotFound { span: if is_dummy { span } else { assoc_ident.span }, assoc_ident, - assoc_kind: assoc_kind_str, + assoc_kind, qself: &qself_str, label: None, sugg: None, @@ -161,7 +161,8 @@ pub(super) fn report_unresolved_assoc_item( }; if is_dummy { - err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span }); + err.label = + Some(errors::AssocItemNotFoundLabel::NotFound { span, assoc_ident, assoc_kind }); return self.dcx().emit_err(err); } @@ -181,7 +182,7 @@ pub(super) fn report_unresolved_assoc_item( { err.sugg = Some(errors::AssocItemNotFoundSugg::Similar { span: assoc_ident.span, - assoc_kind: assoc_kind_str, + assoc_kind, suggested_name, }); return self.dcx().emit_err(err); @@ -214,7 +215,7 @@ pub(super) fn report_unresolved_assoc_item( if let [best_trait] = visible_traits .iter() .copied() - .filter(|trait_def_id| { + .filter(|&trait_def_id| { tcx.associated_items(trait_def_id) .filter_by_name_unhygienic(suggested_name) .any(|item| item.tag() == assoc_tag) @@ -224,7 +225,7 @@ pub(super) fn report_unresolved_assoc_item( let trait_name = tcx.def_path_str(best_trait); err.label = Some(errors::AssocItemNotFoundLabel::FoundInOtherTrait { span: assoc_ident.span, - assoc_kind: assoc_kind_str, + assoc_kind, trait_name: &trait_name, suggested_name, identically_named: suggested_name == assoc_ident.name, @@ -256,7 +257,7 @@ pub(super) fn report_unresolved_assoc_item( err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait { span: assoc_ident.span, trait_name: &trait_name, - assoc_kind: assoc_kind_str, + assoc_kind, suggested_name, }); return self.dcx().emit_err(err); @@ -286,6 +287,7 @@ pub(super) fn report_unresolved_assoc_item( trait_ref, identically_named, suggested_name, + assoc_kind, applicability, }); } else { @@ -322,11 +324,15 @@ pub(super) fn report_unresolved_assoc_item( err.sugg = Some(errors::AssocItemNotFoundSugg::Other { span: assoc_ident.span, qself: &qself_str, - assoc_kind: assoc_kind_str, + assoc_kind, suggested_name: *candidate_name, }); } else { - err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_ident.span }); + err.label = Some(errors::AssocItemNotFoundLabel::NotFound { + span: assoc_ident.span, + assoc_ident, + assoc_kind, + }); } self.dcx().emit_err(err) @@ -528,7 +534,7 @@ pub(super) fn report_unresolved_type_relative_path( } } } - err.multipart_suggestion_verbose( + err.multipart_suggestion( "there is a variant with a similar name", suggestion, Applicability::HasPlaceholders, @@ -1234,7 +1240,7 @@ pub(crate) fn maybe_report_similar_assoc_fn( && let name = Symbol::intern(&format!("{ident2}_{ident3}")) && let Some(item) = inherent_impls .iter() - .flat_map(|inherent_impl| { + .flat_map(|&inherent_impl| { tcx.associated_items(inherent_impl).filter_by_name_unhygienic(name) }) .next() @@ -1546,7 +1552,7 @@ pub fn prohibit_assoc_item_constraint( (constraint.span.with_lo(constraint.ident.span.hi()), String::new()), ]; - err.multipart_suggestion_verbose( + err.multipart_suggestion( "declare the type parameter right after the `impl` keyword", suggestions, Applicability::MaybeIncorrect, @@ -1721,7 +1727,7 @@ fn generics_args_err_extend<'a>( }, (args_span, String::new()), ]; - err.multipart_suggestion_verbose(msg, suggestion, Applicability::MaybeIncorrect); + err.multipart_suggestion(msg, suggestion, Applicability::MaybeIncorrect); } GenericsArgsErrExtend::DefVariant(segments) => { let args: Vec = segments 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 ff0a5a8df0fa..0ca57cb50cf2 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -1,6 +1,9 @@ use rustc_ast::ast::ParamKindOrd; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, struct_span_code_err}; +use rustc_errors::{ + Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, Level, MultiSpan, + struct_span_code_err, +}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, GenericArg}; @@ -625,6 +628,17 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes( args: &hir::GenericArgs<'_>, position: GenericArgPosition, ) -> ExplicitLateBound { + struct LifetimeArgsIssue { + msg: &'static str, + } + + 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) + } + } + let param_counts = def.own_counts(); if let Some(span_late) = def.has_late_bound_regions @@ -644,13 +658,11 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes( } else { let mut multispan = MultiSpan::from_span(span); multispan.push_span_label(span_late, note); - cx.tcx().node_span_lint( + cx.tcx().emit_node_span_lint( LATE_BOUND_LIFETIME_ARGUMENTS, args.args[0].hir_id(), multispan, - |lint| { - lint.primary_message(msg); - }, + LifetimeArgsIssue { 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 3f8237530892..bf97bfb1ebbc 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -19,14 +19,15 @@ pub mod errors; pub mod generics; -use std::slice; +use std::{assert_matches, slice}; +use rustc_abi::FIRST_VARIANT; use rustc_ast::LitKind; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err, + Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, FatalError, Level, + struct_span_code_err, }; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -50,11 +51,11 @@ use tracing::{debug, instrument}; use crate::check::check_abi; -use crate::check_c_variadic_abi; -use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation}; +use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoFieldOnType}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; +use crate::{NoVariantNamed, check_c_variadic_abi}; /// The context in which an implied bound is being added to a item being lowered (i.e. a sizedness /// trait or a default trait) @@ -265,10 +266,11 @@ fn assoc_tag(self) -> ty::AssocTag { } } - fn def_kind(self) -> DefKind { + ///NOTE: use `assoc_tag` for any important logic + fn def_kind_for_diagnostics(self) -> DefKind { match self { Self::Type(_) => DefKind::AssocTy, - Self::Const => DefKind::AssocConst, + Self::Const => DefKind::AssocConst { is_type_const: false }, } } @@ -319,7 +321,7 @@ pub enum IsMethodCall { /// Denotes the "position" of a generic argument, indicating if it is a generic type, /// generic function or generic method call. -#[derive(Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq)] pub(crate) enum GenericArgPosition { Type, Value, // e.g., functions @@ -385,6 +387,8 @@ impl<'tcx> ForbidMCGParamUsesFolder<'tcx> { fn error(&self) -> ErrorGuaranteed { let msg = if self.is_self_alias { "generic `Self` types are currently not permitted in anonymous constants" + } else if self.tcx.features().opaque_generic_const_args() { + "generic parameters in const blocks are only allowed as the direct value of a `type const`" } else { "generic parameters may not be used in const operations" }; @@ -403,11 +407,13 @@ fn error(&self) -> ErrorGuaranteed { diag.span_note(impl_.self_ty.span, "not a concrete type"); } } - if self.tcx.features().min_generic_const_args() - && !self.tcx.features().opaque_generic_const_args() - { - diag.help("add `#![feature(opaque_generic_const_args)]` to allow generic expressions as the RHS of const items"); - } + if self.tcx.features().min_generic_const_args() { + if !self.tcx.features().opaque_generic_const_args() { + diag.help("add `#![feature(opaque_generic_const_args)]` to allow generic expressions as the RHS of const items"); + } else { + diag.help("consider factoring the expression into a `type const` item and use it as the const argument instead"); + } + }; diag.emit() } } @@ -550,7 +556,14 @@ pub fn lower_generic_args_of_path_segment( def_id: DefId, item_segment: &hir::PathSegment<'tcx>, ) -> GenericArgsRef<'tcx> { - let (args, _) = self.lower_generic_args_of_path(span, def_id, &[], item_segment, None); + let (args, _) = self.lower_generic_args_of_path( + span, + def_id, + &[], + item_segment, + None, + GenericArgPosition::Type, + ); if let Some(c) = item_segment.args().constraints.first() { prohibit_assoc_item_constraint(self, c, Some((def_id, item_segment, span))); } @@ -592,13 +605,14 @@ pub fn lower_generic_args_of_path_segment( /// type itself: `['a]`. The returned `GenericArgsRef` concatenates these two /// lists: `[Vec, u8, 'a]`. #[instrument(level = "debug", skip(self, span), ret)] - fn lower_generic_args_of_path( + pub(crate) fn lower_generic_args_of_path( &self, span: Span, def_id: DefId, parent_args: &[ty::GenericArg<'tcx>], segment: &hir::PathSegment<'tcx>, self_ty: Option>, + pos: GenericArgPosition, ) -> (GenericArgsRef<'tcx>, GenericArgCountResult) { // If the type is parameterized by this region, then replace this // region with the current anon region binding (in other words, @@ -621,14 +635,8 @@ fn lower_generic_args_of_path( assert!(self_ty.is_none()); } - let arg_count = check_generic_arg_count( - self, - def_id, - segment, - generics, - GenericArgPosition::Type, - self_ty.is_some(), - ); + let arg_count = + check_generic_arg_count(self, def_id, segment, generics, pos, self_ty.is_some()); // Skip processing if type has no generic parameters. // Traits always have `Self` as a generic parameter, which means they will not return early @@ -791,6 +799,7 @@ fn inferred_kind( infer_args: segment.infer_args, incorrect_args: &arg_count.correct, }; + let args = lower_generic_args( self, def_id, @@ -812,8 +821,14 @@ pub fn lower_generic_args_of_assoc_item( item_segment: &hir::PathSegment<'tcx>, parent_args: GenericArgsRef<'tcx>, ) -> GenericArgsRef<'tcx> { - let (args, _) = - self.lower_generic_args_of_path(span, item_def_id, parent_args, item_segment, None); + let (args, _) = self.lower_generic_args_of_path( + span, + item_def_id, + parent_args, + item_segment, + None, + GenericArgPosition::Type, + ); if let Some(c) = item_segment.args().constraints.first() { prohibit_assoc_item_constraint(self, c, Some((item_def_id, item_segment, span))); } @@ -925,6 +940,7 @@ pub(crate) fn lower_poly_trait_ref( &[], segment, Some(self_ty), + GenericArgPosition::Type, ); let constraints = segment.args().constraints; @@ -1100,8 +1116,14 @@ fn lower_mono_trait_ref( ) -> ty::TraitRef<'tcx> { self.report_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); - let (generic_args, _) = - self.lower_generic_args_of_path(span, trait_def_id, &[], trait_segment, Some(self_ty)); + let (generic_args, _) = self.lower_generic_args_of_path( + span, + trait_def_id, + &[], + trait_segment, + Some(self_ty), + GenericArgPosition::Type, + ); if let Some(c) = trait_segment.args().constraints.first() { prohibit_assoc_item_constraint(self, c, Some((trait_def_id, trait_segment, span))); } @@ -1459,6 +1481,54 @@ fn lower_type_relative_path( span: Span, mode: LowerTypeRelativePathMode, ) -> Result, ErrorGuaranteed> { + struct AmbiguousAssocItem<'tcx> { + variant_def_id: DefId, + item_def_id: DefId, + span: Span, + segment_ident: Ident, + bound_def_id: DefId, + self_ty: Ty<'tcx>, + tcx: TyCtxt<'tcx>, + mode: LowerTypeRelativePathMode, + } + + impl<'a, 'tcx> Diagnostic<'a, ()> for AmbiguousAssocItem<'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { + variant_def_id, + item_def_id, + span, + segment_ident, + bound_def_id, + self_ty, + tcx, + mode, + } = self; + let mut lint = Diag::new(dcx, level, "ambiguous associated item"); + + let mut could_refer_to = |kind: DefKind, def_id, also| { + let note_msg = format!( + "`{}` could{} refer to the {} defined here", + segment_ident, + also, + tcx.def_kind_descr(kind, def_id) + ); + lint.span_note(tcx.def_span(def_id), note_msg); + }; + + could_refer_to(DefKind::Variant, variant_def_id, ""); + could_refer_to(mode.def_kind_for_diagnostics(), item_def_id, " also"); + + lint.span_suggestion( + span, + "use fully-qualified syntax", + format!("<{} as {}>::{}", self_ty, tcx.item_name(bound_def_id), segment_ident), + Applicability::MachineApplicable, + ); + lint + } + } + debug!(%self_ty, ?segment.ident); let tcx = self.tcx(); @@ -1534,33 +1604,21 @@ fn lower_type_relative_path( let (item_def_id, args) = self.lower_assoc_item_path(span, item_def_id, segment, bound)?; if let Some(variant_def_id) = variant_def_id { - tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, qpath_hir_id, span, |lint| { - lint.primary_message("ambiguous associated item"); - let mut could_refer_to = |kind: DefKind, def_id, also| { - let note_msg = format!( - "`{}` could{} refer to the {} defined here", - segment.ident, - also, - tcx.def_kind_descr(kind, def_id) - ); - lint.span_note(tcx.def_span(def_id), note_msg); - }; - - could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(mode.def_kind(), item_def_id, " also"); - - lint.span_suggestion( + tcx.emit_node_span_lint( + AMBIGUOUS_ASSOCIATED_ITEMS, + qpath_hir_id, + span, + AmbiguousAssocItem { + variant_def_id, + item_def_id, span, - "use fully-qualified syntax", - format!( - "<{} as {}>::{}", - self_ty, - tcx.item_name(bound.def_id()), - segment.ident - ), - Applicability::MachineApplicable, - ); - }); + segment_ident: segment.ident, + bound_def_id: bound.def_id(), + self_ty, + tcx, + mode, + }, + ); } Ok(TypeRelativePath::AssocItem(item_def_id, args)) @@ -2080,12 +2138,12 @@ pub fn probe_generic_path_segments( } // Case 3. Reference to a top-level value. - DefKind::Fn | DefKind::Const | DefKind::ConstParam | DefKind::Static { .. } => { + DefKind::Fn | DefKind::Const { .. } | DefKind::ConstParam | DefKind::Static { .. } => { generic_segments.push(GenericPathSegment(def_id, last)); } // Case 4. Reference to a method or associated const. - DefKind::AssocFn | DefKind::AssocConst => { + DefKind::AssocFn | DefKind::AssocConst { .. } => { if segments.len() >= 2 { let generics = tcx.generics_of(def_id); generic_segments.push(GenericPathSegment(generics.parent.unwrap(), last - 1)); @@ -2394,11 +2452,15 @@ fn lower_const_arg_array( ) -> Const<'tcx> { let tcx = self.tcx(); - let ty::Array(elem_ty, _) = ty.kind() else { - let e = tcx - .dcx() - .span_err(array_expr.span, format!("expected `{}`, found const array", ty)); - return Const::new_error(tcx, e); + let elem_ty = match ty.kind() { + ty::Array(elem_ty, _) => elem_ty, + ty::Error(e) => return Const::new_error(tcx, *e), + _ => { + let e = tcx + .dcx() + .span_err(array_expr.span, format!("expected `{}`, found const array", ty)); + return Const::new_error(tcx, e); + } }; let elems = array_expr @@ -2517,9 +2579,13 @@ fn lower_const_arg_tup( ) -> Const<'tcx> { let tcx = self.tcx(); - let ty::Tuple(tys) = ty.kind() else { - let e = tcx.dcx().span_err(span, format!("expected `{}`, found const tuple", ty)); - return Const::new_error(tcx, e); + let tys = match ty.kind() { + ty::Tuple(tys) => tys, + ty::Error(e) => return Const::new_error(tcx, *e), + _ => { + let e = tcx.dcx().span_err(span, format!("expected `{}`, found const tuple", ty)); + return Const::new_error(tcx, e); + } }; let exprs = exprs @@ -2658,7 +2724,7 @@ fn lower_resolved_const_path( ); self.lower_const_param(def_id, hir_id) } - Res::Def(DefKind::Const, did) => { + Res::Def(DefKind::Const { .. }, did) => { if let Err(guar) = self.require_type_const_attribute(did, span) { return Const::new_error(self.tcx(), guar); } @@ -2699,7 +2765,7 @@ fn lower_resolved_const_path( let args = self.lower_generic_args_of_path_segment(span, generics_did, segment); ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args)) } - Res::Def(DefKind::AssocConst, did) => { + Res::Def(DefKind::AssocConst { .. }, did) => { let trait_segment = if let [modules @ .., trait_, _item] = path.segments { let _ = self.prohibit_generic_args(modules.iter(), GenericsArgsErrExtend::None); Some(trait_) @@ -2886,11 +2952,12 @@ fn require_type_const_attribute( } } - fn lower_delegation_ty(&self, idx: hir::InferDelegationKind) -> Ty<'tcx> { + fn lower_delegation_ty(&self, idx: hir::InferDelegationKind<'tcx>) -> Ty<'tcx> { let delegation_sig = self.tcx().inherit_sig_for_delegation_item(self.item_def_id()); + match idx { hir::InferDelegationKind::Input(idx) => delegation_sig[idx], - hir::InferDelegationKind::Output => *delegation_sig.last().unwrap(), + hir::InferDelegationKind::Output { .. } => *delegation_sig.last().unwrap(), } } @@ -3046,6 +3113,14 @@ pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { self.record_ty(pat.hir_id, ty, pat.span); pat_ty } + hir::TyKind::FieldOf(ty, hir::TyFieldPath { variant, field }) => self.lower_field_of( + self.lower_ty(ty), + self.item_def_id(), + ty.span, + hir_ty.hir_id, + *variant, + *field, + ), hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar), }; @@ -3087,6 +3162,159 @@ fn lower_pat_ty_pat( } } + fn lower_field_of( + &self, + ty: Ty<'tcx>, + item_def_id: LocalDefId, + ty_span: Span, + hir_id: HirId, + variant: Option, + field: Ident, + ) -> Ty<'tcx> { + let dcx = self.dcx(); + let tcx = self.tcx(); + match ty.kind() { + ty::Adt(def, _) => { + let base_did = def.did(); + let kind_name = tcx.def_descr(base_did); + let (variant_idx, variant) = if def.is_enum() { + let Some(variant) = variant else { + let err = dcx + .create_err(NoVariantNamed { span: field.span, ident: field, ty }) + .with_span_help( + field.span.shrink_to_lo(), + "you might be missing a variant here: `Variant.`", + ) + .emit(); + return Ty::new_error(tcx, err); + }; + + if let Some(res) = def + .variants() + .iter_enumerated() + .find(|(_, f)| f.ident(tcx).normalize_to_macros_2_0() == variant) + { + res + } else { + let err = dcx + .create_err(NoVariantNamed { span: variant.span, ident: variant, ty }) + .emit(); + return Ty::new_error(tcx, err); + } + } else { + if let Some(variant) = variant { + let adt_path = tcx.def_path_str(base_did); + struct_span_code_err!( + dcx, + variant.span, + E0609, + "{kind_name} `{adt_path}` does not have any variants", + ) + .with_span_label(variant.span, "variant unknown") + .emit(); + } + (FIRST_VARIANT, def.non_enum_variant()) + }; + let block = tcx.local_def_id_to_hir_id(item_def_id); + let (ident, def_scope) = tcx.adjust_ident_and_get_scope(field, def.did(), block); + if let Some((field_idx, field)) = variant + .fields + .iter_enumerated() + .find(|(_, f)| f.ident(tcx).normalize_to_macros_2_0() == ident) + { + if field.vis.is_accessible_from(def_scope, tcx) { + tcx.check_stability(field.did, Some(hir_id), ident.span, None); + } else { + let adt_path = tcx.def_path_str(base_did); + struct_span_code_err!( + dcx, + ident.span, + E0616, + "field `{ident}` of {kind_name} `{adt_path}` is private", + ) + .with_span_label(ident.span, "private field") + .emit(); + } + 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(); + Ty::new_error(tcx, err) + } + } + ty::Tuple(tys) => { + let index = match field.as_str().parse::() { + Ok(idx) => idx, + Err(_) => { + let err = + dcx.create_err(NoFieldOnType { span: field.span, field, ty }).emit(); + return Ty::new_error(tcx, err); + } + }; + if field.name != sym::integer(index) { + bug!("we parsed above, but now not equal?"); + } + 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(); + Ty::new_error(tcx, err) + } + } + // FIXME(FRTs): support type aliases + /* + ty::Alias(AliasTyKind::Free, ty) => { + return self.lower_field_of( + ty, + item_def_id, + ty_span, + hir_id, + variant, + field, + ); + }*/ + ty::Alias(..) => Ty::new_error( + tcx, + dcx.span_err(ty_span, format!("could not resolve fields of `{ty}`")), + ), + ty::Error(err) => Ty::new_error(tcx, *err), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_, _) + | ty::UnsafeBinder(_) + | ty::Dynamic(_, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(_, _) + | ty::Never + | ty::Param(_) + | ty::Bound(_, _) + | ty::Placeholder(_) + | ty::Slice(..) => Ty::new_error( + tcx, + dcx.span_err(ty_span, format!("type `{ty}` doesn't have fields")), + ), + ty::Infer(_) => Ty::new_error( + tcx, + dcx.span_err(ty_span, format!("cannot use `{ty}` in this position")), + ), + // FIXME(FRTs): support these types? + ty::Array(..) | ty::Pat(..) => Ty::new_error( + tcx, + dcx.span_err(ty_span, format!("type `{ty}` is not yet supported in `field_of!`")), + ), + } + } + /// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR. #[instrument(level = "debug", skip(self), ret)] fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: Option) -> Ty<'tcx> { diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 08da8d19344d..87b52c57dc6a 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -104,7 +104,7 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { if self.depth >= self.cause_depth { self.cause = Some(error.obligation.cause); if let hir::TyKind::TraitObject(..) = ty.kind - && let DefKind::AssocTy | DefKind::AssocConst | DefKind::AssocFn = + && let DefKind::AssocTy | DefKind::AssocConst { .. } | DefKind::AssocFn = self.tcx.def_kind(self.def_id) { self.cause = Some(ObligationCause::new( diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index f5c77c680000..57ee79017038 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -8,8 +8,9 @@ //! specialization errors. These things can (and probably should) be //! fixed, but for the moment it's easier to do these checks early. +use std::debug_assert_matches; + use min_specialization::check_min_specialization; -use rustc_data_structures::debug_assert_matches; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_errors::codes::*; @@ -63,7 +64,7 @@ pub(crate) fn check_impl_wf( // Check that the args are constrained. We queryfied the check for ty/const params // since unconstrained type/const params cause ICEs in projection, so we want to // detect those specifically and project those to `TyKind::Error`. - let mut res = tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id); + let mut res = tcx.ensure_result().enforce_impl_non_lifetime_params_are_constrained(impl_def_id); res = res.and(enforce_impl_lifetime_params_are_constrained(tcx, impl_def_id, of_trait)); if of_trait && tcx.features().min_specialization() { @@ -101,7 +102,7 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained( let lifetimes_in_associated_types: FxHashSet<_> = tcx .associated_item_def_ids(impl_def_id) .iter() - .flat_map(|def_id| { + .flat_map(|&def_id| { let item = tcx.associated_item(def_id); match item.kind { ty::AssocKind::Type { .. } => { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 70209993a510..0e8bc9ad5822 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -56,10 +56,8 @@ */ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(default_field_values)] #![feature(gen_blocks)] -#![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(never_type)] #![feature(slice_partition_dedup)] @@ -88,6 +86,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::lints::DelayedLint; +use rustc_lint::DecorateAttrLint; use rustc_middle::mir::interpret::GlobalId; use rustc_middle::query::Providers; use rustc_middle::ty::{Const, Ty, TyCtxt}; @@ -148,20 +147,17 @@ pub fn provide(providers: &mut Providers) { }; } -fn emit_delayed_lint(lint: &DelayedLint, tcx: TyCtxt<'_>) { +pub fn emit_delayed_lint(lint: &DelayedLint, tcx: TyCtxt<'_>) { match lint { DelayedLint::AttributeParsing(attribute_lint) => { - tcx.node_span_lint( + tcx.emit_node_span_lint( attribute_lint.lint_id.lint, attribute_lint.id, attribute_lint.span, - |diag| { - rustc_lint::decorate_attribute_lint( - tcx.sess, - Some(tcx), - &attribute_lint.kind, - diag, - ); + DecorateAttrLint { + sess: tcx.sess, + tcx: Some(tcx), + diagnostic: &attribute_lint.kind, }, ); } @@ -176,14 +172,14 @@ pub fn check_crate(tcx: TyCtxt<'_>) { // what we are intending to discard, to help future type-based refactoring. type R = Result<(), ErrorGuaranteed>; - let _: R = tcx.ensure_ok().check_type_wf(()); + let _: R = tcx.ensure_result().check_type_wf(()); for &trait_def_id in tcx.all_local_trait_impls(()).keys() { - let _: R = tcx.ensure_ok().coherent_trait(trait_def_id); + let _: R = tcx.ensure_result().coherent_trait(trait_def_id); } // these queries are executed for side-effects (error reporting): - let _: R = tcx.ensure_ok().crate_inherent_impls_validity_check(()); - let _: R = tcx.ensure_ok().crate_inherent_impls_overlap_check(()); + let _: R = tcx.ensure_result().crate_inherent_impls_validity_check(()); + let _: R = tcx.ensure_result().crate_inherent_impls_overlap_check(()); }); tcx.sess.time("emit_ast_lowering_delayed_lints", || { @@ -231,7 +227,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { tcx.ensure_ok().eval_static_initializer(item_def_id); check::maybe_check_static_with_link_section(tcx, item_def_id); } - DefKind::Const + DefKind::Const { .. } if !tcx.generics_of(item_def_id).own_requires_monomorphization() && !tcx.is_type_const(item_def_id) => { diff --git a/compiler/rustc_hir_analysis/src/outlives/dump.rs b/compiler/rustc_hir_analysis/src/outlives/dump.rs index cf770a2561db..fe8b2a30ab9e 100644 --- a/compiler/rustc_hir_analysis/src/outlives/dump.rs +++ b/compiler/rustc_hir_analysis/src/outlives/dump.rs @@ -1,4 +1,3 @@ -use rustc_hir::attrs::AttributeKind; use rustc_hir::find_attr; use rustc_middle::bug; use rustc_middle::ty::{self, TyCtxt}; @@ -6,7 +5,7 @@ pub(crate) fn inferred_outlives(tcx: TyCtxt<'_>) { for id in tcx.hir_free_items() { - if !find_attr!(tcx.get_all_attrs(id.owner_id), AttributeKind::RustcOutlives) { + if !find_attr!(tcx, id.owner_id, RustcDumpInferredOutlives) { continue; } @@ -22,7 +21,7 @@ pub(crate) fn inferred_outlives(tcx: TyCtxt<'_>) { preds.sort(); let span = tcx.def_span(id.owner_id); - let mut err = tcx.dcx().struct_span_err(span, sym::rustc_outlives.as_str()); + let mut err = tcx.dcx().struct_span_err(span, sym::rustc_dump_inferred_outlives.as_str()); for pred in preds { err.note(pred); } diff --git a/compiler/rustc_hir_analysis/src/variance/dump.rs b/compiler/rustc_hir_analysis/src/variance/dump.rs index e625d9dfe3b5..2e1746473695 100644 --- a/compiler/rustc_hir_analysis/src/variance/dump.rs +++ b/compiler/rustc_hir_analysis/src/variance/dump.rs @@ -1,7 +1,7 @@ use std::fmt::Write; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::LocalDefId; use rustc_hir::find_attr; use rustc_middle::ty::{GenericArgs, TyCtxt}; @@ -26,23 +26,30 @@ fn format_variances(tcx: TyCtxt<'_>, def_id: LocalDefId) -> String { pub(crate) fn variances(tcx: TyCtxt<'_>) { let crate_items = tcx.hir_crate_items(()); - if find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcVarianceOfOpaques) { + if find_attr!(tcx, crate, RustcDumpVariancesOfOpaques) { for id in crate_items.opaques() { - tcx.dcx().emit_err(crate::errors::VariancesOf { - span: tcx.def_span(id), - variances: format_variances(tcx, id), - }); + tcx.dcx().span_err(tcx.def_span(id), format_variances(tcx, id)); } } - for id in crate_items.free_items() { - if !find_attr!(tcx.get_all_attrs(id.owner_id), AttributeKind::RustcVariance) { + for id in crate_items.owners() { + if !find_attr!(tcx, id, RustcDumpVariances) { continue; } - tcx.dcx().emit_err(crate::errors::VariancesOf { - span: tcx.def_span(id.owner_id), - variances: format_variances(tcx, id.owner_id.def_id), - }); + match tcx.def_kind(id) { + DefKind::AssocFn | DefKind::Fn | DefKind::Enum | DefKind::Struct | DefKind::Union => {} + DefKind::TyAlias if tcx.type_alias_is_lazy(id) => {} + kind => { + let message = format!( + "attr parsing didn't report an error for `#[{}]` on {kind:?}", + rustc_span::sym::rustc_dump_variances, + ); + tcx.dcx().span_delayed_bug(tcx.def_span(id), message); + continue; + } + } + + tcx.dcx().span_err(tcx.def_span(id), format_variances(tcx, id.def_id)); } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index fc6cc4e67251..e806d72d3dd2 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -9,21 +9,22 @@ use std::vec; use rustc_abi::ExternAbi; +use rustc_ast as ast; use rustc_ast::util::parser::{self, ExprPrecedence, Fixity}; use rustc_ast::{DUMMY_NODE_ID, DelimArgs}; 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_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, - PreciseCapturingArg, RangeEnd, Term, TyPatKind, + PreciseCapturingArg, RangeEnd, Term, TyFieldPath, TyPatKind, }; use rustc_span::source_map::{SourceMap, Spanned}; use rustc_span::{DUMMY_SP, FileName, Ident, Span, Symbol, kw, sym}; -use {rustc_ast as ast, rustc_hir as hir}; pub fn id_to_string(cx: &dyn rustc_hir::intravisit::HirTyCtxt<'_>, hir_id: HirId) -> String { to_string(&cx, |s| s.print_node(cx.hir_node(hir_id))) @@ -464,6 +465,17 @@ fn print_type(&mut self, ty: &hir::Ty<'_>) { self.word(" is "); self.print_ty_pat(pat); } + hir::TyKind::FieldOf(ty, TyFieldPath { variant, field }) => { + self.word("field_of!("); + self.print_type(ty); + self.word(", "); + if let Some(variant) = *variant { + self.print_ident(variant); + self.word("."); + } + self.print_ident(*field); + self.word(")"); + } } self.end(ib) } @@ -1303,6 +1315,7 @@ fn print_expr_struct( self.end(ib); } hir::StructTailExpr::None => {} + hir::StructTailExpr::NoneWithError(_) => {} } self.space(); self.word("}"); diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 7ad0b85ee53f..93662b36a05d 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -3,7 +3,6 @@ use rustc_abi::{CanonAbi, ExternAbi}; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey, msg}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, HirId, LangItem, find_attr}; @@ -51,7 +50,7 @@ pub(crate) fn check_legal_trait_for_method_call( }; return Err(tcx.dcx().emit_err(errors::ExplicitDestructorCall { span, sugg })); } - tcx.ensure_ok().coherent_trait(trait_id) + tcx.ensure_result().coherent_trait(trait_id) } #[derive(Debug)] @@ -88,7 +87,7 @@ pub(crate) fn check_expr_call( result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef); } - match autoderef.final_ty().kind() { + match *autoderef.final_ty().kind() { ty::FnDef(def_id, _) => { let abi = self.tcx.fn_sig(def_id).skip_binder().skip_binder().abi; self.check_call_abi(abi, call_expr.span); @@ -99,6 +98,15 @@ pub(crate) fn check_expr_call( _ => { /* cannot have a non-rust abi */ } } + if self.is_scalable_vector_ctor(autoderef.final_ty()) { + let mut err = self.dcx().create_err(errors::ScalableVectorCtor { + span: callee_expr.span, + ty: autoderef.final_ty(), + }); + err.span_label(callee_expr.span, "you can create scalable vectors using intrinsics"); + Ty::new_error(self.tcx, err.emit()); + } + self.register_predicates(autoderef.into_obligations()); let output = match result { @@ -421,6 +429,19 @@ fn try_overloaded_call_traits( None } + fn is_scalable_vector_ctor(&self, callee_ty: Ty<'_>) -> bool { + if let ty::FnDef(def_id, _) = *callee_ty.kind() + && let def::DefKind::Ctor(def::CtorOf::Struct, _) = self.tcx.def_kind(def_id) + { + self.tcx + .opt_parent(def_id) + .and_then(|id| self.tcx.adt_def(id).repr().scalable) + .is_some() + } else { + false + } + } + /// Give appropriate suggestion when encountering `||{/* not callable */}()`, where the /// likely intention is to call the closure, suggest `(||{})()`. (#55851) fn identify_bad_closure_def_and_call( @@ -526,12 +547,7 @@ fn confirm_builtin_call( // Unit testing: function items annotated with // `#[rustc_evaluate_where_clauses]` trigger special output // to let us test the trait evaluation system. - if self.has_rustc_attrs - && find_attr!( - self.tcx.get_all_attrs(def_id), - AttributeKind::RustcEvaluateWhereClauses - ) - { + if self.has_rustc_attrs && find_attr!(self.tcx, def_id, RustcEvaluateWhereClauses) { let predicates = self.tcx.predicates_of(def_id); let predicates = predicates.instantiate(self.tcx, args); for (predicate, predicate_span) in predicates { @@ -905,16 +921,8 @@ pub(super) fn enforce_context_effects( callee_did: DefId, callee_args: GenericArgsRef<'tcx>, ) { - // FIXME(const_trait_impl): We should be enforcing these effects unconditionally. - // This can be done as soon as we convert the standard library back to - // using const traits, since if we were to enforce these conditions now, - // we'd fail on basically every builtin trait call (i.e. `1 + 2`). - if !self.tcx.features().const_trait_impl() { - return; - } - // If we have `rustc_do_not_const_check`, do not check `[const]` bounds. - if self.has_rustc_attrs && self.tcx.has_attr(self.body_id, sym::rustc_do_not_const_check) { + if self.has_rustc_attrs && find_attr!(self.tcx, self.body_id, RustcDoNotConstCheck) { return; } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index b5094d736dd5..eaa87f1d4cbc 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -485,7 +485,7 @@ fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError<'tcx>) { )); } - err.multipart_suggestion_verbose( + err.multipart_suggestion( "consider borrowing the value", suggestion, Applicability::MachineApplicable, diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 88d2e80f1521..8a73125d6476 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -778,7 +778,7 @@ fn coerce_unsized_old_solver( // for local impls, since upstream impls should be valid. if impl_source.impl_def_id.is_local() && let Err(guar) = - self.tcx.ensure_ok().coerce_unsized_info(impl_source.impl_def_id) + self.tcx.ensure_result().coerce_unsized_info(impl_source.impl_def_id) { self.fcx.set_tainted_by_errors(guar); } diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 68cbfa728058..0d49e0624053 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,5 +1,4 @@ use rustc_errors::{Applicability, Diag, MultiSpan, listify}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::Res; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, find_attr}; @@ -724,7 +723,8 @@ fn annotate_expected_due_to_let_ty( hir::Path { res: hir::def::Res::Def( - hir::def::DefKind::Static { .. } | hir::def::DefKind::Const, + hir::def::DefKind::Static { .. } + | hir::def::DefKind::Const { .. }, def_id, ), .. @@ -888,7 +888,7 @@ fn annotate_mut_binding_to_immutable_binding( ]); // We suggest changing the argument from `mut ident: &Ty` to `ident: &'_ mut Ty` and the // assignment from `ident = val;` to `*ident = val;`. - err.multipart_suggestion_verbose( + err.multipart_suggestion( "you might have meant to mutate the pointed at value being passed in, instead of \ changing the reference in the local binding", sugg, @@ -1008,7 +1008,7 @@ fn annotate_alternative_method_deref( ); let container_id = pick.item.container_id(self.tcx); let container = with_no_trimmed_paths!(self.tcx.def_path_str(container_id)); - for def_id in pick.import_ids { + for &def_id in pick.import_ids { let hir_id = self.tcx.local_def_id_to_hir_id(def_id); path_span .push_span_label(self.tcx.hir_span(hir_id), format!("`{container}` imported here")); @@ -1092,7 +1092,7 @@ pub(crate) fn get_conversion_methods_for_diagnostic( // // FIXME? Other potential candidate methods: `as_ref` and // `as_mut`? - && find_attr!(self.tcx.get_all_attrs(m.def_id), AttributeKind::RustcConversionSuggestion) + && find_attr!(self.tcx, m.def_id, RustcConversionSuggestion) }, ); diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index e0467f4dc6ed..5c6a66403019 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -11,7 +11,7 @@ }; use rustc_hir as hir; use rustc_hir::ExprKind; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; use rustc_span::source_map::Spanned; @@ -215,7 +215,7 @@ pub(crate) struct MissingParenthesesInRange<'tcx> { pub add_missing_parentheses: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum NeverTypeFallbackFlowingIntoUnsafe { #[help("specify the type explicitly")] #[diag("never type fallback affects this call to an `unsafe` function")] @@ -249,7 +249,7 @@ pub(crate) enum NeverTypeFallbackFlowingIntoUnsafe { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[help("specify the types explicitly")] #[diag("this function depends on never type fallback being `()`")] pub(crate) struct DependencyOnUnitNeverTypeFallback<'tcx> { @@ -304,7 +304,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } } - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( "use `()` annotations to avoid fallback changes", suggestions, Applicability::MachineApplicable, @@ -372,7 +372,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")] #[help( "if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead" @@ -411,7 +411,7 @@ pub(crate) struct LossyProvenanceInt2PtrSuggestion { pub hi: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}`" )] @@ -490,15 +490,6 @@ pub(crate) fn new() -> Self { } } -#[derive(Diagnostic)] -#[diag("no field `{$field}` on type `{$ty}`", code = E0609)] -pub(crate) struct NoFieldOnType<'tcx> { - #[primary_span] - pub(crate) span: Span, - pub(crate) ty: Ty<'tcx>, - pub(crate) field: Ident, -} - #[derive(Diagnostic)] #[diag("no field named `{$field}` on enum variant `{$container}::{$ident}`", code = E0609)] pub(crate) struct NoFieldOnVariant<'tcx> { @@ -569,6 +560,14 @@ pub(crate) struct InvalidCallee<'tcx> { pub found: String, } +#[derive(Diagnostic)] +#[diag("scalable vector types cannot be initialised using their constructor")] +pub(crate) struct ScalableVectorCtor<'tcx> { + #[primary_span] + pub span: Span, + pub ty: Ty<'tcx>, +} + #[derive(Diagnostic)] #[diag("cannot cast `{$expr_ty}` to a pointer that {$known_wide -> [true] is @@ -761,7 +760,7 @@ pub(crate) struct SuggestPtrNullMut { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "trivial {$numeric -> [true] numeric cast @@ -988,13 +987,17 @@ impl rustc_errors::Subdiagnostic for CastUnknownPointerSub { fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { CastUnknownPointerSub::To(span) => { - let msg = diag.eagerly_translate(msg!("needs more type information")); + let msg = msg!("needs more type information"); diag.span_label(span, msg); - let msg = diag.eagerly_translate(msg!("the type information given here is insufficient to check whether the pointer cast is valid")); + let msg = msg!( + "the type information given here is insufficient to check whether the pointer cast is valid" + ); diag.note(msg); } CastUnknownPointerSub::From(span) => { - let msg = diag.eagerly_translate(msg!("the type information given here is insufficient to check whether the pointer cast is valid")); + let msg = msg!( + "the type information given here is insufficient to check whether the pointer cast is valid" + ); diag.span_label(span, msg); } } @@ -1101,7 +1104,7 @@ pub(crate) struct InnerItem { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("can't reference `Self` constructor from outer item")] pub(crate) struct SelfCtorFromOuterItemLint { #[label( @@ -1206,7 +1209,7 @@ pub(crate) struct ReplaceCommaWithSemicolon { pub descr: &'static str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait")] pub(crate) struct SupertraitItemShadowing { pub item: Symbol, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 5b40531f9462..2de101a1e452 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -6,6 +6,7 @@ //! See [`rustc_hir_analysis::check`] for more context on type checking in general. use rustc_abi::{FIRST_VARIANT, FieldIdx}; +use rustc_ast as ast; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -15,12 +16,13 @@ Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, Subdiagnostic, listify, pluralize, struct_span_code_err, }; -use rustc_hir::attrs::AttributeKind; +use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal}; use rustc_hir_analysis::NoVariantNamed; +use rustc_hir_analysis::errors::NoFieldOnType; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin}; use rustc_infer::traits::query::NoSolution; @@ -37,14 +39,13 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt}; use tracing::{debug, instrument, trace}; -use {rustc_ast as ast, rustc_hir as hir}; use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; use crate::coercion::CoerceMany; use crate::errors::{ AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer, - FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType, + FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, TypeMismatchFruTypo, YieldExprOutsideOfCoroutine, }; @@ -2000,229 +2001,260 @@ fn check_expr_struct_fields( return; } - if let hir::StructTailExpr::DefaultFields(span) = *base_expr { - let mut missing_mandatory_fields = Vec::new(); - let mut missing_optional_fields = Vec::new(); - for f in &variant.fields { - let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); - if let Some(_) = remaining_fields.remove(&ident) { - if f.value.is_none() { - missing_mandatory_fields.push(ident); - } else { - missing_optional_fields.push(ident); + match *base_expr { + hir::StructTailExpr::DefaultFields(span) => { + let mut missing_mandatory_fields = Vec::new(); + let mut missing_optional_fields = Vec::new(); + for f in &variant.fields { + let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); + if let Some(_) = remaining_fields.remove(&ident) { + if f.value.is_none() { + missing_mandatory_fields.push(ident); + } else { + missing_optional_fields.push(ident); + } } } - } - if !self.tcx.features().default_field_values() { - let sugg = self.tcx.crate_level_attribute_injection_span(); - self.dcx().emit_err(BaseExpressionDoubleDot { - span: span.shrink_to_hi(), - // We only mention enabling the feature if this is a nightly rustc *and* the - // expression would make sense with the feature enabled. - default_field_values_suggestion: if self.tcx.sess.is_nightly_build() - && missing_mandatory_fields.is_empty() - && !missing_optional_fields.is_empty() - { - Some(sugg) - } else { - None - }, - add_expr: if !missing_mandatory_fields.is_empty() - || !missing_optional_fields.is_empty() - { - Some(BaseExpressionDoubleDotAddExpr { span: span.shrink_to_hi() }) - } else { - None - }, - remove_dots: if missing_mandatory_fields.is_empty() - && missing_optional_fields.is_empty() - { - Some(BaseExpressionDoubleDotRemove { span }) - } else { - None - }, - }); - return; - } - if variant.fields.is_empty() { - let mut err = self.dcx().struct_span_err( - span, - format!( - "`{adt_ty}` has no fields, `..` needs at least one default field in the \ - struct definition", - ), - ); - err.span_label(path_span, "this type has no fields"); - err.emit(); - } - if !missing_mandatory_fields.is_empty() { - let s = pluralize!(missing_mandatory_fields.len()); - let fields = listify(&missing_mandatory_fields, |f| format!("`{f}`")).unwrap(); - self.dcx() - .struct_span_err( - span.shrink_to_lo(), - format!("missing field{s} {fields} in initializer"), - ) - .with_span_label( - span.shrink_to_lo(), - "fields that do not have a defaulted value must be provided explicitly", - ) - .emit(); - return; - } - let fru_tys = match adt_ty.kind() { - ty::Adt(adt, args) if adt.is_struct() => variant - .fields - .iter() - .map(|f| self.normalize(span, f.ty(self.tcx, args))) - .collect(), - ty::Adt(adt, args) if adt.is_enum() => variant - .fields - .iter() - .map(|f| self.normalize(span, f.ty(self.tcx, args))) - .collect(), - _ => { - self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { span }); + if !self.tcx.features().default_field_values() { + let sugg = self.tcx.crate_level_attribute_injection_span(); + self.dcx().emit_err(BaseExpressionDoubleDot { + span: span.shrink_to_hi(), + // We only mention enabling the feature if this is a nightly rustc *and* the + // expression would make sense with the feature enabled. + default_field_values_suggestion: if self.tcx.sess.is_nightly_build() + && missing_mandatory_fields.is_empty() + && !missing_optional_fields.is_empty() + { + Some(sugg) + } else { + None + }, + add_expr: if !missing_mandatory_fields.is_empty() + || !missing_optional_fields.is_empty() + { + Some(BaseExpressionDoubleDotAddExpr { span: span.shrink_to_hi() }) + } else { + None + }, + remove_dots: if missing_mandatory_fields.is_empty() + && missing_optional_fields.is_empty() + { + Some(BaseExpressionDoubleDotRemove { span }) + } else { + None + }, + }); return; } - }; - self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys); - } else if let hir::StructTailExpr::Base(base_expr) = base_expr { - // FIXME: We are currently creating two branches here in order to maintain - // consistency. But they should be merged as much as possible. - let fru_tys = if self.tcx.features().type_changing_struct_update() { - if adt.is_struct() { - // Make some fresh generic parameters for our ADT type. - let fresh_args = self.fresh_args_for_item(base_expr.span, adt.did()); - // We do subtyping on the FRU fields first, so we can - // learn exactly what types we expect the base expr - // needs constrained to be compatible with the struct - // type we expect from the expectation value. - let fru_tys = variant - .fields - .iter() - .map(|f| { - let fru_ty = self - .normalize(expr.span, self.field_ty(base_expr.span, f, fresh_args)); - let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); - if let Some(_) = remaining_fields.remove(&ident) { - let target_ty = self.field_ty(base_expr.span, f, args); - let cause = self.misc(base_expr.span); - match self.at(&cause, self.param_env).sup( - // We're already using inference variables for any params, and don't allow converting - // between different structs, so there is no way this ever actually defines an opaque type. - // Thus choosing `Yes` is fine. - DefineOpaqueTypes::Yes, - target_ty, - fru_ty, - ) { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations) - } - Err(_) => { - span_bug!( - cause.span, - "subtyping remaining fields of type changing FRU failed: {target_ty} != {fru_ty}: {}::{}", - variant.name, - ident.name, - ); - } - } - } - self.resolve_vars_if_possible(fru_ty) - }) - .collect(); - // The use of fresh args that we have subtyped against - // our base ADT type's fields allows us to guide inference - // along so that, e.g. - // ``` - // MyStruct<'a, F1, F2, const C: usize> { - // f: F1, - // // Other fields that reference `'a`, `F2`, and `C` - // } - // - // let x = MyStruct { - // f: 1usize, - // ..other_struct - // }; - // ``` - // will have the `other_struct` expression constrained to - // `MyStruct<'a, _, F2, C>`, as opposed to just `_`... - // This is important to allow coercions to happen in - // `other_struct` itself. See `coerce-in-base-expr.rs`. - let fresh_base_ty = Ty::new_adt(self.tcx, *adt, fresh_args); - self.check_expr_has_type_or_error( - base_expr, - self.resolve_vars_if_possible(fresh_base_ty), - |_| {}, + if variant.fields.is_empty() { + let mut err = self.dcx().struct_span_err( + span, + format!( + "`{adt_ty}` has no fields, `..` needs at least one default field in \ + the struct definition", + ), ); - fru_tys - } else { - // Check the base_expr, regardless of a bad expected adt_ty, so we can get - // type errors on that expression, too. - self.check_expr(base_expr); - self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); - return; + err.span_label(path_span, "this type has no fields"); + err.emit(); } - } else { - self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { - let base_ty = self.typeck_results.borrow().expr_ty(*base_expr); - let same_adt = matches!((adt_ty.kind(), base_ty.kind()), - (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt); - if self.tcx.sess.is_nightly_build() && same_adt { - feature_err( - &self.tcx.sess, - sym::type_changing_struct_update, - base_expr.span, - "type changing struct updating is experimental", + if !missing_mandatory_fields.is_empty() { + let s = pluralize!(missing_mandatory_fields.len()); + let fields = listify(&missing_mandatory_fields, |f| format!("`{f}`")).unwrap(); + self.dcx() + .struct_span_err( + span.shrink_to_lo(), + format!("missing field{s} {fields} in initializer"), + ) + .with_span_label( + span.shrink_to_lo(), + "fields that do not have a defaulted value must be provided explicitly", ) .emit(); - } - }); - match adt_ty.kind() { + return; + } + let fru_tys = match adt_ty.kind() { ty::Adt(adt, args) if adt.is_struct() => variant .fields .iter() - .map(|f| self.normalize(expr.span, f.ty(self.tcx, args))) + .map(|f| self.normalize(span, f.ty(self.tcx, args))) + .collect(), + ty::Adt(adt, args) if adt.is_enum() => variant + .fields + .iter() + .map(|f| self.normalize(span, f.ty(self.tcx, args))) .collect(), _ => { + self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { span }); + return; + } + }; + self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys); + } + hir::StructTailExpr::Base(base_expr) => { + // FIXME: We are currently creating two branches here in order to maintain + // consistency. But they should be merged as much as possible. + let fru_tys = if self.tcx.features().type_changing_struct_update() { + if adt.is_struct() { + // Make some fresh generic parameters for our ADT type. + let fresh_args = self.fresh_args_for_item(base_expr.span, adt.did()); + // We do subtyping on the FRU fields first, so we can + // learn exactly what types we expect the base expr + // needs constrained to be compatible with the struct + // type we expect from the expectation value. + let fru_tys = variant + .fields + .iter() + .map(|f| { + let fru_ty = self.normalize( + expr.span, + self.field_ty(base_expr.span, f, fresh_args), + ); + let ident = + self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); + if let Some(_) = remaining_fields.remove(&ident) { + let target_ty = self.field_ty(base_expr.span, f, args); + let cause = self.misc(base_expr.span); + match self.at(&cause, self.param_env).sup( + // We're already using inference variables for any params, + // and don't allow converting between different structs, + // so there is no way this ever actually defines an opaque + // type. Thus choosing `Yes` is fine. + DefineOpaqueTypes::Yes, + target_ty, + fru_ty, + ) { + Ok(InferOk { obligations, value: () }) => { + self.register_predicates(obligations) + } + Err(_) => { + span_bug!( + cause.span, + "subtyping remaining fields of type changing FRU \ + failed: {target_ty} != {fru_ty}: {}::{}", + variant.name, + ident.name, + ); + } + } + } + self.resolve_vars_if_possible(fru_ty) + }) + .collect(); + // The use of fresh args that we have subtyped against + // our base ADT type's fields allows us to guide inference + // along so that, e.g. + // ``` + // MyStruct<'a, F1, F2, const C: usize> { + // f: F1, + // // Other fields that reference `'a`, `F2`, and `C` + // } + // + // let x = MyStruct { + // f: 1usize, + // ..other_struct + // }; + // ``` + // will have the `other_struct` expression constrained to + // `MyStruct<'a, _, F2, C>`, as opposed to just `_`... + // This is important to allow coercions to happen in + // `other_struct` itself. See `coerce-in-base-expr.rs`. + let fresh_base_ty = Ty::new_adt(self.tcx, *adt, fresh_args); + self.check_expr_has_type_or_error( + base_expr, + self.resolve_vars_if_possible(fresh_base_ty), + |_| {}, + ); + fru_tys + } else { + // Check the base_expr, regardless of a bad expected adt_ty, so we can get + // type errors on that expression, too. + self.check_expr(base_expr); self.dcx() .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); return; } - } - }; - self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys); - } else if adt_kind != AdtKind::Union - && !remaining_fields.is_empty() - //~ non_exhaustive already reported, which will only happen for extern modules - && !variant.field_list_has_applicable_non_exhaustive() - { - debug!(?remaining_fields); - let private_fields: Vec<&ty::FieldDef> = variant - .fields - .iter() - .filter(|field| !field.vis.is_accessible_from(tcx.parent_module(expr.hir_id), tcx)) - .collect(); + } else { + self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { + let base_ty = self.typeck_results.borrow().expr_ty(base_expr); + let same_adt = matches!((adt_ty.kind(), base_ty.kind()), + (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt); + if self.tcx.sess.is_nightly_build() && same_adt { + feature_err( + &self.tcx.sess, + sym::type_changing_struct_update, + base_expr.span, + "type changing struct updating is experimental", + ) + .emit(); + } + }); + match adt_ty.kind() { + ty::Adt(adt, args) if adt.is_struct() => variant + .fields + .iter() + .map(|f| self.normalize(expr.span, f.ty(self.tcx, args))) + .collect(), + _ => { + self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { + span: base_expr.span, + }); + return; + } + } + }; + self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys); + } + rustc_hir::StructTailExpr::NoneWithError(guaranteed) => { + // If parsing the struct recovered from a syntax error, do not report missing + // fields. This prevents spurious errors when a field is intended to be present + // but a preceding syntax error caused it not to be parsed. For example, if a + // struct type `StructName` has fields `foo` and `bar`, then + // StructName { foo(), bar: 2 } + // will not successfully parse a field `foo`, but we will not mention that, + // since the syntax error has already been reported. - if !private_fields.is_empty() { - self.report_private_fields( - adt_ty, - path_span, - expr.span, - private_fields, - hir_fields, - ); - } else { - self.report_missing_fields( - adt_ty, - path_span, - expr.span, - remaining_fields, - variant, - hir_fields, - args, - ); + // Signal that type checking has failed, even though we haven’t emitted a diagnostic + // about it ourselves. + self.infcx.set_tainted_by_errors(guaranteed); + } + rustc_hir::StructTailExpr::None => { + if adt_kind != AdtKind::Union + && !remaining_fields.is_empty() + //~ non_exhaustive already reported, which will only happen for extern modules + && !variant.field_list_has_applicable_non_exhaustive() + { + debug!(?remaining_fields); + + // Report missing fields. + + let private_fields: Vec<&ty::FieldDef> = variant + .fields + .iter() + .filter(|field| { + !field.vis.is_accessible_from(tcx.parent_module(expr.hir_id), tcx) + }) + .collect(); + + if !private_fields.is_empty() { + self.report_private_fields( + adt_ty, + path_span, + expr.span, + private_fields, + hir_fields, + ); + } else { + self.report_missing_fields( + adt_ty, + path_span, + expr.span, + remaining_fields, + variant, + hir_fields, + args, + ); + } + } } } } @@ -2449,7 +2481,7 @@ fn report_private_fields( .tcx .inherent_impls(def_id) .into_iter() - .flat_map(|i| self.tcx.associated_items(i).in_definition_order()) + .flat_map(|&i| self.tcx.associated_items(i).in_definition_order()) // Only assoc fn with no receivers. .filter(|item| item.is_fn() && !item.is_method()) .filter_map(|item| { @@ -3184,7 +3216,7 @@ fn no_such_field_err( // Check if there is an associated function with the same name. if let Some(def_id) = base_ty.peel_refs().ty_adt_def().map(|d| d.did()) { - for impl_def_id in self.tcx.inherent_impls(def_id) { + for &impl_def_id in self.tcx.inherent_impls(def_id) { for item in self.tcx.associated_items(impl_def_id).in_definition_order() { if let ExprKind::Field(base_expr, _) = expr.kind && item.name() == field.name @@ -3676,7 +3708,7 @@ fn check_expr_asm_operand(&self, expr: &'tcx hir::Expr<'tcx>, is_input: bool) { fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) -> Ty<'tcx> { if let rustc_ast::AsmMacro::NakedAsm = asm.asm_macro { - if !find_attr!(self.tcx.get_all_attrs(self.body_id), AttributeKind::Naked(..)) { + if !find_attr!(self.tcx, self.body_id, Naked(..)) { self.tcx.dcx().emit_err(NakedAsmOutsideNakedFn { span }); } } diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 77ecfc2fb0be..0a492c795b29 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -673,7 +673,9 @@ fn walk_struct_expr<'hir>( let with_expr = match *opt_with { hir::StructTailExpr::Base(w) => &*w, - hir::StructTailExpr::DefaultFields(_) | hir::StructTailExpr::None => { + hir::StructTailExpr::DefaultFields(_) + | hir::StructTailExpr::None + | hir::StructTailExpr::NoneWithError(_) => { return Ok(()); } }; @@ -908,7 +910,8 @@ fn walk_pat( let res = self.cx.typeck_results().qpath_res(qpath, *hir_id); match res { - Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { + Res::Def(DefKind::Const { .. }, _) + | Res::Def(DefKind::AssocConst { .. }, _) => { // Named constants have to be equated with the value // being matched, so that's a read of the value being matched. // @@ -1405,9 +1408,9 @@ fn cat_res( match res { Res::Def( DefKind::Ctor(..) - | DefKind::Const + | DefKind::Const { .. } | DefKind::ConstParam - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Fn | DefKind::AssocFn, _, diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index c10e7f3bfb8b..f7ecfb6cd09c 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -2,8 +2,8 @@ use std::ops::ControlFlow; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::graph; use rustc_data_structures::graph::vec_graph::VecGraph; -use rustc_data_structures::graph::{self}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir as hir; use rustc_hir::HirId; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index e8d5ff017cf8..c24d1127d5a6 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -3,7 +3,9 @@ use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{ + Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, Level, MultiSpan, +}; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::VisitorExt; @@ -80,6 +82,26 @@ pub(crate) fn transform_args_for_inherent_type_const( /// Produces warning on the given node, if the current point in the /// function is unreachable, and there hasn't been another warning. pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) { + struct UnreachableItem<'a, 'b> { + kind: &'a str, + span: Span, + orig_span: Span, + 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, ()> { + 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( + orig_span, + custom_note.map(|c| c.to_owned()).unwrap_or_else(|| { + "any code following this expression is unreachable".to_owned() + }), + ) + } + } + let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() else { return; }; @@ -102,14 +124,12 @@ pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) { debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); - let msg = format!("unreachable {kind}"); - self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { - lint.primary_message(msg.clone()); - lint.span_label(span, msg).span_label( - orig_span, - custom_note.unwrap_or("any code following this expression is unreachable"), - ); - }) + self.tcx().emit_node_span_lint( + lint::builtin::UNREACHABLE_CODE, + id, + span, + UnreachableItem { kind, span, orig_span, custom_note }, + ); } /// Resolves type and const variables in `t` if possible. Unlike the infcx @@ -988,7 +1008,7 @@ pub(crate) fn instantiate_value_path( Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => { err_extend = GenericsArgsErrExtend::DefVariant(segments); } - Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => { + Res::Def(DefKind::AssocFn | DefKind::AssocConst { .. }, def_id) => { let assoc_item = tcx.associated_item(def_id); let container = assoc_item.container; let container_id = assoc_item.container_id(tcx); @@ -1118,7 +1138,7 @@ pub(crate) fn instantiate_value_path( // error in `validate_res_from_ribs` -- it's just difficult to tell whether the // self type has any generic types during rustc_resolve, which is what we use // to determine if this is a hard error or warning. - if std::iter::successors(Some(self.body_id.to_def_id()), |def_id| { + if std::iter::successors(Some(self.body_id.to_def_id()), |&def_id| { self.tcx.generics_of(def_id).parent }) .all(|def_id| def_id != impl_def_id) @@ -1314,7 +1334,7 @@ fn inferred_kind( ) }); - let args_for_user_type = if let Res::Def(DefKind::AssocConst, def_id) = res { + let args_for_user_type = if let Res::Def(DefKind::AssocConst { .. }, def_id) = res { self.transform_args_for_inherent_type_const(def_id, args_raw) } else { args_raw @@ -1362,7 +1382,7 @@ fn inferred_kind( debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_instantiated); - let args = if let Res::Def(DefKind::AssocConst, def_id) = res { + let args = if let Res::Def(DefKind::AssocConst { .. }, def_id) = res { self.transform_args_for_inherent_type_const(def_id, args) } else { args diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 54d8306936dd..9faa75e18480 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2,9 +2,11 @@ use std::{fmt, iter}; use itertools::Itertools; +use rustc_ast as ast; use rustc_data_structures::fx::FxIndexSet; 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::DivergingBlockBehavior; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -25,7 +27,6 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext}; use smallvec::SmallVec; use tracing::debug; -use {rustc_ast as ast, rustc_hir as hir}; use crate::Expectation::*; use crate::TupleArgumentsFlag::*; @@ -82,7 +83,8 @@ pub(in super::super) fn check_repeat_exprs(&self) { hir::ExprKind::ConstBlock(..) => return None, hir::ExprKind::Path(qpath) => { let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id); - if let Res::Def(DefKind::Const | DefKind::AssocConst, _) = res { + if let Res::Def(DefKind::Const { .. } | DefKind::AssocConst { .. }, _) = res + { return None; } } @@ -1994,7 +1996,7 @@ fn check_wrap_args_in_tuple(&self) -> Option { ), ); err.code(self.err_code.to_owned()); - err.multipart_suggestion_verbose( + err.multipart_suggestion( "wrap these arguments in parentheses to construct a tuple", vec![ (lo.shrink_to_lo(), "(".to_string()), @@ -2649,7 +2651,7 @@ fn format_suggestion_text( Some(format!("provide the argument{}", if plural { "s" } else { "" })) } SuggestionText::Remove(plural) => { - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!("remove the extra argument{}", if plural { "s" } else { "" }), suggestions, Applicability::HasPlaceholders, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 412df9162e9f..873e95f13bb9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -8,9 +8,8 @@ use std::cell::{Cell, RefCell}; use std::ops::Deref; -use hir::def_id::CRATE_DEF_ID; use rustc_errors::DiagCtxtHandle; -use rustc_hir::attrs::{AttributeKind, DivergingBlockBehavior, DivergingFallbackBehavior}; +use rustc_hir::attrs::{DivergingBlockBehavior, DivergingFallbackBehavior}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, HirId, ItemLocalMap, find_attr}; use rustc_hir_analysis::hir_ty_lowering::{ @@ -516,5 +515,5 @@ fn parse_never_type_options_attr( // Error handling is dubious here (unwraps), but that's probably fine for an internal attribute. // Just don't write incorrect attributes <3 - find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcNeverTypeOptions {fallback, diverging_block_default} => (*fallback, *diverging_block_default)).unwrap_or_default() + find_attr!(tcx, crate, RustcNeverTypeOptions {fallback, diverging_block_default} => (*fallback, *diverging_block_default)).unwrap_or_default() } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 735e684500fe..801f5acf9d2f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -156,11 +156,7 @@ pub(crate) fn suggest_fn_call( } }; - err.multipart_suggestion_verbose( - format!("use parentheses to {msg}"), - sugg, - applicability, - ); + err.multipart_suggestion(format!("use parentheses to {msg}"), sugg, applicability); return true; } false @@ -245,7 +241,7 @@ pub(crate) fn suggest_two_fn_call( } } - err.multipart_suggestion_verbose("use parentheses to call these", sugg, applicability); + err.multipart_suggestion("use parentheses to call these", sugg, applicability); true } else { @@ -298,7 +294,7 @@ pub(crate) fn suggest_deref_ref_or_into( self.suggest_deref_or_ref(expr, found, expected) { if verbose { - err.multipart_suggestion_verbose(msg, suggestion, applicability); + err.multipart_suggestion(msg, suggestion, applicability); } else { err.multipart_suggestion(msg, suggestion, applicability); } @@ -571,7 +567,7 @@ pub(in super::super) fn suggest_boxing_when_appropriate( return false; } if self.may_coerce(Ty::new_box(self.tcx, found), expected) { - let suggest_boxing = match found.kind() { + let suggest_boxing = match *found.kind() { ty::Tuple(tuple) if tuple.is_empty() => { errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span } } @@ -1299,7 +1295,7 @@ fn is_in_arm<'tcx>(expr: &'tcx hir::Expr<'tcx>, tcx: TyCtxt<'tcx>) -> bool { if !is_in_arm(expr, self.tcx) { suggs.push((span.shrink_to_hi(), ";".to_string())); } - err.multipart_suggestion_verbose( + err.multipart_suggestion( "you might have meant to return this value", suggs, Applicability::MaybeIncorrect, @@ -1611,7 +1607,7 @@ pub(crate) fn suggest_deref_unwrap_or( return; } let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`"); - err.multipart_suggestion_verbose( + err.multipart_suggestion( msg, vec![ (call_ident.span, "map_or".to_owned()), @@ -1639,7 +1635,7 @@ pub(crate) fn suggest_block_to_brackets( && snippet.starts_with('{') && snippet.ends_with('}') { - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( "to create an array, use square brackets instead of curly braces", vec![ ( @@ -1838,7 +1834,9 @@ pub(crate) fn suggest_associated_const( } _ => return false, }; - if item.def_id == old_def_id || self.tcx.def_kind(item.def_id) != DefKind::AssocConst { + if item.def_id == old_def_id + || !matches!(self.tcx.def_kind(item.def_id), DefKind::AssocConst { .. }) + { // Same item return false; } @@ -2383,7 +2381,7 @@ pub(crate) fn suggest_semicolon_in_repeat_expr( && match expr.kind { ExprKind::Path(QPath::Resolved( None, - Path { res: Res::Def(DefKind::Const, _), .. }, + Path { res: Res::Def(DefKind::Const { .. }, _), .. }, )) => true, ExprKind::Call( Expr { @@ -2604,7 +2602,7 @@ pub(crate) fn suggest_compatible_variants( [] => { /* No variants to format */ } [(variant, ctor_kind, field_name, note)] => { // Just a single matching variant. - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!( "try wrapping the expression in `{variant}`{note}", note = note.as_deref().unwrap_or("") @@ -3110,6 +3108,7 @@ pub(crate) fn suggest_deref_or_ref( { let deref_kind = if checked_ty.is_box() { // detect Box::new(..) + // FIXME: use `box_new` diagnostic item instead? if let ExprKind::Call(box_new, [_]) = expr.kind && let ExprKind::Path(qpath) = &box_new.kind && let Res::Def(DefKind::AssocFn, fn_id) = @@ -3375,7 +3374,7 @@ pub(crate) fn suggest_cast( )); (msg, suggestion) }; - err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable); + err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable); }; let suggest_to_change_suffix_or_into = @@ -3410,7 +3409,7 @@ pub(crate) fn suggest_cast( } else { into_suggestion.clone() }; - err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable); + err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable); }; match (expected_ty.kind(), checked_ty.kind()) { @@ -3464,14 +3463,14 @@ pub(crate) fn suggest_cast( if found.bit_width() < exp.bit_width() { suggest_to_change_suffix_or_into(err, false, true); } else if literal_is_ty_suffixed(expr) { - err.multipart_suggestion_verbose( + err.multipart_suggestion( lit_msg, suffix_suggestion, Applicability::MachineApplicable, ); } else if can_cast { // Missing try_into implementation for `f64` to `f32` - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!("{cast_msg}, producing the closest possible value"), cast_suggestion, Applicability::MaybeIncorrect, // lossy conversion @@ -3481,14 +3480,14 @@ pub(crate) fn suggest_cast( } (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => { if literal_is_ty_suffixed(expr) { - err.multipart_suggestion_verbose( + err.multipart_suggestion( lit_msg, suffix_suggestion, Applicability::MachineApplicable, ); } else if can_cast { // Missing try_into implementation for `{float}` to `{integer}` - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!("{msg}, rounding the float towards zero"), cast_suggestion, Applicability::MaybeIncorrect, // lossy conversion @@ -3499,7 +3498,7 @@ pub(crate) fn suggest_cast( (ty::Float(exp), ty::Uint(found)) => { // if `found` is `None` (meaning found is `usize`), don't suggest `.into()` if exp.bit_width() > found.bit_width().unwrap_or(256) { - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!( "{msg}, producing the floating point representation of the integer", ), @@ -3507,14 +3506,14 @@ pub(crate) fn suggest_cast( Applicability::MachineApplicable, ); } else if literal_is_ty_suffixed(expr) { - err.multipart_suggestion_verbose( + err.multipart_suggestion( lit_msg, suffix_suggestion, Applicability::MachineApplicable, ); } else { // Missing try_into implementation for `{integer}` to `{float}` - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!( "{cast_msg}, producing the floating point representation of the integer, \ rounded if necessary", @@ -3528,7 +3527,7 @@ pub(crate) fn suggest_cast( (ty::Float(exp), ty::Int(found)) => { // if `found` is `None` (meaning found is `isize`), don't suggest `.into()` if exp.bit_width() > found.bit_width().unwrap_or(256) { - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!( "{}, producing the floating point representation of the integer", msg.clone(), @@ -3537,14 +3536,14 @@ pub(crate) fn suggest_cast( Applicability::MachineApplicable, ); } else if literal_is_ty_suffixed(expr) { - err.multipart_suggestion_verbose( + err.multipart_suggestion( lit_msg, suffix_suggestion, Applicability::MachineApplicable, ); } else { // Missing try_into implementation for `{integer}` to `{float}` - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!( "{}, producing the floating point representation of the integer, \ rounded if necessary", @@ -3561,7 +3560,7 @@ pub(crate) fn suggest_cast( | &ty::Int(ty::IntTy::I32 | ty::IntTy::I64 | ty::IntTy::I128), &ty::Char, ) => { - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!("{cast_msg}, since a `char` always occupies 4 bytes"), cast_suggestion, Applicability::MachineApplicable, diff --git a/compiler/rustc_hir_typeck/src/inline_asm.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs index 7c1655f8201d..f2b746ac96b1 100644 --- a/compiler/rustc_hir_typeck/src/inline_asm.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -1,6 +1,7 @@ use rustc_abi::FieldIdx; use rustc_ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxIndexSet; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; use rustc_middle::bug; @@ -168,6 +169,40 @@ fn check_asm_operand_type( is_input: bool, tied_input: Option<(&'tcx hir::Expr<'tcx>, Option)>, ) -> Option { + struct FormattingSubRegisterArg<'a> { + expr_span: Span, + idx: usize, + suggested_modifier: char, + suggested_result: &'a str, + suggested_size: u16, + default_modifier: char, + default_result: &'a str, + default_size: u16, + } + + impl<'a, 'b> Diagnostic<'a, ()> for FormattingSubRegisterArg<'b> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { + expr_span, + idx, + suggested_modifier, + suggested_result, + suggested_size, + default_modifier, + default_result, + default_size, + } = self; + Diag::new(dcx, level, "formatting may not be suitable for sub-register argument") + .with_span_label(expr_span, "for this argument") + .with_help(format!( + "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)", + )) + .with_help(format!( + "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)", + )) + } + } + let ty = self.expr_ty(expr); if ty.has_non_region_infer() { bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty); @@ -362,19 +397,19 @@ fn check_asm_operand_type( result: default_result, size: default_size, } = reg_class.default_modifier(asm_arch).unwrap(); - self.tcx().node_span_lint( + self.tcx().emit_node_span_lint( lint::builtin::ASM_SUB_REGISTER, expr.hir_id, spans, - |lint| { - lint.primary_message("formatting may not be suitable for sub-register argument"); - lint.span_label(expr.span, "for this argument"); - lint.help(format!( - "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)", - )); - lint.help(format!( - "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)", - )); + FormattingSubRegisterArg { + expr_span: expr.span, + idx, + suggested_modifier, + suggested_result, + suggested_size, + default_modifier, + default_result, + default_size, }, ); } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 8692720529d5..77c95fd55be3 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -1,7 +1,5 @@ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] -#![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iter_order_by)] #![feature(never_type)] @@ -452,7 +450,7 @@ fn report_unexpected_variant_res( } } - err.multipart_suggestion_verbose(descr, suggestion, Applicability::HasPlaceholders); + err.multipart_suggestion(descr, suggestion, Applicability::HasPlaceholders); err } Res::Def(DefKind::Variant, _) if expr.is_none() => { diff --git a/compiler/rustc_hir_typeck/src/loops.rs b/compiler/rustc_hir_typeck/src/loops.rs index 799e82ec13b8..e77c93641e1a 100644 --- a/compiler/rustc_hir_typeck/src/loops.rs +++ b/compiler/rustc_hir_typeck/src/loops.rs @@ -3,7 +3,6 @@ use Context::*; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; @@ -208,7 +207,7 @@ fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { }; // A `#[const_continue]` must break to a block in a `#[loop_match]`. - if find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::ConstContinue(_)) { + if find_attr!(self.tcx.hir_attrs(e.hir_id), ConstContinue(_)) { let Some(label) = break_destination.label else { let span = e.span; self.tcx.dcx().emit_fatal(ConstContinueBadLabel { span }); @@ -421,7 +420,7 @@ fn is_loop_match( e: &'hir hir::Expr<'hir>, body: &'hir hir::Block<'hir>, ) -> Option { - if !find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::LoopMatch(_)) { + if !find_attr!(self.tcx.hir_attrs(e.hir_id), LoopMatch(_)) { return None; } diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 746678e2865f..083e29bff04f 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -196,7 +196,7 @@ pub(crate) fn lookup_method( // NOTE: on the failure path, we also record the possibly-used trait methods // since an unused import warning is kinda distracting from the method error. - for &import_id in &pick.import_ids { + for &import_id in pick.import_ids { debug!("used_trait_import: {:?}", import_id); self.typeck_results.borrow_mut().used_trait_imports.insert(import_id); } @@ -554,7 +554,7 @@ pub(crate) fn resolve_fully_qualified_call( debug!(?pick); { let mut typeck_results = self.typeck_results.borrow_mut(); - for import_id in pick.import_ids { + for &import_id in pick.import_ids { debug!(used_trait_import=?import_id); typeck_results.used_trait_imports.insert(import_id); } 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 38413cca633c..d846f4433dc7 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -3,19 +3,157 @@ use hir::def_id::DefId; use hir::{HirId, ItemKind}; use rustc_ast::join_path_idents; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir as hir; use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; use rustc_middle::span_bug; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint::builtin::{RUST_2021_PRELUDE_COLLISIONS, RUST_2024_PRELUDE_COLLISIONS}; -use rustc_span::{Ident, STDLIB_STABLE_CRATES, Span, kw, sym}; +use rustc_span::{Ident, STDLIB_STABLE_CRATES, Span, Symbol, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; use crate::FnCtxt; use crate::method::probe::{self, Pick}; +struct AmbiguousTraitMethodCall<'a, 'b, 'tcx> { + segment_name: Symbol, + self_expr_span: Span, + pick: &'a Pick<'tcx>, + tcx: TyCtxt<'tcx>, + edition: &'b str, +} + +impl<'a, 'b, 'c, 'tcx> Diagnostic<'a, ()> for AmbiguousTraitMethodCall<'b, 'c, '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, + level, + format!("trait method `{}` will become ambiguous in Rust {edition}", segment_name), + ); + let derefs = "*".repeat(pick.autoderefs); + + let autoref = match pick.autoref_or_ptr_adjustment { + Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(), + Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", + Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl { + hir::Mutability::Mut => "Pin<&mut ", + hir::Mutability::Not => "Pin<&", + }, + }; + if let Ok(self_expr) = tcx.sess.source_map().span_to_snippet(self_expr_span) { + let mut self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = + pick.autoref_or_ptr_adjustment + { + format!("{derefs}{self_expr} as *const _") + } else { + format!("{autoref}{derefs}{self_expr}") + }; + + if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) = + pick.autoref_or_ptr_adjustment + { + self_adjusted.push('>'); + } + + lint.span_suggestion( + self_expr_span, + "disambiguate the method call", + format!("({self_adjusted})"), + Applicability::MachineApplicable, + ); + } else { + let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = + pick.autoref_or_ptr_adjustment + { + format!("{derefs}(...) as *const _") + } else { + format!("{autoref}{derefs}...") + }; + lint.span_help( + self_expr_span, + format!("disambiguate the method call with `({self_adjusted})`",), + ); + } + lint + } +} + +struct AmbiguousTraitMethod<'a, 'b, 'tcx, 'pcx, 'fnctx> { + segment: &'a hir::PathSegment<'pcx>, + call_expr: &'tcx hir::Expr<'tcx>, + self_expr: &'tcx hir::Expr<'tcx>, + pick: &'a Pick<'tcx>, + args: &'tcx [hir::Expr<'tcx>], + edition: &'b str, + span: Span, + this: &'a FnCtxt<'fnctx, 'tcx>, +} + +impl<'a, 'b, 'c, 'tcx, 'pcx, 'fnctx> Diagnostic<'a, ()> + for AmbiguousTraitMethod<'b, 'c, 'tcx, 'pcx, '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, + level, + format!( + "trait method `{}` will become ambiguous in Rust {edition}", + segment.ident.name + ), + ); + + let sp = call_expr.span; + let trait_name = + this.trait_path_or_bare_name(span, call_expr.hir_id, pick.item.container_id(this.tcx)); + + let (self_adjusted, precise) = this.adjust_expr(pick, self_expr, sp); + if precise { + let args = args.iter().fold(String::new(), |mut string, arg| { + let span = arg.span.find_ancestor_inside(sp).unwrap_or_default(); + write!(string, ", {}", this.sess().source_map().span_to_snippet(span).unwrap()) + .unwrap(); + string + }); + + lint.span_suggestion( + sp, + "disambiguate the associated function", + format!( + "{}::{}{}({}{})", + trait_name, + segment.ident.name, + if let Some(args) = segment.args.as_ref().and_then(|args| this + .sess() + .source_map() + .span_to_snippet(args.span_ext) + .ok()) + { + // Keep turbofish. + format!("::{args}") + } else { + String::new() + }, + self_adjusted, + args, + ), + Applicability::MachineApplicable, + ); + } else { + lint.span_help( + sp, + format!( + "disambiguate the associated function with `{}::{}(...)`", + trait_name, segment.ident, + ), + ); + } + lint + } +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(super) fn lint_edition_dependent_dot_call( &self, @@ -101,133 +239,34 @@ pub(super) fn lint_edition_dependent_dot_call( // Inherent impls only require not relying on autoref and autoderef in order to // ensure that the trait implementation won't be used - self.tcx.node_span_lint( + self.tcx.emit_node_span_lint( prelude_or_array_lint, self_expr.hir_id, self_expr.span, - |lint| { - lint.primary_message(format!( - "trait method `{}` will become ambiguous in Rust {edition}", - segment.ident.name - )); - - let sp = self_expr.span; - - let derefs = "*".repeat(pick.autoderefs); - - let autoref = match pick.autoref_or_ptr_adjustment { - Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => { - mutbl.ref_prefix_str() - } - Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", - Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl { - hir::Mutability::Mut => "Pin<&mut ", - hir::Mutability::Not => "Pin<&", - }, - }; - if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) - { - let mut self_adjusted = - if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = - pick.autoref_or_ptr_adjustment - { - format!("{derefs}{self_expr} as *const _") - } else { - format!("{autoref}{derefs}{self_expr}") - }; - - if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) = - pick.autoref_or_ptr_adjustment - { - self_adjusted.push('>'); - } - - lint.span_suggestion( - sp, - "disambiguate the method call", - format!("({self_adjusted})"), - Applicability::MachineApplicable, - ); - } else { - let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = - pick.autoref_or_ptr_adjustment - { - format!("{derefs}(...) as *const _") - } else { - format!("{autoref}{derefs}...") - }; - lint.span_help( - sp, - format!("disambiguate the method call with `({self_adjusted})`",), - ); - } + AmbiguousTraitMethodCall { + segment_name: segment.ident.name, + self_expr_span: self_expr.span, + pick, + tcx: self.tcx, + edition, }, ); } else { // trait implementations require full disambiguation to not clash with the new prelude // additions (i.e. convert from dot-call to fully-qualified call) - self.tcx.node_span_lint( + self.tcx.emit_node_span_lint( prelude_or_array_lint, call_expr.hir_id, call_expr.span, - |lint| { - lint.primary_message(format!( - "trait method `{}` will become ambiguous in Rust {edition}", - segment.ident.name - )); - - let sp = call_expr.span; - let trait_name = self.trait_path_or_bare_name( - span, - call_expr.hir_id, - pick.item.container_id(self.tcx), - ); - - let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp); - if precise { - let args = args.iter().fold(String::new(), |mut string, arg| { - let span = arg.span.find_ancestor_inside(sp).unwrap_or_default(); - write!( - string, - ", {}", - self.sess().source_map().span_to_snippet(span).unwrap() - ) - .unwrap(); - string - }); - - lint.span_suggestion( - sp, - "disambiguate the associated function", - format!( - "{}::{}{}({}{})", - trait_name, - segment.ident.name, - if let Some(args) = segment.args.as_ref().and_then(|args| self - .sess() - .source_map() - .span_to_snippet(args.span_ext) - .ok()) - { - // Keep turbofish. - format!("::{args}") - } else { - String::new() - }, - self_adjusted, - args, - ), - Applicability::MachineApplicable, - ); - } else { - lint.span_help( - sp, - format!( - "disambiguate the associated function with `{}::{}(...)`", - trait_name, segment.ident, - ), - ); - } + AmbiguousTraitMethod { + segment, + call_expr, + self_expr, + pick, + args, + edition, + span, + this: self, }, ); } @@ -242,6 +281,88 @@ pub(super) fn lint_fully_qualified_call_from_2018( expr_id: hir::HirId, pick: &Pick<'tcx>, ) { + struct AmbiguousTraitAssocFunc<'a, 'fnctx, 'tcx> { + method_name: Symbol, + this: &'a FnCtxt<'fnctx, 'tcx>, + pick: &'a Pick<'tcx>, + span: Span, + expr_id: hir::HirId, + self_ty_span: Span, + 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, ()> { + let Self { method_name, this, pick, span, expr_id, self_ty_span, self_ty } = self; + let mut lint = Diag::new( + dcx, + level, + format!( + "trait-associated function `{}` will become ambiguous in Rust 2021", + method_name + ), + ); + + // "type" refers to either a type or, more likely, a trait from which + // the associated function or method is from. + let container_id = pick.item.container_id(this.tcx); + let trait_path = this.trait_path_or_bare_name(span, expr_id, container_id); + let trait_generics = this.tcx.generics_of(container_id); + + let trait_name = + if trait_generics.own_params.len() <= trait_generics.has_self as usize { + trait_path + } else { + let counts = trait_generics.own_counts(); + format!( + "{}<{}>", + trait_path, + std::iter::repeat("'_") + .take(counts.lifetimes) + .chain(std::iter::repeat("_").take( + counts.types + counts.consts - trait_generics.has_self as usize + )) + .collect::>() + .join(", ") + ) + }; + + let mut self_ty_name = self_ty_span + .find_ancestor_inside(span) + .and_then(|span| this.sess().source_map().span_to_snippet(span).ok()) + .unwrap_or_else(|| self_ty.to_string()); + + // Get the number of generics the self type has (if an Adt) unless we can determine that + // the user has written the self type with generics already which we (naively) do by looking + // for a "<" in `self_ty_name`. + if !self_ty_name.contains('<') { + if let ty::Adt(def, _) = self_ty.kind() { + let generics = this.tcx.generics_of(def.did()); + if !generics.is_own_empty() { + let counts = generics.own_counts(); + self_ty_name += &format!( + "<{}>", + std::iter::repeat("'_") + .take(counts.lifetimes) + .chain( + std::iter::repeat("_").take(counts.types + counts.consts) + ) + .collect::>() + .join(", ") + ); + } + } + } + lint.span_suggestion( + span, + "disambiguate the associated function", + format!("<{} as {}>::{}", self_ty_name, trait_name, method_name), + Applicability::MachineApplicable, + ); + lint + } + } + // Rust 2021 and later is already using the new prelude if span.at_least_rust_2021() { return; @@ -278,67 +399,20 @@ pub(super) fn lint_fully_qualified_call_from_2018( return; } - self.tcx.node_span_lint(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| { - lint.primary_message(format!( - "trait-associated function `{}` will become ambiguous in Rust 2021", - method_name.name - )); - - // "type" refers to either a type or, more likely, a trait from which - // the associated function or method is from. - let container_id = pick.item.container_id(self.tcx); - let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); - let trait_generics = self.tcx.generics_of(container_id); - - let trait_name = - if trait_generics.own_params.len() <= trait_generics.has_self as usize { - trait_path - } else { - let counts = trait_generics.own_counts(); - format!( - "{}<{}>", - trait_path, - std::iter::repeat("'_") - .take(counts.lifetimes) - .chain(std::iter::repeat("_").take( - counts.types + counts.consts - trait_generics.has_self as usize - )) - .collect::>() - .join(", ") - ) - }; - - let mut self_ty_name = self_ty_span - .find_ancestor_inside(span) - .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) - .unwrap_or_else(|| self_ty.to_string()); - - // Get the number of generics the self type has (if an Adt) unless we can determine that - // the user has written the self type with generics already which we (naively) do by looking - // for a "<" in `self_ty_name`. - if !self_ty_name.contains('<') { - if let ty::Adt(def, _) = self_ty.kind() { - let generics = self.tcx.generics_of(def.did()); - if !generics.is_own_empty() { - let counts = generics.own_counts(); - self_ty_name += &format!( - "<{}>", - std::iter::repeat("'_") - .take(counts.lifetimes) - .chain(std::iter::repeat("_").take(counts.types + counts.consts)) - .collect::>() - .join(", ") - ); - } - } - } - lint.span_suggestion( + self.tcx.emit_node_span_lint( + RUST_2021_PRELUDE_COLLISIONS, + expr_id, + span, + AmbiguousTraitAssocFunc { + method_name: method_name.name, + this: self, + pick, span, - "disambiguate the associated function", - format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), - Applicability::MachineApplicable, - ); - }); + expr_id, + self_ty_span, + self_ty, + }, + ); } fn trait_path_or_bare_name( diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 2a92a4b48edc..4a11c5944af6 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1,18 +1,18 @@ use std::cell::{Cell, RefCell}; use std::cmp::max; +use std::debug_assert_matches; use std::ops::Deref; -use rustc_data_structures::debug_assert_matches; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; -use rustc_errors::Applicability; -use rustc_hir::attrs::AttributeKind; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir::def::DefKind; use rustc_hir::{self as hir, ExprKind, HirId, Node, find_attr}; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, query}; +use rustc_macros::Diagnostic; use rustc_middle::middle::stability; use rustc_middle::ty::elaborate::supertrait_def_ids; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}; @@ -36,7 +36,7 @@ CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult, }; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt}; -use smallvec::{SmallVec, smallvec}; +use smallvec::SmallVec; use tracing::{debug, instrument}; use self::CandidateKind::*; @@ -99,7 +99,7 @@ fn deref(&self) -> &Self::Target { pub(crate) struct Candidate<'tcx> { pub(crate) item: ty::AssocItem, pub(crate) kind: CandidateKind<'tcx>, - pub(crate) import_ids: SmallVec<[LocalDefId; 1]>, + pub(crate) import_ids: &'tcx [LocalDefId], } #[derive(Debug, Clone)] @@ -206,7 +206,7 @@ fn candidate_may_shadow(&self, candidate: &Candidate<'_>) -> bool { pub(crate) struct Pick<'tcx> { pub item: ty::AssocItem, pub kind: PickKind<'tcx>, - pub import_ids: SmallVec<[LocalDefId; 1]>, + pub import_ids: &'tcx [LocalDefId], /// Indicates that the source expression should be autoderef'd N times /// ```ignore (not-rust) @@ -390,6 +390,10 @@ pub(crate) fn probe_op( where OP: FnOnce(ProbeContext<'_, 'tcx>) -> Result>, { + #[derive(Diagnostic)] + #[diag("type annotations needed")] + struct MissingTypeAnnot; + let mut orig_values = OriginalQueryValues::default(); let predefined_opaques_in_body = if self.next_trait_solver() { self.tcx.mk_predefined_opaques_in_body_from_iter( @@ -472,13 +476,11 @@ pub(crate) fn probe_op( // this case used to be allowed by the compiler, // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) - self.tcx.node_span_lint( + self.tcx.emit_node_span_lint( lint::builtin::TYVAR_BEHIND_RAW_POINTER, scope_expr_id, span, - |lint| { - lint.primary_message("type annotations needed"); - }, + MissingTypeAnnot, ); } else { // Ended up encountering a type variable when doing autoderef, @@ -490,6 +492,7 @@ pub(crate) fn probe_op( .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); let ty = self.resolve_vars_if_possible(ty.value); let guar = match *ty.kind() { + _ if let Some(guar) = self.tainted_by_errors() => guar, ty::Infer(ty::TyVar(_)) => { // We want to get the variable name that the method // is being called on. If it is a method call. @@ -571,7 +574,7 @@ pub(crate) fn probe_op( ty::Binder::dummy(trait_ref), false, ), - import_ids: smallvec![], + import_ids: &[], }, false, ); @@ -890,7 +893,20 @@ fn assemble_probe( | ty::Tuple(..) => { self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps) } - _ => {} + ty::Alias(..) + | ty::Bound(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineClosure(..) + | ty::CoroutineWitness(..) + | ty::Dynamic(..) + | ty::Error(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Infer(..) + | ty::Pat(..) + | ty::Placeholder(..) + | ty::UnsafeBinder(..) => {} } } @@ -930,7 +946,7 @@ fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId, receiver_steps: u Candidate { item, kind: InherentImplCandidate { impl_def_id, receiver_steps }, - import_ids: smallvec![], + import_ids: &[], }, true, ); @@ -963,11 +979,7 @@ fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>) { traits::supertraits(self.tcx, trait_ref), |this, new_trait_ref, item| { this.push_candidate( - Candidate { - item, - kind: ObjectCandidate(new_trait_ref), - import_ids: smallvec![], - }, + Candidate { item, kind: ObjectCandidate(new_trait_ref), import_ids: &[] }, true, ); }, @@ -1002,11 +1014,7 @@ fn assemble_inherent_candidates_from_param(&mut self, param_ty: Ty<'tcx>) { self.assemble_candidates_for_bounds(bounds, |this, poly_trait_ref, item| { this.push_candidate( - Candidate { - item, - kind: WhereClauseCandidate(poly_trait_ref), - import_ids: smallvec![], - }, + Candidate { item, kind: WhereClauseCandidate(poly_trait_ref), import_ids: &[] }, true, ); }); @@ -1056,11 +1064,7 @@ fn assemble_extension_candidates_for_all_traits(&mut self) { let mut duplicates = FxHashSet::default(); for trait_info in suggest::all_traits(self.tcx) { if duplicates.insert(trait_info.def_id) { - self.assemble_extension_candidates_for_trait( - &smallvec![], - trait_info.def_id, - false, - ); + self.assemble_extension_candidates_for_trait(&[], trait_info.def_id, false); } } } @@ -1084,7 +1088,7 @@ fn matches_return_type(&self, method: ty::AssocItem, expected: Ty<'tcx>) -> bool #[instrument(level = "debug", skip(self))] fn assemble_extension_candidates_for_trait( &mut self, - import_ids: &SmallVec<[LocalDefId; 1]>, + import_ids: &'tcx [LocalDefId], trait_def_id: DefId, lint_ambiguous: bool, ) { @@ -1107,7 +1111,7 @@ fn assemble_extension_candidates_for_trait( self.push_candidate( Candidate { item, - import_ids: import_ids.clone(), + import_ids, kind: TraitCandidate(bound_trait_ref, lint_ambiguous), }, false, @@ -1130,7 +1134,7 @@ fn assemble_extension_candidates_for_trait( self.push_candidate( Candidate { item, - import_ids: import_ids.clone(), + import_ids, kind: TraitCandidate(ty::Binder::dummy(trait_ref), lint_ambiguous), }, false, @@ -1810,47 +1814,68 @@ pub(crate) fn maybe_emit_unstable_name_collision_hint( span: Span, scope_expr_id: HirId, ) { + struct ItemMaybeBeAddedToStd<'a, 'tcx> { + this: &'a Pick<'tcx>, + tcx: TyCtxt<'tcx>, + span: Span, + } + + 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( + dcx, + level, + format!( + "{} {} with this name may be added to the standard library in the future", + tcx.def_kind_descr_article(def_kind, this.item.def_id), + tcx.def_kind_descr(def_kind, this.item.def_id), + ), + ); + + match (this.item.kind, this.item.container) { + (ty::AssocKind::Fn { .. }, _) => { + // FIXME: This should be a `span_suggestion` instead of `help` + // However `this.span` only + // highlights the method name, so we can't use it. Also consider reusing + // the code from `report_method_error()`. + lint.help(format!( + "call with fully qualified syntax `{}(...)` to keep using the current \ + method", + tcx.def_path_str(this.item.def_id), + )); + } + (ty::AssocKind::Const { name, .. }, ty::AssocContainer::Trait) => { + let def_id = this.item.container_id(tcx); + lint.span_suggestion( + span, + "use the fully qualified path to the associated const", + format!("<{} as {}>::{}", this.self_ty, tcx.def_path_str(def_id), name), + Applicability::MachineApplicable, + ); + } + _ => {} + } + tcx.disabled_nightly_features( + &mut lint, + this.unstable_candidates.iter().map(|(candidate, feature)| { + (format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature) + }), + ); + lint + } + } + if self.unstable_candidates.is_empty() { return; } - let def_kind = self.item.as_def_kind(); - tcx.node_span_lint(lint::builtin::UNSTABLE_NAME_COLLISIONS, scope_expr_id, span, |lint| { - lint.primary_message(format!( - "{} {} with this name may be added to the standard library in the future", - tcx.def_kind_descr_article(def_kind, self.item.def_id), - tcx.def_kind_descr(def_kind, self.item.def_id), - )); - - match (self.item.kind, self.item.container) { - (ty::AssocKind::Fn { .. }, _) => { - // FIXME: This should be a `span_suggestion` instead of `help` - // However `self.span` only - // highlights the method name, so we can't use it. Also consider reusing - // the code from `report_method_error()`. - lint.help(format!( - "call with fully qualified syntax `{}(...)` to keep using the current \ - method", - tcx.def_path_str(self.item.def_id), - )); - } - (ty::AssocKind::Const { name }, ty::AssocContainer::Trait) => { - let def_id = self.item.container_id(tcx); - lint.span_suggestion( - span, - "use the fully qualified path to the associated const", - format!("<{} as {}>::{}", self.self_ty, tcx.def_path_str(def_id), name), - Applicability::MachineApplicable, - ); - } - _ => {} - } - tcx.disabled_nightly_features( - lint, - self.unstable_candidates.iter().map(|(candidate, feature)| { - (format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature) - }), - ); - }); + tcx.emit_node_span_lint( + lint::builtin::UNSTABLE_NAME_COLLISIONS, + scope_expr_id, + span, + ItemMaybeBeAddedToStd { this: self, tcx, span }, + ); } } @@ -2316,7 +2341,7 @@ fn collapse_candidates_to_trait_pick( Some(Pick { item: probes[0].0.item, kind: TraitPick(lint_ambiguous), - import_ids: probes[0].0.import_ids.clone(), + import_ids: probes[0].0.import_ids, autoderefs: 0, autoref_or_ptr_adjustment: None, self_ty, @@ -2393,7 +2418,7 @@ fn collapse_candidates_to_subtrait_pick( Some(Pick { item: child_candidate.item, kind: TraitPick(lint_ambiguous), - import_ids: child_candidate.import_ids.clone(), + import_ids: child_candidate.import_ids, autoderefs: 0, autoref_or_ptr_adjustment: None, self_ty, @@ -2577,7 +2602,7 @@ fn matches_by_doc_alias(&self, def_id: DefId) -> bool { let hir_id = self.fcx.tcx.local_def_id_to_hir_id(local_def_id); let attrs = self.fcx.tcx.hir_attrs(hir_id); - if let Some(d) = find_attr!(attrs, AttributeKind::Doc(d) => d) + if let Some(d) = find_attr!(attrs, Doc(d) => d) && d.aliases.contains_key(&method.name) { return true; @@ -2670,7 +2695,7 @@ fn to_unadjusted_pick( WhereClausePick(trait_ref) } }, - import_ids: self.import_ids.clone(), + import_ids: self.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 517d73f51783..1fe82bc7ff6b 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -16,7 +16,7 @@ use rustc_errors::{ Applicability, Diag, MultiSpan, StashKey, listify, pluralize, struct_span_code_err, }; -use rustc_hir::attrs::AttributeKind; +use rustc_hir::attrs::diagnostic::OnUnimplementedNote; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; @@ -38,7 +38,6 @@ kw, sym, }; use rustc_trait_selection::error_reporting::traits::DefIdOrName; -use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ @@ -200,7 +199,7 @@ pub(crate) fn report_method_error( // NOTE: Reporting a method error should also suppress any unused trait errors, // since the method error is very possibly the reason why the trait wasn't used. for &import_id in - self.tcx.in_scope_traits(call_id).into_iter().flatten().flat_map(|c| &c.import_ids) + self.tcx.in_scope_traits(call_id).into_iter().flatten().flat_map(|c| c.import_ids) { self.typeck_results.borrow_mut().used_trait_imports.insert(import_id); } @@ -854,7 +853,7 @@ fn suggest_unsatisfied_ty_or_trait( { err.span_label(span, format!("`{rcvr_ty}` is not an iterator")); if !span.in_external_macro(self.tcx.sess.source_map()) { - err.multipart_suggestion_verbose( + err.multipart_suggestion( "call `.into_iter()` first", vec![(span.shrink_to_lo(), format!("into_iter()."))], Applicability::MaybeIncorrect, @@ -1415,7 +1414,7 @@ fn set_not_found_span_label( }) .collect::>(); if !inherent_impls_candidate.is_empty() { - inherent_impls_candidate.sort_by_key(|id| self.tcx.def_path_str(id)); + inherent_impls_candidate.sort_by_key(|&id| self.tcx.def_path_str(id)); inherent_impls_candidate.dedup(); // number of types to show at most @@ -1581,7 +1580,7 @@ fn suggest_enum_variant_for_method_call( } } } - err.multipart_suggestion_verbose( + err.multipart_suggestion( "there is a variant with a similar name", suggestion, Applicability::HasPlaceholders, @@ -2257,11 +2256,11 @@ pub(crate) fn confusable_method_name( call_args: Option>>, ) -> Option { if let ty::Adt(adt, adt_args) = rcvr_ty.kind() { - for inherent_impl_did in self.tcx.inherent_impls(adt.did()).into_iter() { + for &inherent_impl_did in self.tcx.inherent_impls(adt.did()).into_iter() { for inherent_method in self.tcx.associated_items(inherent_impl_did).in_definition_order() { - if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::RustcConfusables{symbols, ..} => symbols) + if let Some(candidates) = find_attr!(self.tcx, inherent_method.def_id, RustcConfusables{symbols, ..} => symbols) && candidates.contains(&item_name.name) && inherent_method.is_fn() { @@ -2316,7 +2315,7 @@ fn note_candidates_on_method_error( sources: &mut Vec, sugg_span: Option, ) { - sources.sort_by_key(|source| match source { + sources.sort_by_key(|source| match *source { CandidateSource::Trait(id) => (0, self.tcx.def_path_str(id)), CandidateSource::Impl(id) => (1, self.tcx.def_path_str(id)), }); @@ -2469,7 +2468,7 @@ fn find_builder_fn(&self, err: &mut Diag<'_>, rcvr_ty: Ty<'tcx>, expr_id: hir::H .tcx .inherent_impls(adt_def.did()) .iter() - .flat_map(|i| self.tcx.associated_items(i).in_definition_order()) + .flat_map(|&i| self.tcx.associated_items(i).in_definition_order()) // Only assoc fn with no receivers and only if // they are resolvable .filter(|item| { @@ -2522,7 +2521,7 @@ fn find_builder_fn(&self, err: &mut Diag<'_>, rcvr_ty: Ty<'tcx>, expr_id: hir::H } else { String::new() }; - match &items[..] { + match items[..] { [] => {} [(def_id, ret_ty)] => { err.span_note( @@ -2537,7 +2536,7 @@ fn find_builder_fn(&self, err: &mut Diag<'_>, rcvr_ty: Ty<'tcx>, expr_id: hir::H _ => { let span: MultiSpan = items .iter() - .map(|(def_id, _)| self.tcx.def_span(def_id)) + .map(|&(def_id, _)| self.tcx.def_span(def_id)) .collect::>() .into(); err.span_note( @@ -2547,7 +2546,7 @@ fn find_builder_fn(&self, err: &mut Diag<'_>, rcvr_ty: Ty<'tcx>, expr_id: hir::H following associated functions:\n{}{post}", items .iter() - .map(|(def_id, _ret_ty)| self.tcx.def_path_str(def_id)) + .map(|&(def_id, _ret_ty)| self.tcx.def_path_str(def_id)) .collect::>() .join("\n") ), @@ -3519,7 +3518,7 @@ fn note_predicate_source_and_get_derives( traits.push(trait_pred.def_id()); } } - traits.sort_by_key(|id| self.tcx.def_path_str(id)); + traits.sort_by_key(|&id| self.tcx.def_path_str(id)); traits.dedup(); let len = traits.len(); @@ -3777,8 +3776,9 @@ fn set_label_for_method_error( "method `poll` found on `Pin<&mut {ty_str}>`, \ see documentation for `std::pin::Pin`" )); - err.help("self type must be pinned to call `Future::poll`, \ - see https://rust-lang.github.io/async-book/04_pinning/01_chapter.html#pinning-in-practice" + err.help( + "self type must be pinned to call `Future::poll`, \ + see https://rust-lang.github.io/async-book/part-reference/pinning.html", ); } @@ -3921,7 +3921,7 @@ fn suggest_valid_traits( valid_out_of_scope_traits.retain(|id| self.tcx.is_user_visible_dep(id.krate)); if !valid_out_of_scope_traits.is_empty() { let mut candidates = valid_out_of_scope_traits; - candidates.sort_by_key(|id| self.tcx.def_path_str(id)); + candidates.sort_by_key(|&id| self.tcx.def_path_str(id)); candidates.dedup(); // `TryFrom` and `FromIterator` have no methods diff --git a/compiler/rustc_hir_typeck/src/naked_functions.rs b/compiler/rustc_hir_typeck/src/naked_functions.rs index d3fd63d871aa..c118f49dca6d 100644 --- a/compiler/rustc_hir_typeck/src/naked_functions.rs +++ b/compiler/rustc_hir_typeck/src/naked_functions.rs @@ -1,7 +1,6 @@ //! Checks validity of naked functions. use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, HirIdSet, StmtKind, find_attr}; @@ -21,7 +20,7 @@ pub(crate) fn typeck_naked_fn<'tcx>( def_id: LocalDefId, body: &'tcx hir::Body<'tcx>, ) { - debug_assert!(find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Naked(..))); + debug_assert!(find_attr!(tcx, def_id, Naked(..))); check_no_patterns(tcx, body.params); check_no_parameters_use(tcx, body); check_asm(tcx, def_id, body); diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index a175b3557c47..4b325a7ad14a 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -1,8 +1,10 @@ //! Code related to processing overloaded binary and unary operators. +use rustc_ast as ast; use rustc_data_structures::packed::Pu128; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, struct_span_code_err}; +use rustc_hir as hir; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::bug; use rustc_middle::ty::adjustment::{ @@ -16,7 +18,6 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt}; use tracing::debug; -use {rustc_ast as ast, rustc_hir as hir}; use super::FnCtxt; use super::method::MethodCallee; @@ -380,8 +381,8 @@ fn check_overloaded_binop( self.tcx .associated_item_def_ids(def_id) .iter() - .find(|item_def_id| { - self.tcx.associated_item(*item_def_id).name() == sym::Output + .find(|&&item_def_id| { + self.tcx.associated_item(item_def_id).name() == sym::Output }) .cloned() }); @@ -465,7 +466,7 @@ fn check_overloaded_binop( && lhs_new_mutbl.is_not() && rhs_new_mutbl.is_not() { - err.multipart_suggestion_verbose( + err.multipart_suggestion( "consider reborrowing both sides", vec![ (lhs_expr.span.shrink_to_lo(), "&*".to_string()), @@ -826,7 +827,7 @@ fn check_str_addition( lhs_sugg, (rhs_expr.span.shrink_to_lo(), "&".to_owned()), ]; - err.multipart_suggestion_verbose( + err.multipart_suggestion( sugg_msg, suggestions, Applicability::MachineApplicable, diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 13f87c092352..7b5f5f3f520e 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1,13 +1,13 @@ -use std::cmp; use std::collections::hash_map::Entry::{Occupied, Vacant}; +use std::{assert_matches, cmp}; use rustc_abi::FieldIdx; use rustc_ast as ast; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err, + Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, Level, MultiSpan, pluralize, + struct_span_code_err, }; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -312,7 +312,7 @@ enum ResolvedPatKind<'tcx> { impl<'tcx> ResolvedPat<'tcx> { fn adjust_mode(&self) -> AdjustMode { if let ResolvedPatKind::Path { res, .. } = self.kind - && matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _)) + && matches!(res, Res::Def(DefKind::Const { .. } | DefKind::AssocConst { .. }, _)) { // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. // Peeling the reference types too early will cause type checking failures. @@ -1410,7 +1410,7 @@ fn borrow_pat_suggestion(&self, err: &mut Diag<'_>, pat: &Pat<'_>) { // Check that there is explicit type (ie this is not a closure param with inferred type) // so we don't suggest moving something to the type that does not exist hir::Node::Param(hir::Param { ty_span, pat, .. }) if pat.span != *ty_span => { - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!("to take parameter `{binding}` by reference, move `&{pin_and_mut}` to the type"), vec![ (pat.span.until(inner.span), "".to_owned()), @@ -1567,8 +1567,8 @@ fn resolve_pat_path( } Res::Def( DefKind::Ctor(_, CtorKind::Const) - | DefKind::Const - | DefKind::AssocConst + | DefKind::Const { .. } + | DefKind::AssocConst { .. } | DefKind::ConstParam, _, ) => {} // OK @@ -1660,7 +1660,9 @@ fn emit_bad_pat_path( _ => { let (type_def_id, item_def_id) = match resolved_pat.ty.kind() { ty::Adt(def, _) => match res { - Res::Def(DefKind::Const, def_id) => (Some(def.did()), Some(def_id)), + Res::Def(DefKind::Const { .. }, def_id) => { + (Some(def.did()), Some(def_id)) + } _ => (None, None), }, _ => (None, None), @@ -1733,7 +1735,7 @@ fn resolve_pat_tuple_struct( Res::Err => { self.dcx().span_bug(pat.span, "`Res::Err` but no error emitted"); } - Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { + Res::Def(DefKind::AssocConst { .. } | DefKind::AssocFn, _) => { return report_unexpected_res(res); } Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res), @@ -2434,10 +2436,20 @@ fn error_no_accessible_fields( .struct_span_err(pat.span, "pattern requires `..` due to inaccessible fields"); if let Some(field) = fields.last() { + let tail_span = field.span.shrink_to_hi().to(pat.span.shrink_to_hi()); + let comma_hi_offset = + self.tcx.sess.source_map().span_to_snippet(tail_span).ok().and_then(|snippet| { + let trimmed = snippet.trim_start(); + trimmed.starts_with(',').then(|| (snippet.len() - trimmed.len() + 1) as u32) + }); err.span_suggestion_verbose( - field.span.shrink_to_hi(), + if let Some(comma_hi_offset) = comma_hi_offset { + tail_span.with_hi(tail_span.lo() + BytePos(comma_hi_offset)).shrink_to_hi() + } else { + field.span.shrink_to_hi() + }, "ignore the inaccessible and unused fields", - ", ..", + if comma_hi_offset.is_some() { " .." } else { ", .." }, Applicability::MachineApplicable, ); } else { @@ -2469,6 +2481,27 @@ fn lint_non_exhaustive_omitted_patterns( unmentioned_fields: &[(&ty::FieldDef, Ident)], ty: Ty<'tcx>, ) { + struct FieldsNotListed<'a, 'b, 'tcx> { + pat_span: Span, + unmentioned_fields: &'a [(&'b ty::FieldDef, Ident)], + joined_patterns: String, + 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, ()> { + 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)) + .with_help( + "ensure that all fields are mentioned explicitly by adding the suggested fields", + ) + .with_note(format!( + "the pattern is of type `{ty}` and the `non_exhaustive_omitted_patterns` attribute was found", + )) + } + } + fn joined_uncovered_patterns(witnesses: &[&Ident]) -> String { const LIMIT: usize = 3; match witnesses { @@ -2493,16 +2526,12 @@ fn joined_uncovered_patterns(witnesses: &[&Ident]) -> String { &unmentioned_fields.iter().map(|(_, i)| i).collect::>(), ); - self.tcx.node_span_lint(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, |lint| { - lint.primary_message("some fields are not explicitly listed"); - lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns)); - lint.help( - "ensure that all fields are mentioned explicitly by adding the suggested fields", - ); - lint.note(format!( - "the pattern is of type `{ty}` and the `non_exhaustive_omitted_patterns` attribute was found", - )); - }); + self.tcx.emit_node_span_lint( + NON_EXHAUSTIVE_OMITTED_PATTERNS, + pat.hir_id, + pat.span, + FieldsNotListed { pat_span: pat.span, unmentioned_fields, joined_patterns, ty }, + ); } /// Returns a diagnostic reporting a struct pattern which does not mention some fields. diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index c33f8bdaffe2..b3ac237adf95 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -1,4 +1,6 @@ +use rustc_ast as ast; use rustc_errors::Applicability; +use rustc_hir as hir; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::InferOk; use rustc_infer::traits::{Obligation, ObligationCauseCode}; @@ -10,7 +12,6 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; use tracing::debug; -use {rustc_ast as ast, rustc_hir as hir}; use crate::method::{MethodCallee, TreatNotYetDefinedOpaques}; use crate::{FnCtxt, PlaceOp}; diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 767913ba5261..df02974d2fb2 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -35,8 +35,7 @@ use rustc_abi::FIRST_VARIANT; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::{ExtendUnord, UnordSet}; -use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::attrs::AttributeKind; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir, HirId, find_attr}; @@ -50,7 +49,9 @@ use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_span::{BytePos, Pos, Span, Symbol, sym}; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt as _; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::solve; use tracing::{debug, instrument}; use super::FnCtxt; @@ -197,17 +198,17 @@ fn analyze_closure( let closure_def_id = closure_def_id.expect_local(); assert_eq!(self.tcx.hir_body_owner_def_id(body.id()), closure_def_id); + + let closure_fcx = FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id); + let mut delegate = InferBorrowKind { + fcx: &closure_fcx, closure_def_id, capture_information: Default::default(), fake_reads: Default::default(), }; - let _ = euv::ExprUseVisitor::new( - &FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id), - &mut delegate, - ) - .consume_body(body); + let _ = euv::ExprUseVisitor::new(&closure_fcx, &mut delegate).consume_body(body); // There are several curious situations with coroutine-closures where // analysis is too aggressive with borrows when the coroutine-closure is @@ -287,7 +288,7 @@ fn analyze_closure( let hir::def::Res::Local(local_id) = path.res else { bug!(); }; - let place = self.place_for_root_variable(closure_def_id, local_id); + let place = closure_fcx.place_for_root_variable(closure_def_id, local_id); delegate.capture_information.push(( place, ty::CaptureInfo { @@ -326,7 +327,7 @@ fn analyze_closure( if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { for var_hir_id in upvars.keys() { - let place = self.place_for_root_variable(closure_def_id, *var_hir_id); + let place = closure_fcx.place_for_root_variable(closure_def_id, *var_hir_id); debug!("seed place {:?}", place); @@ -560,17 +561,17 @@ fn coroutine_body_consumes_upvars( bug!(); }; + let coroutine_fcx = + FnCtxt::new(self, self.tcx.param_env(coroutine_def_id), coroutine_def_id); + let mut delegate = InferBorrowKind { + fcx: &coroutine_fcx, closure_def_id: coroutine_def_id, capture_information: Default::default(), fake_reads: Default::default(), }; - let _ = euv::ExprUseVisitor::new( - &FnCtxt::new(self, self.tcx.param_env(coroutine_def_id), coroutine_def_id), - &mut delegate, - ) - .consume_expr(body); + let _ = euv::ExprUseVisitor::new(&coroutine_fcx, &mut delegate).consume_expr(body); let (_, kind, _) = self.process_collected_capture_information( hir::CaptureBy::Ref, @@ -963,6 +964,198 @@ fn perform_2229_migration_analysis( capture_clause: hir::CaptureBy, span: Span, ) { + struct MigrationLint<'a, 'b, 'tcx> { + closure_def_id: LocalDefId, + this: &'a FnCtxt<'b, 'tcx>, + body_id: hir::BodyId, + need_migrations: Vec, + migration_message: String, + } + + impl<'a, 'b, 'c, 'tcx> Diagnostic<'a, ()> for MigrationLint<'b, 'c, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { closure_def_id, this, body_id, need_migrations, migration_message } = + self; + let mut lint = Diag::new(dcx, level, migration_message); + + let (migration_string, migrated_variables_concat) = + migration_suggestion_for_2229(this.tcx, &need_migrations); + + let closure_hir_id = this.tcx.local_def_id_to_hir_id(closure_def_id); + let closure_head_span = this.tcx.def_span(closure_def_id); + + for NeededMigration { var_hir_id, diagnostics_info } in &need_migrations { + // Labels all the usage of the captured variable and why they are responsible + // for migration being needed + for lint_note in diagnostics_info.iter() { + match &lint_note.captures_info { + UpvarMigrationInfo::CapturingPrecise { + source_expr: Some(capture_expr_id), + var_name: captured_name, + } => { + let cause_span = this.tcx.hir_span(*capture_expr_id); + lint.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`", + this.tcx.hir_name(*var_hir_id), + captured_name, + )); + } + UpvarMigrationInfo::CapturingNothing { use_span } => { + lint.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", + this.tcx.hir_name(*var_hir_id), + )); + } + + _ => {} + } + + // Add a label pointing to where a captured variable affected by drop order + // is dropped + if lint_note.reason.drop_order { + let drop_location_span = drop_location_span(this.tcx, closure_hir_id); + + match &lint_note.captures_info { + UpvarMigrationInfo::CapturingPrecise { + var_name: captured_name, + .. + } => { + lint.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", + this.tcx.hir_name(*var_hir_id), + captured_name, + )); + } + UpvarMigrationInfo::CapturingNothing { use_span: _ } => { + lint.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", + v = this.tcx.hir_name(*var_hir_id), + )); + } + } + } + + // Add a label explaining why a closure no longer implements a trait + for &missing_trait in &lint_note.reason.auto_traits { + // not capturing something anymore cannot cause a trait to fail to be implemented: + match &lint_note.captures_info { + UpvarMigrationInfo::CapturingPrecise { + var_name: captured_name, + .. + } => { + let var_name = this.tcx.hir_name(*var_hir_id); + lint.span_label( + closure_head_span, + format!( + "\ + in Rust 2018, this closure implements {missing_trait} \ + as `{var_name}` implements {missing_trait}, but in Rust 2021, \ + this closure will no longer implement {missing_trait} \ + because `{var_name}` is not fully captured \ + and `{captured_name}` does not implement {missing_trait}" + ), + ); + } + + // Cannot happen: if we don't capture a variable, we impl strictly more traits + UpvarMigrationInfo::CapturingNothing { use_span } => span_bug!( + *use_span, + "missing trait from not capturing something" + ), + } + } + } + } + + let diagnostic_msg = format!( + "add a dummy let to cause {migrated_variables_concat} to be fully captured" + ); + + let closure_span = this.tcx.hir_span_with_body(closure_hir_id); + let mut closure_body_span = { + // If the body was entirely expanded from a macro + // invocation, i.e. the body is not contained inside the + // closure span, then we walk up the expansion until we + // find the span before the expansion. + let s = this.tcx.hir_span_with_body(body_id.hir_id); + s.find_ancestor_inside(closure_span).unwrap_or(s) + }; + + if let Ok(mut s) = this.tcx.sess.source_map().span_to_snippet(closure_body_span) { + if s.starts_with('$') { + // Looks like a macro fragment. Try to find the real block. + if let hir::Node::Expr(&hir::Expr { + kind: hir::ExprKind::Block(block, ..), + .. + }) = this.tcx.hir_node(body_id.hir_id) + { + // If the body is a block (with `{..}`), we use the span of that block. + // E.g. with a `|| $body` expanded from a `m!({ .. })`, we use `{ .. }`, and not `$body`. + // Since we know it's a block, we know we can insert the `let _ = ..` without + // breaking the macro syntax. + if let Ok(snippet) = + this.tcx.sess.source_map().span_to_snippet(block.span) + { + closure_body_span = block.span; + s = snippet; + } + } + } + + let mut lines = s.lines(); + let line1 = lines.next().unwrap_or_default(); + + if line1.trim_end() == "{" { + // This is a multi-line closure with just a `{` on the first line, + // so we put the `let` on its own line. + // We take the indentation from the next non-empty line. + let line2 = lines.find(|line| !line.is_empty()).unwrap_or_default(); + let indent = + line2.split_once(|c: char| !c.is_whitespace()).unwrap_or_default().0; + lint.span_suggestion( + closure_body_span + .with_lo(closure_body_span.lo() + BytePos::from_usize(line1.len())) + .shrink_to_lo(), + diagnostic_msg, + format!("\n{indent}{migration_string};"), + Applicability::MachineApplicable, + ); + } else if line1.starts_with('{') { + // This is a closure with its body wrapped in + // braces, but with more than just the opening + // brace on the first line. We put the `let` + // directly after the `{`. + lint.span_suggestion( + closure_body_span + .with_lo(closure_body_span.lo() + BytePos(1)) + .shrink_to_lo(), + diagnostic_msg, + format!(" {migration_string};"), + Applicability::MachineApplicable, + ); + } else { + // This is a closure without braces around the body. + // We add braces to add the `let` before the body. + lint.multipart_suggestion( + diagnostic_msg, + vec![ + ( + closure_body_span.shrink_to_lo(), + format!("{{ {migration_string}; "), + ), + (closure_body_span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MachineApplicable, + ); + } + } else { + lint.span_suggestion( + closure_span, + diagnostic_msg, + migration_string, + Applicability::HasPlaceholders, + ); + } + lint + } + } + let (need_migrations, reasons) = self.compute_2229_migrations( closure_def_id, span, @@ -971,161 +1164,59 @@ fn perform_2229_migration_analysis( ); if !need_migrations.is_empty() { - let (migration_string, migrated_variables_concat) = - migration_suggestion_for_2229(self.tcx, &need_migrations); - - let closure_hir_id = self.tcx.local_def_id_to_hir_id(closure_def_id); - let closure_head_span = self.tcx.def_span(closure_def_id); - self.tcx.node_span_lint( + self.tcx.emit_node_span_lint( lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, - closure_hir_id, - closure_head_span, - |lint| { - lint.primary_message(reasons.migration_message()); - - for NeededMigration { var_hir_id, diagnostics_info } in &need_migrations { - // Labels all the usage of the captured variable and why they are responsible - // for migration being needed - for lint_note in diagnostics_info.iter() { - match &lint_note.captures_info { - UpvarMigrationInfo::CapturingPrecise { source_expr: Some(capture_expr_id), var_name: captured_name } => { - let cause_span = self.tcx.hir_span(*capture_expr_id); - lint.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`", - self.tcx.hir_name(*var_hir_id), - captured_name, - )); - } - UpvarMigrationInfo::CapturingNothing { use_span } => { - lint.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", - self.tcx.hir_name(*var_hir_id), - )); - } - - _ => { } - } - - // Add a label pointing to where a captured variable affected by drop order - // is dropped - if lint_note.reason.drop_order { - let drop_location_span = drop_location_span(self.tcx, closure_hir_id); - - match &lint_note.captures_info { - UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { - lint.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", - self.tcx.hir_name(*var_hir_id), - captured_name, - )); - } - UpvarMigrationInfo::CapturingNothing { use_span: _ } => { - lint.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", - v = self.tcx.hir_name(*var_hir_id), - )); - } - } - } - - // Add a label explaining why a closure no longer implements a trait - for &missing_trait in &lint_note.reason.auto_traits { - // not capturing something anymore cannot cause a trait to fail to be implemented: - match &lint_note.captures_info { - UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { - let var_name = self.tcx.hir_name(*var_hir_id); - lint.span_label(closure_head_span, format!("\ - in Rust 2018, this closure implements {missing_trait} \ - as `{var_name}` implements {missing_trait}, but in Rust 2021, \ - this closure will no longer implement {missing_trait} \ - because `{var_name}` is not fully captured \ - and `{captured_name}` does not implement {missing_trait}")); - } - - // Cannot happen: if we don't capture a variable, we impl strictly more traits - UpvarMigrationInfo::CapturingNothing { use_span } => span_bug!(*use_span, "missing trait from not capturing something"), - } - } - } - } - - let diagnostic_msg = format!( - "add a dummy let to cause {migrated_variables_concat} to be fully captured" - ); - - let closure_span = self.tcx.hir_span_with_body(closure_hir_id); - let mut closure_body_span = { - // If the body was entirely expanded from a macro - // invocation, i.e. the body is not contained inside the - // closure span, then we walk up the expansion until we - // find the span before the expansion. - let s = self.tcx.hir_span_with_body(body_id.hir_id); - s.find_ancestor_inside(closure_span).unwrap_or(s) - }; - - if let Ok(mut s) = self.tcx.sess.source_map().span_to_snippet(closure_body_span) { - if s.starts_with('$') { - // Looks like a macro fragment. Try to find the real block. - if let hir::Node::Expr(&hir::Expr { - kind: hir::ExprKind::Block(block, ..), .. - }) = self.tcx.hir_node(body_id.hir_id) { - // If the body is a block (with `{..}`), we use the span of that block. - // E.g. with a `|| $body` expanded from a `m!({ .. })`, we use `{ .. }`, and not `$body`. - // Since we know it's a block, we know we can insert the `let _ = ..` without - // breaking the macro syntax. - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(block.span) { - closure_body_span = block.span; - s = snippet; - } - } - } - - let mut lines = s.lines(); - let line1 = lines.next().unwrap_or_default(); - - if line1.trim_end() == "{" { - // This is a multi-line closure with just a `{` on the first line, - // so we put the `let` on its own line. - // We take the indentation from the next non-empty line. - let line2 = lines.find(|line| !line.is_empty()).unwrap_or_default(); - let indent = line2.split_once(|c: char| !c.is_whitespace()).unwrap_or_default().0; - lint.span_suggestion( - closure_body_span.with_lo(closure_body_span.lo() + BytePos::from_usize(line1.len())).shrink_to_lo(), - diagnostic_msg, - format!("\n{indent}{migration_string};"), - Applicability::MachineApplicable, - ); - } else if line1.starts_with('{') { - // This is a closure with its body wrapped in - // braces, but with more than just the opening - // brace on the first line. We put the `let` - // directly after the `{`. - lint.span_suggestion( - closure_body_span.with_lo(closure_body_span.lo() + BytePos(1)).shrink_to_lo(), - diagnostic_msg, - format!(" {migration_string};"), - Applicability::MachineApplicable, - ); - } else { - // This is a closure without braces around the body. - // We add braces to add the `let` before the body. - lint.multipart_suggestion( - diagnostic_msg, - vec![ - (closure_body_span.shrink_to_lo(), format!("{{ {migration_string}; ")), - (closure_body_span.shrink_to_hi(), " }".to_string()), - ], - Applicability::MachineApplicable - ); - } - } else { - lint.span_suggestion( - closure_span, - diagnostic_msg, - migration_string, - Applicability::HasPlaceholders - ); - } + self.tcx.local_def_id_to_hir_id(closure_def_id), + self.tcx.def_span(closure_def_id), + MigrationLint { + this: self, + migration_message: reasons.migration_message(), + closure_def_id, + body_id, + need_migrations, }, ); } } + fn normalize_capture_place(&self, span: Span, place: Place<'tcx>) -> Place<'tcx> { + let mut place = self.resolve_vars_if_possible(place); + + // In the new solver, types in HIR `Place`s can contain unnormalized aliases, + // which can ICE later (e.g. when projecting fields for diagnostics). + if self.next_trait_solver() { + let cause = self.misc(span); + let at = self.at(&cause, self.param_env); + match solve::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + at, + place.clone(), + vec![], + ) { + Ok((normalized, goals)) => { + if !goals.is_empty() { + let mut typeck_results = self.typeck_results.borrow_mut(); + typeck_results.coroutine_stalled_predicates.extend( + goals + .into_iter() + // FIXME: throwing away the param-env :( + .map(|goal| (goal.predicate, self.misc(span))), + ); + } + normalized + } + Err(errors) => { + let guar = self.infcx.err_ctxt().report_fulfillment_errors(errors); + place.base_ty = Ty::new_error(self.tcx, guar); + for proj in &mut place.projections { + proj.ty = Ty::new_error(self.tcx, guar); + } + place + } + } + } else { + // For the old solver we can rely on `normalize` to eagerly normalize aliases. + self.normalize(span, place) + } + } /// Combines all the reasons for 2229 migrations fn compute_2229_migrations_reasons( @@ -1735,19 +1826,19 @@ fn place_for_root_variable( ) -> Place<'tcx> { let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id); - Place { + let place = Place { base_ty: self.node_ty(var_hir_id), base: PlaceBase::Upvar(upvar_id), projections: Default::default(), - } + }; + + // Normalize eagerly when inserting into `capture_information`, so all downstream + // capture analysis can assume a normalized `Place`. + self.normalize_capture_place(self.tcx.hir_span(var_hir_id), place) } fn should_log_capture_analysis(&self, closure_def_id: LocalDefId) -> bool { - self.has_rustc_attrs - && find_attr!( - self.tcx.get_all_attrs(closure_def_id), - AttributeKind::RustcCaptureAnalysis - ) + self.has_rustc_attrs && find_attr!(self.tcx, closure_def_id, RustcCaptureAnalysis) } fn log_capture_analysis_first_pass( @@ -1999,7 +2090,8 @@ fn drop_location_span(tcx: TyCtxt<'_>, hir_id: HirId) -> Span { tcx.sess.source_map().end_point(owner_span) } -struct InferBorrowKind<'tcx> { +struct InferBorrowKind<'fcx, 'a, 'tcx> { + fcx: &'fcx FnCtxt<'a, 'tcx>, // The def-id of the closure whose kind and upvar accesses are being inferred. closure_def_id: LocalDefId, @@ -2033,7 +2125,7 @@ struct InferBorrowKind<'tcx> { fake_reads: Vec<(Place<'tcx>, FakeReadCause, HirId)>, } -impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { +impl<'fcx, 'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'fcx, 'a, 'tcx> { #[instrument(skip(self), level = "debug")] fn fake_read( &mut self, @@ -2047,8 +2139,10 @@ fn fake_read( // such as deref of a raw pointer. let dummy_capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::Immutable); - let (place, _) = - restrict_capture_precision(place_with_id.place.clone(), dummy_capture_kind); + let span = self.fcx.tcx.hir_span(diag_expr_id); + let place = self.fcx.normalize_capture_place(span, place_with_id.place.clone()); + + let (place, _) = restrict_capture_precision(place, dummy_capture_kind); let (place, _) = restrict_repr_packed_field_ref_capture(place, dummy_capture_kind); self.fake_reads.push((place, cause, diag_expr_id)); @@ -2059,8 +2153,11 @@ fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; assert_eq!(self.closure_def_id, upvar_id.closure_expr_id); + let span = self.fcx.tcx.hir_span(diag_expr_id); + let place = self.fcx.normalize_capture_place(span, place_with_id.place.clone()); + self.capture_information.push(( - place_with_id.place.clone(), + place, ty::CaptureInfo { capture_kind_expr_id: Some(diag_expr_id), path_expr_id: Some(diag_expr_id), @@ -2074,8 +2171,11 @@ fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: Hir let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; assert_eq!(self.closure_def_id, upvar_id.closure_expr_id); + let span = self.fcx.tcx.hir_span(diag_expr_id); + let place = self.fcx.normalize_capture_place(span, place_with_id.place.clone()); + self.capture_information.push(( - place_with_id.place.clone(), + place, ty::CaptureInfo { capture_kind_expr_id: Some(diag_expr_id), path_expr_id: Some(diag_expr_id), @@ -2097,14 +2197,16 @@ fn borrow( // The region here will get discarded/ignored let capture_kind = ty::UpvarCapture::ByRef(bk); + let span = self.fcx.tcx.hir_span(diag_expr_id); + let place = self.fcx.normalize_capture_place(span, place_with_id.place.clone()); + // We only want repr packed restriction to be applied to reading references into a packed // struct, and not when the data is being moved. Therefore we call this method here instead // of in `restrict_capture_precision`. - let (place, mut capture_kind) = - restrict_repr_packed_field_ref_capture(place_with_id.place.clone(), capture_kind); + let (place, mut capture_kind) = restrict_repr_packed_field_ref_capture(place, capture_kind); // Raw pointers don't inherit mutability - if place_with_id.place.deref_tys().any(Ty::is_raw_ptr) { + if place.deref_tys().any(Ty::is_raw_ptr) { capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::Immutable); } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 0078cd9d0683..afb20b3c7cd0 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -14,7 +14,6 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::ExtendUnord; use rustc_errors::{E0720, ErrorGuaranteed}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, InferKind, Visitor}; use rustc_hir::{self as hir, AmbigArg, HirId, find_attr}; @@ -46,8 +45,8 @@ pub(crate) fn resolve_type_vars_in_body( // This attribute causes us to dump some writeback information // in the form of errors, which is used for unit tests. - let rustc_dump_user_args = self.has_rustc_attrs - && find_attr!(self.tcx.get_all_attrs(item_def_id), AttributeKind::RustcDumpUserArgs); + let rustc_dump_user_args = + self.has_rustc_attrs && find_attr!(self.tcx, item_def_id, RustcDumpUserArgs); let mut wbcx = WritebackCx::new(self, body, rustc_dump_user_args); for param in body.params { diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 3447836598bf..61ddd2ca566b 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -39,19 +39,18 @@ 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::bug; -use rustc_middle::dep_graph::{ - DepGraphQuery, DepKind, DepNode, DepNodeExt, DepNodeFilter, EdgeFilter, dep_kinds, -}; +use rustc_middle::dep_graph::{DepKind, DepNode, DepNodeFilter, EdgeFilter, RetainedDepGraph}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::{Span, Symbol, sym}; use tracing::debug; -use {rustc_graphviz as dot, rustc_hir as hir}; use crate::errors; @@ -59,7 +58,7 @@ pub(crate) fn assert_dep_graph(tcx: TyCtxt<'_>) { tcx.dep_graph.with_ignore(|| { if tcx.sess.opts.unstable_opts.dump_dep_graph { - tcx.dep_graph.with_query(dump_graph); + tcx.dep_graph.with_retained_dep_graph(dump_graph); } if !tcx.sess.opts.unstable_opts.query_dep_graph { @@ -117,7 +116,7 @@ fn process_attrs(&mut self, def_id: LocalDefId) { None => DepNode::from_def_path_hash( self.tcx, def_path_hash, - dep_kinds::opt_hir_owner_nodes, + DepKind::opt_hir_owner_nodes, ), Some(n) => { match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) { @@ -186,7 +185,7 @@ fn check_paths<'tcx>(tcx: TyCtxt<'tcx>, if_this_changed: &Sources, then_this_wou } return; } - tcx.dep_graph.with_query(|query| { + tcx.dep_graph.with_retained_dep_graph(|query| { for &(_, source_def_id, ref source_dep_node) in if_this_changed { let dependents = query.transitive_predecessors(source_dep_node); for &(target_span, ref target_pass, _, ref target_dep_node) in then_this_would_need { @@ -204,7 +203,7 @@ fn check_paths<'tcx>(tcx: TyCtxt<'tcx>, if_this_changed: &Sources, then_this_wou }); } -fn dump_graph(query: &DepGraphQuery) { +fn dump_graph(graph: &RetainedDepGraph) { let path: String = env::var("RUST_DEP_GRAPH").unwrap_or_else(|_| "dep_graph".to_string()); let nodes = match env::var("RUST_DEP_GRAPH_FILTER") { @@ -212,13 +211,13 @@ fn dump_graph(query: &DepGraphQuery) { // Expect one of: "-> target", "source -> target", or "source ->". let edge_filter = EdgeFilter::new(&string).unwrap_or_else(|e| bug!("invalid filter: {}", e)); - let sources = node_set(query, &edge_filter.source); - let targets = node_set(query, &edge_filter.target); - filter_nodes(query, &sources, &targets) + let sources = node_set(graph, &edge_filter.source); + let targets = node_set(graph, &edge_filter.target); + filter_nodes(graph, &sources, &targets) } - Err(_) => query.nodes().into_iter().map(|n| n.kind).collect(), + Err(_) => graph.nodes().into_iter().map(|n| n.kind).collect(), }; - let edges = filter_edges(query, &nodes); + let edges = filter_edges(graph, &nodes); { // dump a .txt file with just the edges: @@ -281,51 +280,51 @@ fn node_label(&self, n: &DepKind) -> dot::LabelText<'_> { // Given an optional filter like `"x,y,z"`, returns either `None` (no // filter) or the set of nodes whose labels contain all of those // substrings. -fn node_set<'q>( - query: &'q DepGraphQuery, +fn node_set<'g>( + graph: &'g RetainedDepGraph, filter: &DepNodeFilter, -) -> Option> { +) -> Option> { debug!("node_set(filter={:?})", filter); if filter.accepts_all() { return None; } - Some(query.nodes().into_iter().filter(|n| filter.test(n)).collect()) + Some(graph.nodes().into_iter().filter(|n| filter.test(n)).collect()) } -fn filter_nodes<'q>( - query: &'q DepGraphQuery, - sources: &Option>, - targets: &Option>, +fn filter_nodes<'g>( + graph: &'g RetainedDepGraph, + sources: &Option>, + targets: &Option>, ) -> FxIndexSet { if let Some(sources) = sources { if let Some(targets) = targets { - walk_between(query, sources, targets) + walk_between(graph, sources, targets) } else { - walk_nodes(query, sources, OUTGOING) + walk_nodes(graph, sources, OUTGOING) } } else if let Some(targets) = targets { - walk_nodes(query, targets, INCOMING) + walk_nodes(graph, targets, INCOMING) } else { - query.nodes().into_iter().map(|n| n.kind).collect() + graph.nodes().into_iter().map(|n| n.kind).collect() } } -fn walk_nodes<'q>( - query: &'q DepGraphQuery, - starts: &FxIndexSet<&'q DepNode>, +fn walk_nodes<'g>( + graph: &'g RetainedDepGraph, + starts: &FxIndexSet<&'g DepNode>, direction: Direction, ) -> FxIndexSet { let mut set = FxIndexSet::default(); for &start in starts { debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING); if set.insert(start.kind) { - let mut stack = vec![query.indices[start]]; + let mut stack = vec![graph.indices[start]]; while let Some(index) = stack.pop() { - for (_, edge) in query.graph.adjacent_edges(index, direction) { + for (_, edge) in graph.inner.adjacent_edges(index, direction) { let neighbor_index = edge.source_or_target(direction); - let neighbor = query.graph.node_data(neighbor_index); + let neighbor = graph.inner.node_data(neighbor_index); if set.insert(neighbor.kind) { stack.push(neighbor_index); } @@ -336,10 +335,10 @@ fn walk_nodes<'q>( set } -fn walk_between<'q>( - query: &'q DepGraphQuery, - sources: &FxIndexSet<&'q DepNode>, - targets: &FxIndexSet<&'q DepNode>, +fn walk_between<'g>( + graph: &'g RetainedDepGraph, + sources: &FxIndexSet<&'g DepNode>, + targets: &FxIndexSet<&'g DepNode>, ) -> FxIndexSet { // This is a bit tricky. We want to include a node only if it is: // (a) reachable from a source and (b) will reach a target. And we @@ -354,27 +353,27 @@ enum State { Excluded, } - let mut node_states = vec![State::Undecided; query.graph.len_nodes()]; + let mut node_states = vec![State::Undecided; graph.inner.len_nodes()]; for &target in targets { - node_states[query.indices[target].0] = State::Included; + node_states[graph.indices[target].0] = State::Included; } - for source in sources.iter().map(|&n| query.indices[n]) { - recurse(query, &mut node_states, source); + for source in sources.iter().map(|&n| graph.indices[n]) { + recurse(graph, &mut node_states, source); } - return query + return graph .nodes() .into_iter() .filter(|&n| { - let index = query.indices[n]; + let index = graph.indices[n]; node_states[index.0] == State::Included }) .map(|n| n.kind) .collect(); - fn recurse(query: &DepGraphQuery, node_states: &mut [State], node: NodeIndex) -> bool { + fn recurse(graph: &RetainedDepGraph, node_states: &mut [State], node: NodeIndex) -> bool { match node_states[node.0] { // known to reach a target State::Included => return true, @@ -390,8 +389,8 @@ fn recurse(query: &DepGraphQuery, node_states: &mut [State], node: NodeIndex) -> node_states[node.0] = State::Deciding; - for neighbor_index in query.graph.successor_nodes(node) { - if recurse(query, node_states, neighbor_index) { + for neighbor_index in graph.inner.successor_nodes(node) { + if recurse(graph, node_states, neighbor_index) { node_states[node.0] = State::Included; } } @@ -407,8 +406,8 @@ fn recurse(query: &DepGraphQuery, node_states: &mut [State], node: NodeIndex) -> } } -fn filter_edges(query: &DepGraphQuery, nodes: &FxIndexSet) -> Vec<(DepKind, DepKind)> { - let uniq: FxIndexSet<_> = query +fn filter_edges(graph: &RetainedDepGraph, nodes: &FxIndexSet) -> Vec<(DepKind, DepKind)> { + let uniq: FxIndexSet<_> = graph .edges() .into_iter() .map(|(s, t)| (s.kind, t.kind)) diff --git a/compiler/rustc_incremental/src/persist/clean.rs b/compiler/rustc_incremental/src/persist/clean.rs index d83ba5a78bed..ab336a69737a 100644 --- a/compiler/rustc_incremental/src/persist/clean.rs +++ b/compiler/rustc_incremental/src/persist/clean.rs @@ -9,7 +9,7 @@ //! - `#[rustc_clean(cfg="rev2")]` same as above, except that the //! fingerprints must be the SAME (along with all other fingerprints). //! -//! - `#[rustc_clean(cfg="rev2", loaded_from_disk='typeck")]` asserts that +//! - `#[rustc_clean(cfg="rev2", loaded_from_disk="typeck")]` asserts that //! the query result for `DepNode::typeck(X)` was actually //! loaded from disk (not just marked green). This can be useful //! to ensure that a test is actually exercising the deserialization @@ -27,7 +27,7 @@ Attribute, ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, find_attr, intravisit, }; -use rustc_middle::dep_graph::{DepNode, DepNodeExt, dep_kind_from_label, label_strs}; +use rustc_middle::dep_graph::{DepNode, dep_kind_from_label, label_strs}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::{Span, Symbol}; @@ -322,7 +322,7 @@ fn dep_node_str(&self, dep_node: &DepNode) -> String { if let Some(def_id) = dep_node.extract_def_id(self.tcx) { format!("{:?}({})", dep_node.kind, self.tcx.def_path_str(def_id)) } else { - format!("{:?}({:?})", dep_node.kind, dep_node.hash) + format!("{:?}({:?})", dep_node.kind, dep_node.key_fingerprint) } } @@ -352,13 +352,11 @@ fn check_item(&mut self, item_id: LocalDefId) { let item_span = self.tcx.def_span(item_id.to_def_id()); let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id()); - let Some(attr) = - find_attr!(self.tcx.get_all_attrs(item_id), AttributeKind::RustcClean(attr) => attr) - else { + let Some(clean_attrs) = find_attr!(self.tcx, item_id, RustcClean(attr) => attr) else { return; }; - for attr in attr { + for attr in clean_attrs { let Some(assertion) = self.assertion_maybe(item_id, attr) else { continue; }; diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index e93cba2c8d0f..f7182e3614dc 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -6,7 +6,7 @@ use rustc_data_structures::memmap::Mmap; use rustc_data_structures::unord::UnordMap; use rustc_hashes::Hash64; -use rustc_middle::dep_graph::{DepGraph, DepsType, SerializedDepGraph, WorkProductMap}; +use rustc_middle::dep_graph::{DepGraph, SerializedDepGraph, WorkProductMap}; use rustc_middle::query::on_disk_cache::OnDiskCache; use rustc_serialize::Decodable; use rustc_serialize::opaque::MemDecoder; @@ -171,7 +171,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc, WorkPr return LoadResult::DataOutOfDate; } - let dep_graph = SerializedDepGraph::decode::(&mut decoder); + let dep_graph = SerializedDepGraph::decode(&mut decoder); LoadResult::Ok { data: (dep_graph, prev_work_products) } } diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 996ae162607d..1de64bb6734d 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -8,7 +8,7 @@ }; use rustc_middle::ty::TyCtxt; use rustc_serialize::Encodable as RustcEncodable; -use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; +use rustc_serialize::opaque::FileEncoder; use rustc_session::Session; use tracing::debug; @@ -60,13 +60,30 @@ pub(crate) fn save_dep_graph(tcx: TyCtxt<'_>) { // We execute this after `incr_comp_persist_dep_graph` for the serial compiler // to catch any potential query execution writing to the dep graph. sess.time("incr_comp_persist_result_cache", || { - // Drop the memory map so that we can remove the file and write to it. - if let Some(odc) = &tcx.query_system.on_disk_cache { - odc.drop_serialized_data(tcx); - } + // The on-disk cache struct is always present in incremental mode, + // even if there was no previous session. + let on_disk_cache = tcx.query_system.on_disk_cache.as_ref().unwrap(); - file_format::save_in(sess, query_cache_path, "query cache", |e| { - encode_query_cache(tcx, e) + // For every green dep node that has a disk-cached value from the + // previous session, make sure the value is loaded into the memory + // cache, so that it will be serialized as part of this session. + // + // This reads data from the previous session, so it needs to happen + // before dropping the mmap. + // + // FIXME(Zalathar): This step is intended to be cheap, but still does + // quite a lot of work, especially in builds with few or no changes. + // Can we be smarter about how we identify values that need promotion? + // Can we promote values without decoding them into the memory cache? + tcx.dep_graph.exec_cache_promotions(tcx); + + // Drop the memory map so that we can remove the file and write to it. + on_disk_cache.close_serialized_data_mmap(); + + file_format::save_in(sess, query_cache_path, "query cache", |encoder| { + tcx.sess.time("incr_comp_serialize_result_cache", || { + on_disk_cache.serialize(tcx, encoder) + }) }); }); }, @@ -132,10 +149,6 @@ fn encode_work_product_index( serialized_products.encode(encoder) } -fn encode_query_cache(tcx: TyCtxt<'_>, encoder: FileEncoder) -> FileEncodeResult { - tcx.sess.time("incr_comp_serialize_result_cache", || tcx.serialize_query_result_cache(encoder)) -} - /// Builds the dependency graph. /// /// This function creates the *staging dep-graph*. When the dep-graph is modified by a query diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/indexed_edges.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/indexed_edges.rs new file mode 100644 index 000000000000..ffc6e54f3cb0 --- /dev/null +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/indexed_edges.rs @@ -0,0 +1,68 @@ +use rustc_index::IndexVec; +use rustc_type_ir::RegionVid; + +use crate::infer::SubregionOrigin; +use crate::infer::region_constraints::{Constraint, ConstraintKind, RegionConstraintData}; + +/// Selects either out-edges or in-edges for [`IndexedConstraintEdges::adjacent_edges`]. +#[derive(Clone, Copy, Debug)] +pub(super) enum EdgeDirection { + Out, + In, +} + +/// Type alias for the pairs stored in [`RegionConstraintData::constraints`], +/// which we are indexing. +type ConstraintPair<'tcx> = (Constraint<'tcx>, SubregionOrigin<'tcx>); + +/// An index from region variables to their corresponding constraint edges, +/// used on some error paths. +pub(super) struct IndexedConstraintEdges<'data, 'tcx> { + out_edges: IndexVec>>, + in_edges: IndexVec>>, +} + +impl<'data, 'tcx> IndexedConstraintEdges<'data, 'tcx> { + pub(super) fn build_index(num_vars: usize, data: &'data RegionConstraintData<'tcx>) -> Self { + let mut out_edges = IndexVec::from_fn_n(|_| vec![], num_vars); + let mut in_edges = IndexVec::from_fn_n(|_| vec![], num_vars); + + for pair @ (c, _) in &data.constraints { + // Only push a var out-edge for `VarSub...` constraints. + match c.kind { + ConstraintKind::VarSubVar | ConstraintKind::VarSubReg => { + out_edges[c.sub.as_var()].push(pair) + } + ConstraintKind::RegSubVar | ConstraintKind::RegSubReg => {} + } + } + + // Index in-edges in reverse order, to match what current tests expect. + // (It's unclear whether this is important or not.) + for pair @ (c, _) in data.constraints.iter().rev() { + // Only push a var in-edge for `...SubVar` constraints. + match c.kind { + ConstraintKind::VarSubVar | ConstraintKind::RegSubVar => { + in_edges[c.sup.as_var()].push(pair) + } + ConstraintKind::VarSubReg | ConstraintKind::RegSubReg => {} + } + } + + IndexedConstraintEdges { out_edges, in_edges } + } + + /// Returns either the out-edges or in-edges of the specified region var, + /// as selected by `dir`. + pub(super) fn adjacent_edges( + &self, + region_vid: RegionVid, + dir: EdgeDirection, + ) -> &[&'data ConstraintPair<'tcx>] { + let edges = match dir { + EdgeDirection::Out => &self.out_edges, + EdgeDirection::In => &self.in_edges, + }; + &edges[region_vid] + } +} diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 5134b7b7ca8f..e99dcd1ef15c 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -3,9 +3,6 @@ use std::fmt; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::graph::linked_graph::{ - Direction, INCOMING, LinkedGraph, NodeIndex, OUTGOING, -}; use rustc_data_structures::intern::Interned; use rustc_data_structures::unord::UnordSet; use rustc_index::{IndexSlice, IndexVec}; @@ -18,11 +15,14 @@ use tracing::{debug, instrument}; use super::outlives::test_type_match; +use crate::infer::lexical_region_resolve::indexed_edges::{EdgeDirection, IndexedConstraintEdges}; use crate::infer::region_constraints::{ - Constraint, ConstraintKind, GenericKind, RegionConstraintData, VarInfos, VerifyBound, + ConstraintKind, GenericKind, RegionConstraintData, VarInfos, VerifyBound, }; use crate::infer::{RegionRelations, RegionVariableOrigin, SubregionOrigin}; +mod indexed_edges; + /// This function performs lexical region resolution given a complete /// set of constraints and variable origins. It performs a fixed-point /// iteration to find region values which satisfy all constraints, @@ -118,8 +118,6 @@ struct RegionAndOrigin<'tcx> { origin: SubregionOrigin<'tcx>, } -type RegionGraph<'tcx> = LinkedGraph<(), Constraint<'tcx>>; - struct LexicalResolver<'cx, 'tcx> { region_rels: &'cx RegionRelations<'cx, 'tcx>, var_infos: VarInfos<'tcx>, @@ -626,9 +624,8 @@ fn collect_var_errors( // overlapping locations. let mut dup_vec = IndexVec::from_elem_n(None, self.num_vars()); - // Only construct the graph when necessary, because it's moderately - // expensive. - let mut graph = None; + // Only construct the edge index when necessary, because it's moderately expensive. + let mut edges: Option> = None; for (node_vid, value) in var_data.values.iter_enumerated() { match *value { @@ -662,56 +659,18 @@ fn collect_var_errors( // influence the constraints on this value for // richer diagnostics in `static_impl_trait`. - let g = graph.get_or_insert_with(|| self.construct_graph()); - self.collect_error_for_expanding_node(g, &mut dup_vec, node_vid, errors); + let e = edges.get_or_insert_with(|| { + IndexedConstraintEdges::build_index(self.num_vars(), &self.data) + }); + self.collect_error_for_expanding_node(e, &mut dup_vec, node_vid, errors); } } } } - fn construct_graph(&self) -> RegionGraph<'tcx> { - let num_vars = self.num_vars(); - - let mut graph = LinkedGraph::new(); - - for _ in 0..num_vars { - graph.add_node(()); - } - - // Issue #30438: two distinct dummy nodes, one for incoming - // edges (dummy_source) and another for outgoing edges - // (dummy_sink). In `dummy -> a -> b -> dummy`, using one - // dummy node leads one to think (erroneously) there exists a - // path from `b` to `a`. Two dummy nodes sidesteps the issue. - let dummy_source = graph.add_node(()); - let dummy_sink = graph.add_node(()); - - for (c, _) in &self.data.constraints { - match c.kind { - ConstraintKind::VarSubVar => { - let sub_vid = c.sub.as_var(); - let sup_vid = c.sup.as_var(); - graph.add_edge(NodeIndex(sub_vid.index()), NodeIndex(sup_vid.index()), *c); - } - ConstraintKind::RegSubVar => { - graph.add_edge(dummy_source, NodeIndex(c.sup.as_var().index()), *c); - } - ConstraintKind::VarSubReg => { - graph.add_edge(NodeIndex(c.sub.as_var().index()), dummy_sink, *c); - } - ConstraintKind::RegSubReg => { - // this would be an edge from `dummy_source` to - // `dummy_sink`; just ignore it. - } - } - } - - graph - } - fn collect_error_for_expanding_node( &self, - graph: &RegionGraph<'tcx>, + edges: &IndexedConstraintEdges<'_, 'tcx>, dup_vec: &mut IndexSlice>, node_idx: RegionVid, errors: &mut Vec>, @@ -719,9 +678,9 @@ fn collect_error_for_expanding_node( // Errors in expanding nodes result from a lower-bound that is // not contained by an upper-bound. let (mut lower_bounds, lower_vid_bounds, lower_dup) = - self.collect_bounding_regions(graph, node_idx, INCOMING, Some(dup_vec)); + self.collect_bounding_regions(edges, node_idx, EdgeDirection::In, Some(dup_vec)); let (mut upper_bounds, _, upper_dup) = - self.collect_bounding_regions(graph, node_idx, OUTGOING, Some(dup_vec)); + self.collect_bounding_regions(edges, node_idx, EdgeDirection::Out, Some(dup_vec)); if lower_dup || upper_dup { return; @@ -829,9 +788,9 @@ fn region_order_key(x: &RegionAndOrigin<'_>) -> u8 { /// those returned by a previous call for another region. fn collect_bounding_regions( &self, - graph: &RegionGraph<'tcx>, + edges: &IndexedConstraintEdges<'_, 'tcx>, orig_node_idx: RegionVid, - dir: Direction, + dir: EdgeDirection, mut dup_vec: Option<&mut IndexSlice>>, ) -> (Vec>, FxHashSet, bool) { struct WalkState<'tcx> { @@ -850,7 +809,7 @@ struct WalkState<'tcx> { // to start off the process, walk the source node in the // direction specified - process_edges(&self.data, &mut state, graph, orig_node_idx, dir); + process_edges(&mut state, edges, orig_node_idx, dir); while let Some(node_idx) = state.stack.pop() { // check whether we've visited this node on some previous walk @@ -867,30 +826,25 @@ struct WalkState<'tcx> { ); } - process_edges(&self.data, &mut state, graph, node_idx, dir); + process_edges(&mut state, edges, node_idx, dir); } let WalkState { result, dup_found, set, .. } = state; return (result, set, dup_found); fn process_edges<'tcx>( - this: &RegionConstraintData<'tcx>, state: &mut WalkState<'tcx>, - graph: &RegionGraph<'tcx>, + edges: &IndexedConstraintEdges<'_, 'tcx>, source_vid: RegionVid, - dir: Direction, + dir: EdgeDirection, ) { debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir); - let source_node_index = NodeIndex(source_vid.index()); - for (_, edge) in graph.adjacent_edges(source_node_index, dir) { - let get_origin = - || this.constraints.iter().find(|(c, _)| *c == edge.data).unwrap().1.clone(); - - match edge.data.kind { + for (c, origin) in edges.adjacent_edges(source_vid, dir) { + match c.kind { ConstraintKind::VarSubVar => { - let from_vid = edge.data.sub.as_var(); - let to_vid = edge.data.sup.as_var(); + let from_vid = c.sub.as_var(); + let to_vid = c.sup.as_var(); let opp_vid = if from_vid == source_vid { to_vid } else { from_vid }; if state.set.insert(opp_vid) { state.stack.push(opp_vid); @@ -898,13 +852,13 @@ fn process_edges<'tcx>( } ConstraintKind::RegSubVar => { - let origin = get_origin(); - state.result.push(RegionAndOrigin { region: edge.data.sub, origin }); + let origin = origin.clone(); + state.result.push(RegionAndOrigin { region: c.sub, origin }); } ConstraintKind::VarSubReg => { - let origin = get_origin(); - state.result.push(RegionAndOrigin { region: edge.data.sup, origin }); + let origin = origin.clone(); + state.result.push(RegionAndOrigin { region: c.sup, origin }); } ConstraintKind::RegSubReg => panic!( diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index affeb01e6d05..5ceb610ae1da 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -1,4 +1,5 @@ -use rustc_data_structures::assert_matches; +use std::assert_matches; + use rustc_middle::ty::outlives::{Component, compute_alias_components_recursive}; use rustc_middle::ty::{self, OutlivesPredicate, Ty, TyCtxt}; use smallvec::smallvec; diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index a6f324b70471..19212c99ae43 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -1,7 +1,8 @@ +use std::assert_matches; use std::marker::PhantomData; use rustc_data_structures::undo_log::{Rollback, UndoLogs}; -use rustc_data_structures::{assert_matches, snapshot_vec as sv, unify as ut}; +use rustc_data_structures::{snapshot_vec as sv, unify as ut}; use rustc_middle::ty::{self, OpaqueTypeKey, ProvisionalHiddenType}; use tracing::debug; diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 008b5c94a5ea..2595c1a7b276 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -14,7 +14,6 @@ // tidy-alphabetical-start #![allow(rustc::direct_use_of_rustc_type_ir)] -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(extend_one)] #![recursion_limit = "512"] // For rustdoc // tidy-alphabetical-end diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index 5aa1616397f8..b2ad70bc4811 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -11,11 +11,10 @@ use std::fmt; -use rustc_errors::{DiagInner, TRACK_DIAGNOSTIC}; -use rustc_middle::dep_graph::dep_node::default_dep_kind_debug; -use rustc_middle::dep_graph::{DepContext, DepKind, DepNode, DepNodeExt, TaskDepsRef}; +use rustc_errors::DiagInner; +use rustc_middle::dep_graph::{DepNodeIndex, QuerySideEffect, TaskDepsRef}; use rustc_middle::ty::tls; -use rustc_query_impl::QueryCtxt; +use rustc_span::Symbol; fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) { tls::with_context_opt(|icx| { @@ -41,10 +40,10 @@ fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) { fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { tls::with_context_opt(|icx| { if let Some(icx) = icx { - icx.tcx.dep_graph.record_diagnostic(QueryCtxt::new(icx.tcx), &diagnostic); + icx.tcx.dep_graph.record_diagnostic(icx.tcx, &diagnostic); // Diagnostics are tracked, we can ignore the dependency. - let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..icx.clone() }; + let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..*icx }; tls::enter_context(&icx, move || (*f)(diagnostic)) } else { // In any other case, invoke diagnostics anyway. @@ -53,6 +52,25 @@ fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) }) } +fn track_feature(feature: Symbol) { + tls::with_context_opt(|icx| { + let Some(icx) = icx else { + return; + }; + let tcx = icx.tcx; + + if let Some(dep_node_index) = tcx.sess.used_features.lock().get(&feature).copied() { + tcx.dep_graph.read_index(DepNodeIndex::from_u32(dep_node_index)); + } else { + let dep_node_index = tcx + .dep_graph + .encode_side_effect(tcx, QuerySideEffect::CheckFeature { symbol: feature }); + tcx.sess.used_features.lock().insert(feature, dep_node_index.as_u32()); + tcx.dep_graph.read_index(dep_node_index); + } + }) +} + /// This is a callback from `rustc_hir` as it cannot access the implicit state /// in `rustc_middle` otherwise. fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -66,49 +84,11 @@ fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> write!(f, ")") } -/// This is a callback from `rustc_query_system` as it cannot access the implicit state -/// in `rustc_middle` otherwise. -pub fn dep_kind_debug(kind: DepKind, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - tls::with_opt(|opt_tcx| { - if let Some(tcx) = opt_tcx { - write!(f, "{}", tcx.dep_kind_vtable(kind).name) - } else { - default_dep_kind_debug(kind, f) - } - }) -} - -/// This is a callback from `rustc_query_system` as it cannot access the implicit state -/// in `rustc_middle` otherwise. -pub fn dep_node_debug(node: DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}(", node.kind)?; - - tls::with_opt(|opt_tcx| { - if let Some(tcx) = opt_tcx { - if let Some(def_id) = node.extract_def_id(tcx) { - write!(f, "{}", tcx.def_path_debug_str(def_id))?; - } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(node) { - write!(f, "{s}")?; - } else { - write!(f, "{}", node.hash)?; - } - } else { - write!(f, "{}", node.hash)?; - } - Ok(()) - })?; - - write!(f, ")") -} - /// Sets up the callbacks in prior crates which we want to refer to the /// TyCtxt in. pub fn setup_callbacks() { rustc_span::SPAN_TRACK.swap(&(track_span_parent as fn(_))); rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); - rustc_middle::dep_graph::dep_node::DEP_KIND_DEBUG - .swap(&(dep_kind_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); - rustc_middle::dep_graph::dep_node::DEP_NODE_DEBUG - .swap(&(dep_node_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); - TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _)); + rustc_errors::TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _)); + rustc_feature::TRACK_FEATURE.swap(&(track_feature as _)); } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 34c064362168..91b7f234d5f6 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -16,7 +16,7 @@ use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::Recovery; use rustc_parse::parser::attr::AllowLeadingUnsafe; -use rustc_query_impl::{QueryCtxt, print_query_stack}; +use rustc_query_impl::print_query_stack; use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName}; use rustc_session::parse::ParseSess; use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, lint}; @@ -435,16 +435,6 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); - let bundle = match rustc_errors::fluent_bundle( - &config.opts.sysroot.all_paths().collect::>(), - config.opts.unstable_opts.translate_lang.clone(), - config.opts.unstable_opts.translate_additional_ftl.as_deref(), - config.opts.unstable_opts.translate_directionality_markers, - ) { - Ok(bundle) => bundle, - Err(e) => early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")), - }; - let mut sess = rustc_session::build_session( config.opts, CompilerIO { @@ -453,7 +443,6 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se output_file: config.output_file, temps_dir, }, - bundle, config.lint_caps, target, util::rustc_version_str().unwrap_or("unknown"), @@ -556,7 +545,7 @@ pub fn try_print_query_stack( let all_frames = ty::tls::with_context_opt(|icx| { if let Some(icx) = icx { ty::print::with_no_queries!(print_query_stack( - QueryCtxt::new(icx.tcx), + icx.tcx, icx.query, dcx, limit_frames, diff --git a/compiler/rustc_interface/src/limits.rs b/compiler/rustc_interface/src/limits.rs index 10e58f32256f..e0fc91f3b723 100644 --- a/compiler/rustc_interface/src/limits.rs +++ b/compiler/rustc_interface/src/limits.rs @@ -8,7 +8,6 @@ //! 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::attrs::AttributeKind; use rustc_hir::limit::Limit; use rustc_hir::{Attribute, find_attr}; use rustc_middle::query::Providers; @@ -19,14 +18,12 @@ pub(crate) fn provide(providers: &mut Providers) { let attrs = tcx.hir_krate_attrs(); Limits { recursion_limit: get_recursion_limit(tcx.hir_krate_attrs()), - move_size_limit: - find_attr!(attrs, AttributeKind::MoveSizeLimit { limit, .. } => *limit) - .unwrap_or(Limit::new(tcx.sess.opts.unstable_opts.move_size_limit.unwrap_or(0))), - type_length_limit: - find_attr!(attrs, AttributeKind::TypeLengthLimit { limit, .. } => *limit) - .unwrap_or(Limit::new(2usize.pow(24))), + move_size_limit: find_attr!(attrs, MoveSizeLimit { limit, .. } => *limit) + .unwrap_or(Limit::new(tcx.sess.opts.unstable_opts.move_size_limit.unwrap_or(0))), + type_length_limit: find_attr!(attrs, TypeLengthLimit { limit, .. } => *limit) + .unwrap_or(Limit::new(2usize.pow(24))), pattern_complexity_limit: - find_attr!(attrs, AttributeKind::PatternComplexityLimit { limit, .. } => *limit) + find_attr!(attrs, PatternComplexityLimit { limit, .. } => *limit) .unwrap_or(Limit::unlimited()), } } @@ -34,6 +31,5 @@ pub(crate) fn provide(providers: &mut Providers) { // This one is separate because it must be read prior to macro expansion. pub(crate) fn get_recursion_limit(attrs: &[Attribute]) -> Limit { - find_attr!(attrs, AttributeKind::RecursionLimit { limit, .. } => *limit) - .unwrap_or(Limit::new(128)) + find_attr!(attrs, RecursionLimit { limit, .. } => *limit).unwrap_or(Limit::new(128)) } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index a2c11c608330..a280b2a2a6bf 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -8,9 +8,8 @@ use rustc_ast::{self as ast, CRATE_NODE_ID}; use rustc_attr_parsing::{AttributeParser, Early, ShouldEmit}; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_codegen_ssa::{CodegenResults, CrateInfo}; +use rustc_codegen_ssa::{CompiledModules, CrateInfo}; use rustc_data_structures::indexmap::IndexMap; -use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal, par_fns}; use rustc_data_structures::thousands; @@ -28,7 +27,7 @@ use rustc_metadata::EncodedMetadata; use rustc_metadata::creader::CStore; use rustc_middle::arena::Arena; -use rustc_middle::ty::{self, CurrentGcx, GlobalCtxt, RegisteredTools, TyCtxt}; +use rustc_middle::ty::{self, RegisteredTools, TyCtxt}; use rustc_middle::util::Providers; use rustc_parse::lexer::StripTokens; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; @@ -782,7 +781,7 @@ fn hash_iter_files>( fn resolver_for_lowering_raw<'tcx>( tcx: TyCtxt<'tcx>, (): (), -) -> (&'tcx Steal<(ty::ResolverAstLowering, Arc)>, &'tcx ty::ResolverGlobalCtxt) { +) -> (&'tcx Steal<(ty::ResolverAstLowering<'tcx>, Arc)>, &'tcx ty::ResolverGlobalCtxt) { let arenas = Resolver::arenas(); let _ = tcx.registered_tools(()); // Uses `crate_for_resolver`. let (krate, pre_configured_attrs) = tcx.crate_for_resolver(()).steal(); @@ -965,72 +964,60 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( let incremental = dep_graph.is_fully_enabled(); + // Note: this function body is the origin point of the widely-used 'tcx lifetime. + // + // `gcx_cell` is defined here and `&gcx_cell` is passed to `create_global_ctxt`, which then + // actually creates the `GlobalCtxt` with a `gcx_cell.get_or_init(...)` call. This is done so + // that the resulting reference has the type `&'tcx GlobalCtxt<'tcx>`, which is what `TyCtxt` + // needs. If we defined and created the `GlobalCtxt` within `create_global_ctxt` then its type + // would be `&'a GlobalCtxt<'tcx>`, with two lifetimes. + // + // Similarly, by creating `arena` here and passing in `&arena`, that reference has the type + // `&'tcx WorkerLocal>`, also with one lifetime. And likewise for `hir_arena`. + let gcx_cell = OnceLock::new(); let arena = WorkerLocal::new(|_| Arena::default()); let hir_arena = WorkerLocal::new(|_| rustc_hir::Arena::default()); - // This closure is necessary to force rustc to perform the correct lifetime - // subtyping for GlobalCtxt::enter to be allowed. - let inner: Box< - dyn for<'tcx> FnOnce( - &'tcx Session, - CurrentGcx, - Arc, - &'tcx OnceLock>, - &'tcx WorkerLocal>, - &'tcx WorkerLocal>, - F, - ) -> T, - > = Box::new(move |sess, current_gcx, jobserver_proxy, gcx_cell, arena, hir_arena, f| { - TyCtxt::create_global_ctxt( - gcx_cell, - sess, - crate_types, - stable_crate_id, - arena, - hir_arena, - untracked, - dep_graph, - rustc_query_impl::make_dep_kind_vtables(arena), - rustc_query_impl::query_system( - providers.queries, - providers.extern_queries, - query_result_on_disk_cache, - incremental, - ), - providers.hooks, - current_gcx, - jobserver_proxy, - |tcx| { - let feed = tcx.create_crate_num(stable_crate_id).unwrap(); - assert_eq!(feed.key(), LOCAL_CRATE); - feed.crate_name(crate_name); - - let feed = tcx.feed_unit_query(); - feed.features_query(tcx.arena.alloc(rustc_expand::config::features( - tcx.sess, - &pre_configured_attrs, - crate_name, - ))); - feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs)))); - feed.output_filenames(Arc::new(outputs)); - - let res = f(tcx); - // FIXME maybe run finish even when a fatal error occurred? or at least tcx.alloc_self_profile_query_strings()? - tcx.finish(); - res - }, - ) - }); - - inner( - &compiler.sess, - compiler.current_gcx.clone(), - Arc::clone(&compiler.jobserver_proxy), + TyCtxt::create_global_ctxt( &gcx_cell, + &compiler.sess, + crate_types, + stable_crate_id, &arena, &hir_arena, - f, + untracked, + dep_graph, + rustc_query_impl::make_dep_kind_vtables(&arena), + rustc_query_impl::query_system( + providers.queries, + providers.extern_queries, + query_result_on_disk_cache, + incremental, + ), + providers.hooks, + compiler.current_gcx.clone(), + Arc::clone(&compiler.jobserver_proxy), + |tcx| { + let feed = tcx.create_crate_num(stable_crate_id).unwrap(); + assert_eq!(feed.key(), LOCAL_CRATE); + feed.crate_name(crate_name); + + let feed = tcx.feed_unit_query(); + feed.features_query(tcx.arena.alloc(rustc_expand::config::features( + tcx.sess, + &pre_configured_attrs, + crate_name, + ))); + feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs)))); + feed.output_filenames(Arc::new(outputs)); + + let res = f(tcx); + // FIXME maybe run finish even when a fatal error occurred? or at least + // tcx.alloc_self_profile_query_strings()? + tcx.finish(); + res + }, ) } @@ -1218,12 +1205,12 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) { pub(crate) fn start_codegen<'tcx>( codegen_backend: &dyn CodegenBackend, tcx: TyCtxt<'tcx>, -) -> (Box, EncodedMetadata) { +) -> (Box, CrateInfo, EncodedMetadata) { tcx.sess.timings.start_section(tcx.sess.dcx(), TimingSection::Codegen); // Hook for tests. if let Some((def_id, _)) = tcx.entry_fn(()) - && find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcDelayedBugFromInsideQuery) + && find_attr!(tcx, def_id, RustcDelayedBugFromInsideQuery) { tcx.ensure_ok().trigger_delayed_bug(def_id); } @@ -1245,19 +1232,17 @@ pub(crate) fn start_codegen<'tcx>( let metadata = rustc_metadata::fs::encode_and_write_metadata(tcx); - let codegen = tcx.sess.time("codegen_crate", move || { + let crate_info = CrateInfo::new(tcx, codegen_backend.target_cpu(tcx.sess)); + + let codegen = tcx.sess.time("codegen_crate", || { if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { // Skip crate items and just output metadata in -Z no-codegen mode. tcx.sess.dcx().abort_if_errors(); // Linker::link will skip join_codegen in case of a CodegenResults Any value. - Box::new(CodegenResults { - modules: vec![], - allocator_module: None, - crate_info: CrateInfo::new(tcx, "".to_owned()), - }) + Box::new(CompiledModules { modules: vec![], allocator_module: None }) } else { - codegen_backend.codegen_crate(tcx) + codegen_backend.codegen_crate(tcx, &crate_info) } }); @@ -1269,7 +1254,7 @@ pub(crate) fn start_codegen<'tcx>( tcx.sess.code_stats.print_type_sizes(); } - (codegen, metadata) + (codegen, crate_info, metadata) } /// Compute and validate the crate name. diff --git a/compiler/rustc_interface/src/proc_macro_decls.rs b/compiler/rustc_interface/src/proc_macro_decls.rs index bd08faa1ed3a..63fed51b020a 100644 --- a/compiler/rustc_interface/src/proc_macro_decls.rs +++ b/compiler/rustc_interface/src/proc_macro_decls.rs @@ -1,4 +1,3 @@ -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::find_attr; use rustc_middle::query::Providers; @@ -8,7 +7,7 @@ fn proc_macro_decls_static(tcx: TyCtxt<'_>, (): ()) -> Option { let mut decls = None; for id in tcx.hir_free_items() { - if find_attr!(tcx.hir_attrs(id.hir_id()), AttributeKind::RustcProcMacroDecls) { + if find_attr!(tcx.hir_attrs(id.hir_id()), RustcProcMacroDecls) { decls = Some(id.owner_id.def_id); } } diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 3799485077ef..170393f3b817 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -1,8 +1,8 @@ use std::any::Any; use std::sync::Arc; -use rustc_codegen_ssa::CodegenResults; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_codegen_ssa::{CompiledModules, CrateInfo}; use rustc_data_structures::indexmap::IndexMap; use rustc_data_structures::svh::Svh; use rustc_errors::timings::TimingSection; @@ -21,6 +21,7 @@ pub struct Linker { output_filenames: Arc, // Only present when incr. comp. is enabled. crate_hash: Option, + crate_info: CrateInfo, metadata: EncodedMetadata, ongoing_codegen: Box, } @@ -30,7 +31,7 @@ pub fn codegen_and_build_linker( tcx: TyCtxt<'_>, codegen_backend: &dyn CodegenBackend, ) -> Linker { - let (ongoing_codegen, metadata) = passes::start_codegen(codegen_backend, tcx); + let (ongoing_codegen, crate_info, metadata) = passes::start_codegen(codegen_backend, tcx); Linker { dep_graph: tcx.dep_graph.clone(), @@ -40,22 +41,32 @@ pub fn codegen_and_build_linker( } else { None }, + crate_info, metadata, ongoing_codegen, } } pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) { - let (codegen_results, mut work_products) = sess.time("finish_ongoing_codegen", || { - match self.ongoing_codegen.downcast::() { + let (compiled_modules, mut work_products) = sess.time("finish_ongoing_codegen", || { + match self.ongoing_codegen.downcast::() { // This was a check only build - Ok(codegen_results) => (*codegen_results, IndexMap::default()), + Ok(compiled_modules) => (*compiled_modules, IndexMap::default()), Err(ongoing_codegen) => { codegen_backend.join_codegen(ongoing_codegen, sess, &self.output_filenames) } } }); + + if sess.codegen_units().as_usize() == 1 && sess.opts.unstable_opts.time_llvm_passes { + codegen_backend.print_pass_timings() + } + + if sess.print_llvm_stats() { + codegen_backend.print_statistics() + } + sess.timings.end_section(sess.dcx(), TimingSection::Codegen); if sess.opts.incremental.is_some() @@ -97,10 +108,11 @@ pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) { if sess.opts.unstable_opts.no_link { let rlink_file = self.output_filenames.with_extension(config::RLINK_EXT); - CodegenResults::serialize_rlink( + CompiledModules::serialize_rlink( sess, &rlink_file, - &codegen_results, + &compiled_modules, + &self.crate_info, &self.metadata, &self.output_filenames, ) @@ -112,6 +124,12 @@ pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) { let _timer = sess.prof.verbose_generic_activity("link_crate"); let _timing = sess.timings.section_guard(sess.dcx(), TimingSection::Linking); - codegen_backend.link(sess, codegen_results, self.metadata, &self.output_filenames) + codegen_backend.link( + sess, + compiled_modules, + self.crate_info, + self.metadata, + &self.output_filenames, + ) } } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 5ebf898f45a3..88056a0db966 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -71,7 +71,6 @@ fn sess_and_cfg(args: &[&'static str], f: F) let sess = build_session( sessopts, io, - None, Default::default(), target, "", @@ -796,7 +795,6 @@ macro_rules! tracked { tracked!(dwarf_version, Some(5)); tracked!(embed_metadata, false); tracked!(embed_source, true); - tracked!(emit_thin_lto, false); tracked!(emscripten_wasm_eh, false); tracked!(export_executable_symbols, true); tracked!(fewer_names, Some(true)); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 613b3c64efed..bacdad25c50b 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -11,13 +11,14 @@ use rustc_codegen_ssa::back::link::link_binary; use rustc_codegen_ssa::target_features::cfg_target_feature; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_codegen_ssa::{CodegenResults, CrateInfo, TargetConfig}; +use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync; use rustc_metadata::{DylibError, EncodedMetadata, load_symbol_from_dylib}; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::{CurrentGcx, TyCtxt}; +use rustc_query_impl::collect_active_jobs_from_all_queries; use rustc_session::config::{ Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple, }; @@ -184,7 +185,7 @@ pub(crate) fn run_in_thread_pool_with_globals< use rustc_data_structures::defer; use rustc_data_structures::sync::FromDyn; use rustc_middle::ty::tls; - use rustc_query_impl::{QueryCtxt, break_query_cycles}; + use rustc_query_impl::break_query_cycles; let thread_stack_size = init_stack_size(thread_builder_diag); @@ -253,7 +254,7 @@ pub(crate) fn run_in_thread_pool_with_globals< || { // Ensure there were no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - QueryCtxt::new(tcx).collect_active_jobs_from_all_queries(false).expect( + collect_active_jobs_from_all_queries(tcx, false).expect( "failed to collect active queries in deadlock handler", ) }, @@ -299,7 +300,7 @@ pub(crate) fn run_in_thread_pool_with_globals< diag.help( "try lowering `-Z threads` or checking the operating system's resource limits", ); - diag.emit(); + diag.emit() }) }) }) @@ -400,12 +401,12 @@ fn supported_crate_types(&self, _sess: &Session) -> Vec { vec![CrateType::Rlib, CrateType::Executable] } - fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { - Box::new(CodegenResults { - modules: vec![], - allocator_module: None, - crate_info: CrateInfo::new(tcx, String::new()), - }) + fn target_cpu(&self, _sess: &Session) -> String { + String::new() + } + + fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>, _crate_info: &CrateInfo) -> Box { + Box::new(CompiledModules { modules: vec![], allocator_module: None }) } fn join_codegen( @@ -413,24 +414,22 @@ fn join_codegen( ongoing_codegen: Box, _sess: &Session, _outputs: &OutputFilenames, - ) -> (CodegenResults, FxIndexMap) { + ) -> (CompiledModules, FxIndexMap) { (*ongoing_codegen.downcast().unwrap(), FxIndexMap::default()) } fn link( &self, sess: &Session, - codegen_results: CodegenResults, + compiled_modules: CompiledModules, + crate_info: CrateInfo, metadata: EncodedMetadata, outputs: &OutputFilenames, ) { // JUSTIFICATION: TyCtxt no longer available here #[allow(rustc::bad_opt_access)] - if let Some(&crate_type) = codegen_results - .crate_info - .crate_types - .iter() - .find(|&&crate_type| crate_type != CrateType::Rlib) + if let Some(&crate_type) = + crate_info.crate_types.iter().find(|&&crate_type| crate_type != CrateType::Rlib) && outputs.outputs.should_link() { sess.dcx().fatal(format!( @@ -441,7 +440,8 @@ fn link( link_binary( sess, &DummyArchiveBuilderBuilder, - codegen_results, + compiled_modules, + crate_info, metadata, outputs, self.name(), diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 9d3da6ef4930..dc6e3b1f358d 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -367,7 +367,8 @@ pub fn is_whitespace(c: char) -> bool { /// True if `c` is considered horizontal whitespace according to Rust language definition. pub fn is_horizontal_whitespace(c: char) -> bool { - // This is Pattern_White_Space. + // This is the horizontal space subset of `Pattern_White_Space` as + // categorized by UAX #31, Section 4.1. // // Note that this set is stable (ie, it doesn't change with different // Unicode versions), so it's ok to just hard-code the values. diff --git a/compiler/rustc_lint/src/async_closures.rs b/compiler/rustc_lint/src/async_closures.rs index 0d3a954667f7..0bc245c742b2 100644 --- a/compiler/rustc_lint/src/async_closures.rs +++ b/compiler/rustc_lint/src/async_closures.rs @@ -1,5 +1,5 @@ use rustc_hir as hir; -use rustc_macros::{LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::Span; @@ -107,7 +107,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("closure returning async block can be made into an async closure")] struct ClosureReturningAsyncBlock { #[label( diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 5bb7df80ffb3..34049288ea80 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -1,5 +1,4 @@ use rustc_ast::{BorrowKind, UnOp}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::{Expr, ExprKind, Mutability, find_attr}; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AutoBorrow, DerefAdjustKind, OverloadedDeref, @@ -108,7 +107,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), _ => None, } - && method_did.map(|did| find_attr!(cx.tcx.get_all_attrs(did), AttributeKind::RustcNoImplicitAutorefs)).unwrap_or(true) + && method_did.map(|did| find_attr!(cx.tcx, did, RustcNoImplicitAutorefs)).unwrap_or(true) { cx.emit_span_lint( DANGEROUS_IMPLICIT_AUTOREFS, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 42e714230010..54c8c75b88fc 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -22,14 +22,13 @@ use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust::expr_to_string; use rustc_attr_parsing::AttributeParser; -use rustc_errors::{Applicability, LintDiagnostic, msg}; +use rustc_errors::{Applicability, Diagnostic, msg}; use rustc_feature::GateIssue; -use rustc_hir as hir; 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::{Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr}; +use rustc_hir::{self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr}; use rustc_middle::bug; use rustc_middle::lint::LevelAndSource; use rustc_middle::ty::layout::LayoutOf; @@ -44,9 +43,9 @@ use rustc_span::{DUMMY_SP, Ident, InnerSpan, Span, Symbol, kw, sym}; use rustc_target::asm::InlineAsmArch; use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt}; +use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{self}; use crate::errors::BuiltinEllipsisInclusiveRangePatterns; use crate::lints::{ @@ -59,7 +58,7 @@ BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, - BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, + BuiltinUnusedDocCommentSub, BuiltinWhileTrue, EqInternalMethodImplemented, InvalidAsmLabel, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext}; declare_lint! { @@ -236,7 +235,7 @@ fn report_unsafe( &self, cx: &EarlyContext<'_>, span: Span, - decorate: impl for<'a> LintDiagnostic<'a, ()>, + decorate: impl for<'a> Diagnostic<'a, ()>, ) { // This comes from a macro that has `#[allow_internal_unsafe]`. if span.allows_unsafe() { @@ -980,15 +979,14 @@ fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { let attrs = cx.tcx.hir_attrs(it.hir_id()); match it.kind { hir::ItemKind::Fn { .. } => { - if let Some(attr_span) = - find_attr!(attrs, AttributeKind::ExportName {span, ..} => *span) - .or_else(|| find_attr!(attrs, AttributeKind::NoMangle(span) => *span)) + if let Some(attr_span) = find_attr!(attrs, ExportName {span, ..} => *span) + .or_else(|| find_attr!(attrs, NoMangle(span) => *span)) { self.check_no_mangle_on_generic_fn(cx, attr_span, it.owner_id.def_id); } } hir::ItemKind::Const(ident, generics, ..) => { - if find_attr!(attrs, AttributeKind::NoMangle(..)) { + if find_attr!(attrs, NoMangle(..)) { let suggestion = if generics.params.is_empty() && generics.where_clause_span.is_empty() { // account for "pub const" (#45562) @@ -1014,9 +1012,8 @@ fn check_impl_item(&mut self, cx: &LateContext<'_>, it: &hir::ImplItem<'_>) { let attrs = cx.tcx.hir_attrs(it.hir_id()); match it.kind { hir::ImplItemKind::Fn { .. } => { - if let Some(attr_span) = - find_attr!(attrs, AttributeKind::ExportName {span, ..} => *span) - .or_else(|| find_attr!(attrs, AttributeKind::NoMangle(span) => *span)) + if let Some(attr_span) = find_attr!(attrs, ExportName {span, ..} => *span) + .or_else(|| find_attr!(attrs, NoMangle(span) => *span)) { self.check_no_mangle_on_generic_fn(cx, attr_span, it.owner_id.def_id); } @@ -1122,12 +1119,10 @@ fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool { ); impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { - fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &hir::Attribute) { - if attr.has_name(sym::feature) - && let Some(items) = attr.meta_item_list() - { - for item in items { - cx.emit_span_lint(UNSTABLE_FEATURES, item.span(), BuiltinUnstableFeatures); + fn check_attributes(&mut self, cx: &LateContext<'_>, attrs: &[hir::Attribute]) { + if let Some(features) = find_attr!(attrs, Feature(features, _) => features) { + for feature in features { + cx.emit_span_lint(UNSTABLE_FEATURES, feature.span, BuiltinUnstableFeatures); } } } @@ -1178,7 +1173,7 @@ fn check_fn( if fn_kind.asyncness().is_async() && !cx.tcx.features().async_fn_track_caller() // Now, check if the function has the `#[track_caller]` attribute - && let Some(attr_span) = find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::TrackCaller(span) => *span) + && let Some(attr_span) = find_attr!(cx.tcx, def_id, TrackCaller(span) => *span) { cx.emit_span_lint( UNGATED_ASYNC_FN_TRACK_CALLER, @@ -2247,10 +2242,10 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), BuiltinExplicitOutlives { - count: bound_count, suggestion: BuiltinExplicitOutlivesSuggestion { spans: lint_spans, applicability, + count: bound_count, }, }, ); @@ -2325,8 +2320,9 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { if features.incomplete(name) { let note = rustc_feature::find_feature_issue(name, GateIssue::Language) .map(|n| BuiltinFeatureIssueNote { n }); - let help = - HAS_MIN_FEATURES.contains(&name).then_some(BuiltinIncompleteFeaturesHelp); + let help = HAS_MIN_FEATURES + .contains(&name) + .then_some(BuiltinIncompleteFeaturesHelp { name }); cx.emit_span_lint( INCOMPLETE_FEATURES, @@ -3188,3 +3184,62 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) { } } } + +declare_lint! { + /// The `internal_eq_trait_method_impls` lint detects manual + /// implementations of `Eq::assert_receiver_is_total_eq`. + /// + /// ### Example + /// + /// ```rust + /// #[derive(PartialEq)] + /// pub struct Foo; + /// + /// impl Eq for Foo { + /// fn assert_receiver_is_total_eq(&self) {} + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This method existed so that `#[derive(Eq)]` could check that all + /// fields of a type implement `Eq`. Other users were never supposed + /// to implement it and it was hidden from documentation. + /// + /// Unfortunately, it was not explicitly marked as unstable and some + /// people have now mistakenly assumed they had to implement this method. + /// + /// As the method is never called by the standard library, you can safely + /// remove any implementations of the method and just write `impl Eq for Foo {}`. + /// + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the future. See [issue #152336] for more details. + /// + /// [issue #152336]: https://github.com/rust-lang/rust/issues/152336 + pub INTERNAL_EQ_TRAIT_METHOD_IMPLS, + Warn, + "manual implementation of the internal `Eq::assert_receiver_is_total_eq` method", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #152336), + report_in_deps: false, + }; +} + +declare_lint_pass!(InternalEqTraitMethodImpls => [INTERNAL_EQ_TRAIT_METHOD_IMPLS]); + +impl<'tcx> LateLintPass<'tcx> for InternalEqTraitMethodImpls { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::ImplItem<'tcx>) { + if let ImplItemImplKind::Trait { defaultness: _, trait_item_def_id: Ok(trait_item_def_id) } = + item.impl_kind + && cx.tcx.is_diagnostic_item(sym::assert_receiver_is_total_eq, trait_item_def_id) + { + cx.emit_span_lint( + INTERNAL_EQ_TRAIT_METHOD_IMPLS, + item.span, + EqInternalMethodImplemented, + ); + } + } +} diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 561bdd1a2db6..d7a0a02f085a 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -6,13 +6,15 @@ use std::cell::Cell; use std::slice; +use rustc_abi as abi; use rustc_ast::BindingMode; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{Diag, LintBuffer, LintDiagnostic, MultiSpan}; +use rustc_errors::{Diagnostic, LintBuffer, MultiSpan}; use rustc_feature::Features; +use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; @@ -28,7 +30,6 @@ use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::{Ident, Span, Symbol, sym}; use tracing::debug; -use {rustc_abi as abi, rustc_hir as hir}; use self::TargetLint::*; use crate::levels::LintLevelsBuilder; @@ -515,66 +516,29 @@ pub trait LintContext { /// /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature #[track_caller] - fn opt_span_lint>( + fn opt_span_diag_lint>( &self, lint: &'static Lint, span: Option, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), + decorate: impl for<'a> Diagnostic<'a, ()>, ); - /// Emit a lint at `span` from a lint struct (some type that implements `LintDiagnostic`, - /// typically generated by `#[derive(LintDiagnostic)]`). + /// Emit a lint at `span` from a lint struct (some type that implements `Diagnostic`, + /// typically generated by `#[derive(Diagnostic)]`). + #[track_caller] fn emit_span_lint>( &self, lint: &'static Lint, span: S, - decorator: impl for<'a> LintDiagnostic<'a, ()>, + decorator: impl for<'a> Diagnostic<'a, ()>, ) { - self.opt_span_lint(lint, Some(span), |lint| { - decorator.decorate_lint(lint); - }); + self.opt_span_diag_lint(lint, Some(span), decorator); } - /// Emit a lint at `span` from a lazily-constructed lint struct (some type that implements - /// `LintDiagnostic`, typically generated by `#[derive(LintDiagnostic)]`). - fn emit_span_lint_lazy, L: for<'a> LintDiagnostic<'a, ()>>( - &self, - lint: &'static Lint, - span: S, - decorator: impl FnOnce() -> L, - ) { - self.opt_span_lint(lint, Some(span), |lint| { - let decorator = decorator(); - decorator.decorate_lint(lint); - }); - } - - /// Emit a lint at the appropriate level, with an associated span. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature - #[track_caller] - fn span_lint>( - &self, - lint: &'static Lint, - span: S, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), - ) { - self.opt_span_lint(lint, Some(span), decorate); - } - - /// Emit a lint from a lint struct (some type that implements `LintDiagnostic`, typically - /// generated by `#[derive(LintDiagnostic)]`). - fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> LintDiagnostic<'a, ()>) { - self.opt_span_lint(lint, None as Option, |lint| { - decorator.decorate_lint(lint); - }); - } - - /// Emit a lint at the appropriate level, with no associated span. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature - fn lint(&self, lint: &'static Lint, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>)) { - self.opt_span_lint(lint, None as Option, decorate); + /// Emit a lint from a lint struct (some type that implements `Diagnostic`, typically + /// generated by `#[derive(Diagnostic)]`). + fn emit_diag_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a, ()>) { + self.opt_span_diag_lint(lint, None as Option, decorator); } /// This returns the lint level for the given lint at the current location. @@ -630,17 +594,17 @@ fn sess(&self) -> &Session { self.tcx.sess } - fn opt_span_lint>( + fn opt_span_diag_lint>( &self, lint: &'static Lint, span: Option, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), + decorate: impl for<'a> Diagnostic<'a, ()>, ) { let hir_id = self.last_node_with_lint_attrs; match span { - Some(s) => self.tcx.node_span_lint(lint, hir_id, s, decorate), - None => self.tcx.node_lint(lint, hir_id, decorate), + Some(s) => self.tcx.emit_node_span_lint(lint, hir_id, s, decorate), + None => self.tcx.emit_node_lint(lint, hir_id, decorate), } } @@ -655,13 +619,13 @@ fn sess(&self) -> &Session { self.builder.sess() } - fn opt_span_lint>( + fn opt_span_diag_lint>( &self, lint: &'static Lint, span: Option, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), + decorator: impl for<'a> Diagnostic<'a, ()>, ) { - self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorate) + self.builder.opt_span_diag_lint(lint, span.map(|s| s.into()), decorator) } fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource { diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index 71ea801a408e..d162ae4b7764 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -1,5 +1,4 @@ use rustc_ast::visit::{visit_opt, walk_list}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; @@ -268,9 +267,8 @@ fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { && let ty = cx.typeck_results().expr_ty(receiver) && owns_allocation(cx.tcx, ty) && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && find_attr!(cx.tcx.get_all_attrs(fn_id), AttributeKind::RustcAsPtr(_)) + && find_attr!(cx.tcx, fn_id, RustcAsPtr(_)) { - // FIXME: use `emit_node_lint` when `#[primary_span]` is added. cx.tcx.emit_node_span_lint( DANGLING_POINTERS_FROM_TEMPORARIES, expr.hir_id, diff --git a/compiler/rustc_lint/src/default_could_be_derived.rs b/compiler/rustc_lint/src/default_could_be_derived.rs index 8f028b1d96a2..0db2f6a3565c 100644 --- a/compiler/rustc_lint/src/default_could_be_derived.rs +++ b/compiler/rustc_lint/src/default_could_be_derived.rs @@ -180,7 +180,7 @@ fn mk_lint( if removed_all_fields { let msg = "to avoid divergence in behavior between `Struct { .. }` and \ `::default()`, derive the `Default`"; - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( msg, vec![ (tcx.def_span(type_def_id).shrink_to_lo(), "#[derive(Default)] ".to_string()), diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index 703f757abd50..3e1a418c8c96 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -87,7 +87,10 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { ty::AssocTag::Type, item.owner_id.to_def_id(), ) - .map(|label| SupertraitAsDerefTargetLabel { label: tcx.def_span(label.def_id) }); + .map(|label| SupertraitAsDerefTargetLabel { + label: tcx.def_span(label.def_id), + self_ty, + }); let span = tcx.def_span(item.owner_id.def_id); cx.emit_span_lint( DEREF_INTO_DYN_SUPERTRAIT, diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/disallowed_pass_by_ref.rs similarity index 67% rename from compiler/rustc_lint/src/pass_by_value.rs rename to compiler/rustc_lint/src/disallowed_pass_by_ref.rs index 3823c9aa1861..2a572a5a76bd 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/disallowed_pass_by_ref.rs @@ -1,37 +1,36 @@ -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::Res; use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind, find_attr}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::lints::PassByValueDiag; +use crate::lints::DisallowedPassByRefDiag; use crate::{LateContext, LateLintPass, LintContext}; declare_tool_lint! { - /// The `rustc_pass_by_value` lint marks a type with `#[rustc_pass_by_value]` requiring it to - /// always be passed by value. This is usually used for types that are thin wrappers around - /// references, so there is no benefit to an extra layer of indirection. (Example: `Ty` which - /// is a reference to an `Interned`) - pub rustc::PASS_BY_VALUE, + /// The `disallowed_pass_by_ref` lint detects if types marked with `#[rustc_pass_by_value]` are + /// passed by reference. Types with this marker are usually thin wrappers around references, so + /// there is no benefit to an extra layer of indirection. (Example: `Ty` which is a reference + /// to an `Interned`) + pub rustc::DISALLOWED_PASS_BY_REF, Warn, "pass by reference of a type flagged as `#[rustc_pass_by_value]`", report_in_external_macro: true } -declare_lint_pass!(PassByValue => [PASS_BY_VALUE]); +declare_lint_pass!(DisallowedPassByRef => [DISALLOWED_PASS_BY_REF]); -impl<'tcx> LateLintPass<'tcx> for PassByValue { +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 }) => { if cx.tcx.trait_impl_of_assoc(ty.hir_id.owner.to_def_id()).is_some() { return; } - if let Some(t) = path_for_pass_by_value(cx, inner_ty) { + if let Some(t) = path_for_rustc_pass_by_value(cx, inner_ty) { cx.emit_span_lint( - PASS_BY_VALUE, + DISALLOWED_PASS_BY_REF, ty.span, - PassByValueDiag { ty: t, suggestion: ty.span }, + DisallowedPassByRefDiag { ty: t, suggestion: ty.span }, ); } } @@ -40,22 +39,17 @@ fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) } } -fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option { +fn path_for_rustc_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option { if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind { match path.res { - Res::Def(_, def_id) - if find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcPassByValue(_)) => - { + Res::Def(_, def_id) if find_attr!(cx.tcx, def_id, RustcPassByValue(_)) => { let name = cx.tcx.item_ident(def_id); let path_segment = path.segments.last().unwrap(); return Some(format!("{}{}", name, gen_args(cx, path_segment))); } Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => { if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind() { - if find_attr!( - cx.tcx.get_all_attrs(adt.did()), - AttributeKind::RustcPassByValue(_) - ) { + if find_attr!(cx.tcx, adt.did(), RustcPassByValue(_)) { return Some(cx.tcx.def_path_str_with_args(adt.did(), args)); } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 0295df2feca5..92232c7d230b 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -15,6 +15,7 @@ use rustc_span::{Ident, Span}; use tracing::debug; +use crate::DecorateBuiltinLint; use crate::context::{EarlyContext, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; @@ -36,12 +37,22 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { fn check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; - self.context.opt_span_lint(lint_id.lint, span, |diag| match diagnostic { + match diagnostic { DecorateDiagCompat::Builtin(b) => { - diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, b, diag); + self.context.opt_span_diag_lint( + lint_id.lint, + span, + DecorateBuiltinLint { + sess: self.context.sess(), + tcx: self.tcx, + diagnostic: b, + }, + ); } - DecorateDiagCompat::Dynamic(d) => d.decorate_lint_box(diag), - }); + DecorateDiagCompat::Dynamic(d) => { + self.context.opt_span_diag_lint(lint_id.lint, span, d); + } + } } } diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 3da2b1bf4069..458553fa747c 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -2,9 +2,10 @@ use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_errors::{ - Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion, + Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, Level, + elided_lifetime_in_path_suggestion, }; -use rustc_hir::lints::AttributeLintKind; +use rustc_hir::lints::{AttributeLintKind, FormatWarning}; use rustc_middle::middle::stability; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -16,435 +17,487 @@ mod check_cfg; -pub fn decorate_builtin_lint( - sess: &Session, - tcx: Option>, - diagnostic: BuiltinLintDiag, - diag: &mut Diag<'_, ()>, -) { - match diagnostic { - BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => { - let spans: Vec<_> = content - .char_indices() - .filter_map(|(i, c)| { - TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { - let lo = comment_span.lo() + BytePos(2 + i as u32); - (c, comment_span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) +/// This is a diagnostic struct that will decorate a `BuiltinLintDiag` +/// Directly creating the lint structs is expensive, using this will only decorate the lint structs when needed. +pub struct DecorateBuiltinLint<'sess, 'tcx> { + pub sess: &'sess Session, + pub tcx: Option>, + pub diagnostic: BuiltinLintDiag, +} + +impl<'a> Diagnostic<'a, ()> for DecorateBuiltinLint<'_, '_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + match self.diagnostic { + BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => { + let spans: Vec<_> = content + .char_indices() + .filter_map(|(i, c)| { + TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { + let lo = comment_span.lo() + BytePos(2 + i as u32); + (c, comment_span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) + }) }) - }) - .collect(); - let characters = spans - .iter() - .map(|&(c, span)| lints::UnicodeCharNoteSub { span, c_debug: format!("{c:?}") }) - .collect(); - let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion { - spans: spans.iter().map(|(_c, span)| *span).collect(), - }); + .collect(); + let characters = spans + .iter() + .map(|&(c, span)| lints::UnicodeCharNoteSub { span, c_debug: format!("{c:?}") }) + .collect(); + let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion { + spans: spans.iter().map(|(_c, span)| *span).collect(), + }); - lints::UnicodeTextFlow { - comment_span, - characters, - suggestions, - num_codepoints: spans.len(), - } - .decorate_lint(diag); - } - BuiltinLintDiag::AbsPathWithModule(mod_span) => { - let (replacement, applicability) = match sess.source_map().span_to_snippet(mod_span) { - Ok(ref s) => { - // FIXME(Manishearth) ideally the emitting code - // can tell us whether or not this is global - let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; - - (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) + lints::UnicodeTextFlow { + comment_span, + characters, + suggestions, + num_codepoints: spans.len(), } - Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), - }; - lints::AbsPathWithModule { - sugg: lints::AbsPathWithModuleSugg { span: mod_span, applicability, replacement }, + .into_diag(dcx, level) } - .decorate_lint(diag); - } - BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => { - lints::ElidedLifetimesInPaths { + BuiltinLintDiag::AbsPathWithModule(mod_span) => { + let (replacement, applicability) = + match self.sess.source_map().span_to_snippet(mod_span) { + Ok(ref s) => { + // FIXME(Manishearth) ideally the emitting code + // can tell us whether or not this is global + let opt_colon = + if s.trim_start().starts_with("::") { "" } else { "::" }; + + (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) + } + Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), + }; + lints::AbsPathWithModule { + sugg: lints::AbsPathWithModuleSugg { + span: mod_span, + applicability, + replacement, + }, + } + .into_diag(dcx, level) + } + BuiltinLintDiag::ElidedLifetimesInPaths( + n, + path_span, + incl_angl_brckt, + insertion_span, + ) => lints::ElidedLifetimesInPaths { subdiag: elided_lifetime_in_path_suggestion( - sess.source_map(), + self.sess.source_map(), n, path_span, incl_angl_brckt, insertion_span, ), } - .decorate_lint(diag); - } - BuiltinLintDiag::UnusedImports { - remove_whole_use, - num_to_remove, - remove_spans, - test_module_span, - span_snippets, - } => { - let sugg = if remove_whole_use { - lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] } - } else { - lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove } - }; - let test_module_span = - test_module_span.map(|span| sess.source_map().guess_head_span(span)); - - lints::UnusedImports { - sugg, + .into_diag(dcx, level), + BuiltinLintDiag::UnusedImports { + remove_whole_use, + num_to_remove, + remove_spans, test_module_span, - num_snippets: span_snippets.len(), - span_snippets: DiagArgValue::StrListSepByAnd( - span_snippets.into_iter().map(Cow::Owned).collect(), - ), - } - .decorate_lint(diag); - } - BuiltinLintDiag::RedundantImport(spans, ident) => { - let subs = spans - .into_iter() - .map(|(span, is_imported)| { - (match (span.is_dummy(), is_imported) { - (false, true) => lints::RedundantImportSub::ImportedHere, - (false, false) => lints::RedundantImportSub::DefinedHere, - (true, true) => lints::RedundantImportSub::ImportedPrelude, - (true, false) => lints::RedundantImportSub::DefinedPrelude, - })(span) - }) - .collect(); - lints::RedundantImport { subs, ident }.decorate_lint(diag); - } - BuiltinLintDiag::DeprecatedMacro { - suggestion, - suggestion_span, - note, - path, - since_kind, - } => { - let sub = suggestion.map(|suggestion| stability::DeprecationSuggestion { - span: suggestion_span, - kind: "macro".to_owned(), - suggestion, - }); + span_snippets, + } => { + let sugg = if remove_whole_use { + lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] } + } else { + lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove } + }; + let test_module_span = + test_module_span.map(|span| self.sess.source_map().guess_head_span(span)); - stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind } - .decorate_lint(diag); - } - BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => { - let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span }; - if is_foreign { - lints::PatternsInFnsWithoutBody::Foreign { sub } - } else { - lints::PatternsInFnsWithoutBody::Bodiless { sub } + lints::UnusedImports { + sugg, + test_module_span, + num_snippets: span_snippets.len(), + span_snippets: DiagArgValue::StrListSepByAnd( + span_snippets.into_iter().map(Cow::Owned).collect(), + ), + } + .into_diag(dcx, level) } - .decorate_lint(diag); - } - BuiltinLintDiag::ReservedPrefix(label_span, prefix) => { - lints::ReservedPrefix { + BuiltinLintDiag::RedundantImport(spans, ident) => { + let subs = spans + .into_iter() + .map(|(span, is_imported)| match (span.is_dummy(), is_imported) { + (false, true) => lints::RedundantImportSub::ImportedHere { span, ident }, + (false, false) => lints::RedundantImportSub::DefinedHere { span, ident }, + (true, true) => lints::RedundantImportSub::ImportedPrelude { span, ident }, + (true, false) => lints::RedundantImportSub::DefinedPrelude { span, ident }, + }) + .collect(); + lints::RedundantImport { subs, ident }.into_diag(dcx, level) + } + BuiltinLintDiag::DeprecatedMacro { + suggestion, + suggestion_span, + note, + path, + since_kind, + } => { + let sub = suggestion.map(|suggestion| stability::DeprecationSuggestion { + span: suggestion_span, + kind: "macro".to_owned(), + suggestion, + }); + + stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind } + .into_diag(dcx, level) + } + BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => { + let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span }; + if is_foreign { + lints::PatternsInFnsWithoutBody::Foreign { sub } + } else { + lints::PatternsInFnsWithoutBody::Bodiless { sub } + } + .into_diag(dcx, level) + } + BuiltinLintDiag::ReservedPrefix(label_span, prefix) => lints::ReservedPrefix { label: label_span, suggestion: label_span.shrink_to_hi(), prefix, } - .decorate_lint(diag); - } - BuiltinLintDiag::RawPrefix(label_span) => { - lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() } - .decorate_lint(diag); - } - BuiltinLintDiag::ReservedString { is_string, suggestion } => { - if is_string { - lints::ReservedString { suggestion }.decorate_lint(diag); - } else { - lints::ReservedMultihash { suggestion }.decorate_lint(diag); + .into_diag(dcx, level), + BuiltinLintDiag::RawPrefix(label_span) => { + lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() } + .into_diag(dcx, level) } - } - BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => { - lints::BreakWithLabelAndLoop { + BuiltinLintDiag::ReservedString { is_string, suggestion } => { + if is_string { + lints::ReservedString { suggestion }.into_diag(dcx, level) + } else { + lints::ReservedMultihash { suggestion }.into_diag(dcx, level) + } + } + BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => lints::BreakWithLabelAndLoop { sub: lints::BreakWithLabelAndLoopSub { left: sugg_span.shrink_to_lo(), right: sugg_span.shrink_to_hi(), }, } - .decorate_lint(diag); - } - BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => { - let suggestion = match sugg { - Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd { - left: left_sp, - right: right_sp, - sugg, - }, - None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp }, - }; - lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag); - } - BuiltinLintDiag::SingleUseLifetime { - param_span, - use_span: Some((use_span, elide)), - deletion_span, - ident, - } => { - debug!(?param_span, ?use_span, ?deletion_span); - let suggestion = if let Some(deletion_span) = deletion_span { - let (use_span, replace_lt) = if elide { - let use_span = sess.source_map().span_extend_while_whitespace(use_span); - (use_span, String::new()) - } else { - (use_span, "'_".to_owned()) + .into_diag(dcx, level), + BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => { + let suggestion = match sugg { + Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd { + left: left_sp, + right: right_sp, + sugg, + }, + None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp }, }; - debug!(?deletion_span, ?use_span); - - // issue 107998 for the case such as a wrong function pointer type - // `deletion_span` is empty and there is no need to report lifetime uses here - let deletion_span = - if deletion_span.is_empty() { None } else { Some(deletion_span) }; - Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt }) - } else { - None - }; - - lints::SingleUseLifetime { suggestion, param_span, use_span, ident } - .decorate_lint(diag); - } - BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => { - lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag); - } - BuiltinLintDiag::NamedArgumentUsedPositionally { - position_sp_to_replace, - position_sp_for_msg, - named_arg_sp, - named_arg_name, - is_formatting_arg, - } => { - let (suggestion, name) = if let Some(positional_arg_to_replace) = position_sp_to_replace - { - let mut name = named_arg_name.clone(); - if is_formatting_arg { - name.push('$') - }; - let span_to_replace = if let Ok(positional_arg_content) = - sess.source_map().span_to_snippet(positional_arg_to_replace) - && positional_arg_content.starts_with(':') - { - positional_arg_to_replace.shrink_to_lo() - } else { - positional_arg_to_replace - }; - (Some(span_to_replace), name) - } else { - (None, String::new()) - }; - - lints::NamedArgumentUsedPositionally { - named_arg_sp, - position_label_sp: position_sp_for_msg, - suggestion, - name, - named_arg_name, + lints::DeprecatedWhereClauseLocation { suggestion }.into_diag(dcx, level) } - .decorate_lint(diag); - } - BuiltinLintDiag::AmbiguousGlobReexports { - name, - namespace, - first_reexport_span, - duplicate_reexport_span, - } => { - lints::AmbiguousGlobReexports { + BuiltinLintDiag::SingleUseLifetime { + param_span, + use_span: Some((use_span, elide)), + deletion_span, + ident, + } => { + debug!(?param_span, ?use_span, ?deletion_span); + let suggestion = if let Some(deletion_span) = deletion_span { + let (use_span, replace_lt) = if elide { + let use_span = + self.sess.source_map().span_extend_while_whitespace(use_span); + (use_span, String::new()) + } else { + (use_span, "'_".to_owned()) + }; + debug!(?deletion_span, ?use_span); + + // issue 107998 for the case such as a wrong function pointer type + // `deletion_span` is empty and there is no need to report lifetime uses here + let deletion_span = + if deletion_span.is_empty() { None } else { Some(deletion_span) }; + Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt }) + } else { + None + }; + + lints::SingleUseLifetime { suggestion, param_span, use_span, ident } + .into_diag(dcx, level) + } + BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => { + lints::UnusedLifetime { deletion_span, ident }.into_diag(dcx, level) + } + BuiltinLintDiag::NamedArgumentUsedPositionally { + position_sp_to_replace, + position_sp_for_msg, + named_arg_sp, + named_arg_name, + is_formatting_arg, + } => { + let (suggestion, name) = + if let Some(positional_arg_to_replace) = position_sp_to_replace { + let mut name = named_arg_name.clone(); + if is_formatting_arg { + name.push('$') + }; + let span_to_replace = if let Ok(positional_arg_content) = + self.sess.source_map().span_to_snippet(positional_arg_to_replace) + && positional_arg_content.starts_with(':') + { + positional_arg_to_replace.shrink_to_lo() + } else { + positional_arg_to_replace + }; + (Some(span_to_replace), name) + } else { + (None, String::new()) + }; + + lints::NamedArgumentUsedPositionally { + named_arg_sp, + position_label_sp: position_sp_for_msg, + suggestion, + name, + named_arg_name, + } + .into_diag(dcx, level) + } + BuiltinLintDiag::AmbiguousGlobReexports { + name, + namespace, + first_reexport_span, + duplicate_reexport_span, + } => lints::AmbiguousGlobReexports { first_reexport: first_reexport_span, duplicate_reexport: duplicate_reexport_span, name, namespace, } - .decorate_lint(diag); - } - BuiltinLintDiag::HiddenGlobReexports { - name, - namespace, - glob_reexport_span, - private_item_span, - } => { - lints::HiddenGlobReexports { + .into_diag(dcx, level), + BuiltinLintDiag::HiddenGlobReexports { + name, + namespace, + glob_reexport_span, + private_item_span, + } => lints::HiddenGlobReexports { glob_reexport: glob_reexport_span, private_item: private_item_span, name, namespace, } - .decorate_lint(diag); - } - BuiltinLintDiag::UnusedQualifications { removal_span } => { - lints::UnusedQualifications { removal_span }.decorate_lint(diag); - } - BuiltinLintDiag::AssociatedConstElidedLifetime { - elided, - span: lt_span, - lifetimes_in_scope, - } => { - let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span }; - let code = if elided { "'static " } else { "'static" }; - lints::AssociatedConstElidedLifetime { - span: lt_span, - code, + .into_diag(dcx, level), + BuiltinLintDiag::UnusedQualifications { removal_span } => { + lints::UnusedQualifications { removal_span }.into_diag(dcx, level) + } + BuiltinLintDiag::AssociatedConstElidedLifetime { elided, + span: lt_span, lifetimes_in_scope, + } => { + let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span }; + let code = if elided { "'static " } else { "'static" }; + lints::AssociatedConstElidedLifetime { + span: lt_span, + code, + elided, + lifetimes_in_scope, + } + .into_diag(dcx, level) } - .decorate_lint(diag); - } - BuiltinLintDiag::UnreachableCfg { span, wildcard_span } => match wildcard_span { - Some(wildcard_span) => { - lints::UnreachableCfgSelectPredicateWildcard { span, wildcard_span } - .decorate_lint(diag) - } - None => lints::UnreachableCfgSelectPredicate { span }.decorate_lint(diag), - }, + BuiltinLintDiag::UnreachableCfg { span, wildcard_span } => match wildcard_span { + Some(wildcard_span) => { + lints::UnreachableCfgSelectPredicateWildcard { span, wildcard_span } + .into_diag(dcx, level) + } + None => lints::UnreachableCfgSelectPredicate { span }.into_diag(dcx, level), + }, - BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { - lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) + BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { + lints::UnusedCrateDependency { extern_crate, local_crate }.into_diag(dcx, level) + } + BuiltinLintDiag::UnusedVisibility(span) => { + lints::UnusedVisibility { span }.into_diag(dcx, level) + } + BuiltinLintDiag::AttributeLint(kind) => { + DecorateAttrLint { sess: self.sess, tcx: self.tcx, diagnostic: &kind } + .into_diag(dcx, level) + } } - BuiltinLintDiag::UnusedVisibility(span) => { - lints::UnusedVisibility { span }.decorate_lint(diag) - } - BuiltinLintDiag::AttributeLint(kind) => decorate_attribute_lint(sess, tcx, &kind, diag), } } -pub fn decorate_attribute_lint( - sess: &Session, - tcx: Option>, - kind: &AttributeLintKind, - diag: &mut Diag<'_, ()>, -) { - match kind { - &AttributeLintKind::UnusedDuplicate { this, other, warning } => { - lints::UnusedDuplicate { this, other, warning }.decorate_lint(diag) - } - AttributeLintKind::IllFormedAttributeInput { suggestions, docs } => { - lints::IllFormedAttributeInput { - num_suggestions: suggestions.len(), - suggestions: DiagArgValue::StrListSepByAnd( - suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), - ), - has_docs: docs.is_some(), - docs: docs.unwrap_or(""), +/// This is a diagnostic struct that will decorate a `AttributeLintKind` +/// Directly creating the lint structs is expensive, using this will only decorate the lint structs when needed. +pub struct DecorateAttrLint<'a, 'sess, 'tcx> { + pub sess: &'sess Session, + pub tcx: Option>, + pub diagnostic: &'a AttributeLintKind, +} + +impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + match self.diagnostic { + &AttributeLintKind::UnusedDuplicate { this, other, warning } => { + lints::UnusedDuplicate { this, other, warning }.into_diag(dcx, level) } - .decorate_lint(diag) - } - AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => { - lints::EmptyAttributeList { - attr_span: *first_span, - attr_path: attr_path.clone(), - valid_without_list: *valid_without_list, + AttributeLintKind::IllFormedAttributeInput { suggestions, docs } => { + lints::IllFormedAttributeInput { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + has_docs: docs.is_some(), + docs: docs.unwrap_or(""), + } + .into_diag(dcx, level) } - .decorate_lint(diag) - } - AttributeLintKind::InvalidTarget { name, target, applied, only, attr_span } => { - lints::InvalidTargetLint { - name: name.clone(), + AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => { + lints::EmptyAttributeList { + attr_span: *first_span, + attr_path: attr_path.clone(), + valid_without_list: *valid_without_list, + } + .into_diag(dcx, level) + } + AttributeLintKind::InvalidTarget { name, target, applied, only, attr_span } => { + lints::InvalidTargetLint { + name: name.clone(), + target, + applied: DiagArgValue::StrListSepByAnd( + applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(), + ), + only, + attr_span: *attr_span, + } + .into_diag(dcx, level) + } + &AttributeLintKind::InvalidStyle { + ref name, + is_used_as_inner, target, - applied: DiagArgValue::StrListSepByAnd( - applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(), - ), - only, - attr_span: *attr_span, - } - .decorate_lint(diag) - } - &AttributeLintKind::InvalidStyle { ref name, is_used_as_inner, target, target_span } => { - lints::InvalidAttrStyle { + target_span, + } => lints::InvalidAttrStyle { name: name.clone(), is_used_as_inner, target_span: (!is_used_as_inner).then_some(target_span), target, } - .decorate_lint(diag) - } - &AttributeLintKind::UnsafeAttrOutsideUnsafe { attribute_name_span, sugg_spans } => { - lints::UnsafeAttrOutsideUnsafeLint { - span: attribute_name_span, - suggestion: sugg_spans - .map(|(left, right)| lints::UnsafeAttrOutsideUnsafeSuggestion { left, right }), + .into_diag(dcx, level), + &AttributeLintKind::UnsafeAttrOutsideUnsafe { attribute_name_span, sugg_spans } => { + lints::UnsafeAttrOutsideUnsafeLint { + span: attribute_name_span, + suggestion: sugg_spans.map(|(left, right)| { + lints::UnsafeAttrOutsideUnsafeSuggestion { left, right } + }), + } + .into_diag(dcx, level) + } + &AttributeLintKind::UnexpectedCfgName(name, value) => { + check_cfg::unexpected_cfg_name(self.sess, self.tcx, name, value) + .into_diag(dcx, level) + } + &AttributeLintKind::UnexpectedCfgValue(name, value) => { + check_cfg::unexpected_cfg_value(self.sess, self.tcx, name, value) + .into_diag(dcx, level) + } + &AttributeLintKind::DuplicateDocAlias { first_definition } => { + lints::DocAliasDuplicated { first_defn: first_definition }.into_diag(dcx, level) + } + + &AttributeLintKind::DocAutoCfgExpectsHideOrShow => { + lints::DocAutoCfgExpectsHideOrShow.into_diag(dcx, level) + } + + &AttributeLintKind::AmbiguousDeriveHelpers => { + lints::AmbiguousDeriveHelpers.into_diag(dcx, level) + } + + &AttributeLintKind::DocAutoCfgHideShowUnexpectedItem { attr_name } => { + lints::DocAutoCfgHideShowUnexpectedItem { attr_name }.into_diag(dcx, level) + } + + &AttributeLintKind::DocAutoCfgHideShowExpectsList { attr_name } => { + lints::DocAutoCfgHideShowExpectsList { attr_name }.into_diag(dcx, level) + } + + &AttributeLintKind::DocInvalid => lints::DocInvalid.into_diag(dcx, level), + + &AttributeLintKind::DocUnknownInclude { span, inner, value } => { + lints::DocUnknownInclude { + inner, + value, + sugg: (span, Applicability::MaybeIncorrect), + } + .into_diag(dcx, level) + } + + &AttributeLintKind::DocUnknownSpotlight { span } => { + lints::DocUnknownSpotlight { sugg_span: span }.into_diag(dcx, level) + } + + &AttributeLintKind::DocUnknownPasses { name, span } => { + lints::DocUnknownPasses { name, note_span: span }.into_diag(dcx, level) + } + + &AttributeLintKind::DocUnknownPlugins { span } => { + lints::DocUnknownPlugins { label_span: span }.into_diag(dcx, level) + } + + &AttributeLintKind::DocUnknownAny { name } => { + lints::DocUnknownAny { name }.into_diag(dcx, level) + } + + &AttributeLintKind::DocAutoCfgWrongLiteral => { + lints::DocAutoCfgWrongLiteral.into_diag(dcx, level) + } + + &AttributeLintKind::DocTestTakesList => lints::DocTestTakesList.into_diag(dcx, level), + + &AttributeLintKind::DocTestUnknown { name } => { + lints::DocTestUnknown { name }.into_diag(dcx, level) + } + + &AttributeLintKind::DocTestLiteral => lints::DocTestLiteral.into_diag(dcx, level), + + &AttributeLintKind::AttrCrateLevelOnly => { + lints::AttrCrateLevelOnly.into_diag(dcx, level) + } + + &AttributeLintKind::DoNotRecommendDoesNotExpectArgs => { + lints::DoNotRecommendDoesNotExpectArgs.into_diag(dcx, level) + } + + &AttributeLintKind::CrateTypeUnknown { span, suggested } => lints::UnknownCrateTypes { + sugg: suggested.map(|s| lints::UnknownCrateTypesSuggestion { span, snippet: s }), + } + .into_diag(dcx, level), + + &AttributeLintKind::MalformedDoc => lints::MalformedDoc.into_diag(dcx, level), + + &AttributeLintKind::ExpectedNoArgs => lints::ExpectedNoArgs.into_diag(dcx, level), + + &AttributeLintKind::ExpectedNameValue => lints::ExpectedNameValue.into_diag(dcx, level), + &AttributeLintKind::MalformedOnUnimplementedAttr { span } => { + lints::MalformedOnUnimplementedAttrLint { span }.into_diag(dcx, level) + } + &AttributeLintKind::MalformedOnConstAttr { span } => { + lints::MalformedOnConstAttrLint { span }.into_diag(dcx, level) + } + AttributeLintKind::MalformedDiagnosticFormat { warning } => match warning { + FormatWarning::PositionalArgument { .. } => { + lints::DisallowedPositionalArgument.into_diag(dcx, level) + } + FormatWarning::InvalidSpecifier { .. } => { + lints::InvalidFormatSpecifier.into_diag(dcx, level) + } + }, + AttributeLintKind::DiagnosticWrappedParserError { description, label, span } => { + lints::WrappedParserError { description, label, span: *span }.into_diag(dcx, level) + } + &AttributeLintKind::IgnoredDiagnosticOption { option_name, first_span, later_span } => { + lints::IgnoredDiagnosticOption { option_name, first_span, later_span } + .into_diag(dcx, level) + } + &AttributeLintKind::MissingOptionsForOnUnimplemented => { + lints::MissingOptionsForOnUnimplementedAttr.into_diag(dcx, level) + } + &AttributeLintKind::MissingOptionsForOnConst => { + lints::MissingOptionsForOnConstAttr.into_diag(dcx, level) } - .decorate_lint(diag) } - &AttributeLintKind::UnexpectedCfgName(name, value) => { - check_cfg::unexpected_cfg_name(sess, tcx, name, value).decorate_lint(diag) - } - &AttributeLintKind::UnexpectedCfgValue(name, value) => { - check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag) - } - &AttributeLintKind::DuplicateDocAlias { first_definition } => { - lints::DocAliasDuplicated { first_defn: first_definition }.decorate_lint(diag) - } - - &AttributeLintKind::DocAutoCfgExpectsHideOrShow => { - lints::DocAutoCfgExpectsHideOrShow.decorate_lint(diag) - } - - &AttributeLintKind::AmbiguousDeriveHelpers => { - lints::AmbiguousDeriveHelpers.decorate_lint(diag) - } - - &AttributeLintKind::DocAutoCfgHideShowUnexpectedItem { attr_name } => { - lints::DocAutoCfgHideShowUnexpectedItem { attr_name }.decorate_lint(diag) - } - - &AttributeLintKind::DocAutoCfgHideShowExpectsList { attr_name } => { - lints::DocAutoCfgHideShowExpectsList { attr_name }.decorate_lint(diag) - } - - &AttributeLintKind::DocInvalid => { lints::DocInvalid }.decorate_lint(diag), - - &AttributeLintKind::DocUnknownInclude { span, inner, value } => { - lints::DocUnknownInclude { inner, value, sugg: (span, Applicability::MaybeIncorrect) } - } - .decorate_lint(diag), - - &AttributeLintKind::DocUnknownSpotlight { span } => { - lints::DocUnknownSpotlight { sugg_span: span }.decorate_lint(diag) - } - - &AttributeLintKind::DocUnknownPasses { name, span } => { - lints::DocUnknownPasses { name, note_span: span }.decorate_lint(diag) - } - - &AttributeLintKind::DocUnknownPlugins { span } => { - lints::DocUnknownPlugins { label_span: span }.decorate_lint(diag) - } - - &AttributeLintKind::DocUnknownAny { name } => { - lints::DocUnknownAny { name }.decorate_lint(diag) - } - - &AttributeLintKind::DocAutoCfgWrongLiteral => { - lints::DocAutoCfgWrongLiteral.decorate_lint(diag) - } - - &AttributeLintKind::DocTestTakesList => lints::DocTestTakesList.decorate_lint(diag), - - &AttributeLintKind::DocTestUnknown { name } => { - lints::DocTestUnknown { name }.decorate_lint(diag) - } - - &AttributeLintKind::DocTestLiteral => lints::DocTestLiteral.decorate_lint(diag), - - &AttributeLintKind::AttrCrateLevelOnly => lints::AttrCrateLevelOnly.decorate_lint(diag), - - &AttributeLintKind::DoNotRecommendDoesNotExpectArgs => { - lints::DoNotRecommendDoesNotExpectArgs.decorate_lint(diag) - } - - &AttributeLintKind::CrateTypeUnknown { span, suggested } => lints::UnknownCrateTypes { - sugg: suggested.map(|s| lints::UnknownCrateTypesSuggestion { span, snippet: s }), - } - .decorate_lint(diag), - - &AttributeLintKind::MalformedDoc => lints::MalformedDoc.decorate_lint(diag), - - &AttributeLintKind::ExpectedNoArgs => lints::ExpectedNoArgs.decorate_lint(diag), - - &AttributeLintKind::ExpectedNameValue => lints::ExpectedNameValue.decorate_lint(diag), } } diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index ad73e15e31f3..d234c6863103 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -2,7 +2,6 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::find_attr; use rustc_middle::query::Providers; @@ -183,11 +182,7 @@ fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName { // information, we could have codegen_fn_attrs also give span information back for // where the attribute was defined. However, until this is found to be a // bottleneck, this does just fine. - ( - overridden_link_name, - find_attr!(tcx.get_all_attrs(fi), AttributeKind::LinkName {span, ..} => *span) - .unwrap(), - ) + (overridden_link_name, find_attr!(tcx, fi, LinkName {span, ..} => *span).unwrap()) }) { SymbolName::Link(overridden_link_name, overridden_link_name_span) diff --git a/compiler/rustc_lint/src/gpukernel_abi.rs b/compiler/rustc_lint/src/gpukernel_abi.rs index dbdf5b6e7955..4fb26739cd28 100644 --- a/compiler/rustc_lint/src/gpukernel_abi.rs +++ b/compiler/rustc_lint/src/gpukernel_abi.rs @@ -1,7 +1,6 @@ use std::iter; use rustc_abi::ExternAbi; -use rustc_hir::attrs::AttributeKind; use rustc_hir::{self as hir, find_attr}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_session::{declare_lint, declare_lint_pass}; @@ -184,10 +183,7 @@ fn check_fn( } // Check for no_mangle/export_name, so the kernel can be found when querying the compiled object for the kernel function by name - if !find_attr!( - cx.tcx.get_all_attrs(id), - AttributeKind::NoMangle(..) | AttributeKind::ExportName { .. } - ) { + if !find_attr!(cx.tcx, id, NoMangle(..) | ExportName { .. }) { cx.emit_span_lint(MISSING_GPU_KERNEL_EXPORT_NAME, span, MissingGpuKernelExportName); } } diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 7899d4690ea5..cc8229b3f387 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -5,7 +5,7 @@ use rustc_ast::Recovered; use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic, SuggestionStyle, msg}; use rustc_hir::{self as hir, HirIdSet}; -use rustc_macros::{LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::significant_drop_order::{ extract_component_with_significant_dtor, ty_dtor_span, @@ -302,7 +302,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`if let` assigns a shorter lifetime since Edition 2024")] struct IfLetRescopeLint { #[subdiagnostic] @@ -354,9 +354,8 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { .chain(repeat_n('}', closing_brackets.count)) .collect(), )); - let msg = diag.eagerly_translate(msg!( - "a `match` with a single arm can preserve the drop order up to Edition 2021" - )); + let msg = + msg!("a `match` with a single arm can preserve the drop order up to Edition 2021"); diag.multipart_suggestion_with_style( msg, suggestions, diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 5e5f9b6f097f..a51d603133f3 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -1,15 +1,15 @@ use std::cell::LazyCell; +use std::debug_assert_matches; -use rustc_data_structures::debug_assert_matches; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{LintDiagnostic, Subdiagnostic, msg}; +use rustc_errors::{Diagnostic, Subdiagnostic, msg}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_macros::LintDiagnostic; +use rustc_macros::Diagnostic; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::ty::relate::{ Relate, RelateResult, TypeRelation, relate_args_with_variances, structurally_relate_consts, @@ -308,7 +308,7 @@ fn visit_ty(&mut self, t: Ty<'tcx>) { return true; }; // We only computed variance of lifetimes... - debug_assert_matches!(self.tcx.def_kind(def_id), DefKind::LifetimeParam); + debug_assert_matches!(self.tcx.def_kind(*def_id), DefKind::LifetimeParam); let uncaptured = match *kind { ParamKind::Early(name, index) => ty::Region::new_early_param( self.tcx, @@ -342,7 +342,7 @@ fn visit_ty(&mut self, t: Ty<'tcx>) { let uncaptured_spans: Vec<_> = uncaptured_args .into_iter() - .map(|(def_id, _)| self.tcx.def_span(def_id)) + .map(|(&def_id, _)| self.tcx.def_span(def_id)) .collect(); self.tcx.emit_node_span_lint( @@ -433,11 +433,17 @@ struct ImplTraitOvercapturesLint<'tcx> { suggestion: Option, } -impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { - fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { - diag.primary_message(msg!( - "`{$self_ty}` will capture more lifetimes than possibly intended in edition 2024" - )); +impl<'a> Diagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { + fn into_diag( + self, + dcx: rustc_errors::DiagCtxtHandle<'a>, + level: rustc_errors::Level, + ) -> rustc_errors::Diag<'a, ()> { + let mut diag = rustc_errors::Diag::new( + dcx, + level, + msg!("`{$self_ty}` will capture more lifetimes than possibly intended in edition 2024"), + ); diag.arg("self_ty", self.self_ty.to_string()) .arg("num_captured", self.num_captured) .span_note( @@ -451,12 +457,13 @@ fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { ) .note(msg!("all lifetimes in scope will be captured by `impl Trait`s in edition 2024")); if let Some(suggestion) = self.suggestion { - suggestion.add_to_diag(diag); + suggestion.add_to_diag(&mut diag); } + diag } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("all possible in-scope parameters are already captured, so `use<...>` syntax is redundant")] struct ImplTraitRedundantCapturesLint { #[suggestion("remove the `use<...>` syntax", code = "", applicability = "machine-applicable")] diff --git a/compiler/rustc_lint/src/interior_mutable_consts.rs b/compiler/rustc_lint/src/interior_mutable_consts.rs index 4c7d2c6af93b..5e6a5abc5117 100644 --- a/compiler/rustc_lint/src/interior_mutable_consts.rs +++ b/compiler/rustc_lint/src/interior_mutable_consts.rs @@ -1,4 +1,3 @@ -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, ItemKind, Node, find_attr}; use rustc_middle::ty::adjustment::Adjust; @@ -76,7 +75,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { }; if let ExprKind::Path(qpath) = &receiver.kind - && let Res::Def(DefKind::Const | DefKind::AssocConst, const_did) = + && let Res::Def(DefKind::Const { .. } | DefKind::AssocConst { .. }, const_did) = typeck.qpath_res(qpath, receiver.hir_id) // Don't consider derefs as those can do arbitrary things // like using thread local (see rust-lang/rust#150157) @@ -87,8 +86,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { .any(|adj| matches!(adj.kind, Adjust::Deref(_))) // Let's do the attribute check after the other checks for perf reasons && find_attr!( - cx.tcx.get_all_attrs(method_did), - AttributeKind::RustcShouldNotBeCalledOnConstItems(_) + cx.tcx, method_did, + RustcShouldNotBeCalledOnConstItems(_) ) && let Some(method_name) = cx.tcx.opt_item_ident(method_did) && let Some(const_name) = cx.tcx.opt_item_ident(const_did) @@ -106,9 +105,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { Some(ConstItemInteriorMutationsSuggestionStatic::Spanful { const_: const_item.vis_span.between(ident.span), before: if !vis_span.is_empty() { " " } else { "" }, + const_name, }) } else { - Some(ConstItemInteriorMutationsSuggestionStatic::Spanless) + Some(ConstItemInteriorMutationsSuggestionStatic::Spanless { const_name }) } } else { None diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 37eb375c7a6c..357aa94feb1e 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -1,7 +1,9 @@ //! Some lints that are only useful in the compiler or crates that use compiler internals, such as //! Clippy. -use rustc_hir::attrs::AttributeKind; +use rustc_ast as ast; +use rustc_ast::{Pat, PatKind, Path}; +use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, HirId, find_attr}; @@ -9,13 +11,12 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::{Span, sym}; -use {rustc_ast as ast, rustc_hir as hir}; use crate::lints::{ - BadOptAccessDiag, DefaultHashTypesDiag, ImplicitSysrootCrateImportDiag, LintPassByHand, - NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag, - SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrDirectUse, - TypeIrInherentUsage, TypeIrTraitUsage, + AttributeKindInFindAttr, BadOptAccessDiag, DefaultHashTypesDiag, + ImplicitSysrootCrateImportDiag, LintPassByHand, NonGlobImportTypeIrInherent, QueryInstability, + QueryUntracked, SpanUseEqCtxtDiag, SymbolInternStringLiteralDiag, TyQualified, TykindDiag, + TykindKind, TypeIrDirectUse, TypeIrInherentUsage, TypeIrTraitUsage, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; @@ -90,7 +91,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { ty::Instance::try_resolve(cx.tcx, cx.typing_env(), callee_def_id, generic_args) { let def_id = instance.def_id(); - if find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcLintQueryInstability) { + if find_attr!(cx.tcx, def_id, RustcLintQueryInstability) { cx.emit_span_lint( POTENTIAL_QUERY_INSTABILITY, span, @@ -105,10 +106,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { ); } - if find_attr!( - cx.tcx.get_all_attrs(def_id), - AttributeKind::RustcLintUntrackedQueryInformation - ) { + if find_attr!(cx.tcx, def_id, RustcLintUntrackedQueryInformation) { cx.emit_span_lint( UNTRACKED_QUERY_INFORMATION, span, @@ -153,10 +151,7 @@ fn has_unstable_into_iter_predicate<'tcx>( }; // Does the input type's `IntoIterator` implementation have the // `rustc_lint_query_instability` attribute on its `into_iter` method? - if find_attr!( - cx.tcx.get_all_attrs(instance.def_id()), - AttributeKind::RustcLintQueryInstability - ) { + if find_attr!(cx.tcx, instance.def_id(), RustcLintQueryInstability) { return true; } } @@ -288,9 +283,9 @@ fn lint_ty_kind_usage(cx: &LateContext<'_>, res: &Res) -> bool { } fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &hir::Path<'_>) -> Option { - match &path.res { + match path.res { Res::Def(_, def_id) => { - if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(*def_id) { + if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(def_id) { return Some(format!("{}{}", name, gen_args(path.segments.last().unwrap()))); } } @@ -508,13 +503,13 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return }; // Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be // avoided. - if !find_attr!(cx.tcx.get_all_attrs(adt_def.did()), AttributeKind::RustcLintOptTy) { + if !find_attr!(cx.tcx, adt_def.did(), RustcLintOptTy) { return; } for field in adt_def.all_fields() { if field.name == target.name - && let Some(lint_message) = find_attr!(cx.tcx.get_all_attrs(field.did), AttributeKind::RustcLintOptDenyFieldAccess { lint_message, } => lint_message) + && let Some(lint_message) = find_attr!(cx.tcx, field.did, RustcLintOptDenyFieldAccess { lint_message, } => lint_message) { cx.emit_span_lint( BAD_OPT_ACCESS, @@ -627,3 +622,94 @@ fn is_whitelisted(crate_name: &str) -> bool { } } } + +declare_tool_lint! { + pub rustc::BAD_USE_OF_FIND_ATTR, + Allow, + "Forbid `AttributeKind::` as a prefix in `find_attr!` macros.", + report_in_external_macro: true +} +declare_lint_pass!(BadUseOfFindAttr => [BAD_USE_OF_FIND_ATTR]); + +impl EarlyLintPass for BadUseOfFindAttr { + fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &rustc_ast::Arm) { + fn path_contains_attribute_kind(cx: &EarlyContext<'_>, path: &Path) { + for segment in &path.segments { + if segment.ident.as_str() == "AttributeKind" { + cx.emit_span_lint( + BAD_USE_OF_FIND_ATTR, + segment.span(), + AttributeKindInFindAttr, + ); + } + } + } + + fn find_attr_kind_in_pat(cx: &EarlyContext<'_>, pat: &Pat) { + match &pat.kind { + PatKind::Struct(_, path, fields, _) => { + path_contains_attribute_kind(cx, path); + for field in fields { + find_attr_kind_in_pat(cx, &field.pat); + } + } + PatKind::TupleStruct(_, path, fields) => { + path_contains_attribute_kind(cx, path); + for field in fields { + find_attr_kind_in_pat(cx, &field); + } + } + PatKind::Or(options) => { + for pat in options { + find_attr_kind_in_pat(cx, pat); + } + } + PatKind::Path(_, path) => { + path_contains_attribute_kind(cx, path); + } + PatKind::Tuple(elems) => { + for pat in elems { + find_attr_kind_in_pat(cx, pat); + } + } + PatKind::Box(pat) => { + find_attr_kind_in_pat(cx, pat); + } + PatKind::Deref(pat) => { + find_attr_kind_in_pat(cx, pat); + } + PatKind::Ref(..) => { + find_attr_kind_in_pat(cx, pat); + } + PatKind::Slice(elems) => { + for pat in elems { + find_attr_kind_in_pat(cx, pat); + } + } + + PatKind::Guard(pat, ..) => { + find_attr_kind_in_pat(cx, pat); + } + PatKind::Paren(pat) => { + find_attr_kind_in_pat(cx, pat); + } + PatKind::Expr(..) + | PatKind::Range(..) + | PatKind::MacCall(..) + | PatKind::Rest + | PatKind::Missing + | PatKind::Err(..) + | PatKind::Ident(..) + | PatKind::Never + | PatKind::Wild => {} + } + } + + if let Some(expn_data) = arm.span.source_callee() + && let ExpnKind::Macro(_, name) = expn_data.kind + && name.as_str() == "find_attr" + { + find_attr_kind_in_pat(cx, &arm.pat); + } + } +} diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index a3376ad967e0..6f3a7e945d93 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,16 +1,18 @@ +use rustc_ast as ast; use rustc_ast::attr::AttributeExt; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{Diag, LintDiagnostic, MultiSpan, msg}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, MultiSpan, msg}; use rustc_feature::{Features, GateIssue}; +use rustc_hir as hir; use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::lint::{ - LevelAndSource, LintExpectation, LintLevelSource, ShallowLintLevelMap, lint_level, + LevelAndSource, LintExpectation, LintLevelSource, ShallowLintLevelMap, diag_lint_level, reveal_actual_level, }; use rustc_middle::query::Providers; @@ -23,7 +25,6 @@ use rustc_session::lint::{Level, Lint, LintExpectationId, LintId}; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use tracing::{debug, instrument}; -use {rustc_ast as ast, rustc_hir as hir}; use crate::builtin::MISSING_DOCS; use crate::context::{CheckLintNameResult, LintStore}; @@ -822,8 +823,11 @@ fn add( RenamedLintSuggestion::WithSpan { suggestion: sp, replace }; let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); - let lint = RenamedLint { name: name.as_str(), replace, suggestion }; - self.emit_span_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint); + self.emit_span_lint( + RENAMED_AND_REMOVED_LINTS, + sp.into(), + RenamedLint { name: name.as_str(), replace, suggestion }, + ); } // If this lint was renamed, apply the new lint instead of ignoring the @@ -844,8 +848,11 @@ fn add( if self.lint_added_lints { let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); - let lint = RemovedLint { name: name.as_str(), reason }; - self.emit_span_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint); + self.emit_span_lint( + RENAMED_AND_REMOVED_LINTS, + sp.into(), + RemovedLint { name: name.as_str(), reason }, + ); } continue; } @@ -861,8 +868,11 @@ fn add( from_rustc, } }); - let lint = UnknownLint { name, suggestion }; - self.emit_span_lint(UNKNOWN_LINTS, sp.into(), lint); + self.emit_span_lint( + UNKNOWN_LINTS, + sp.into(), + UnknownLint { name, suggestion }, + ); } continue; } @@ -939,22 +949,45 @@ fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> return true; }; - if self.lint_added_lints { - let lint = builtin::UNKNOWN_LINTS; - let level = self.lint_level(builtin::UNKNOWN_LINTS); - lint_level(self.sess, lint, level, Some(span.into()), |lint| { - lint.primary_message(msg!("unknown lint: `{$name}`")); - lint.arg("name", lint_id.lint.name_lower()); - lint.note(msg!("the `{$name}` lint is unstable")); + struct UnknownLint<'a> { + sess: &'a Session, + lint_id: LintId, + feature: Symbol, + 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, ()> { + 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()) + .with_note(msg!("the `{$name}` lint is unstable")); rustc_session::parse::add_feature_diagnostics_for_issue( - lint, - &self.sess, + &mut lint, + sess, feature, GateIssue::Language, lint_from_cli, None, ); - }); + lint + } + } + + if self.lint_added_lints { + let lint = builtin::UNKNOWN_LINTS; + let level = self.lint_level(builtin::UNKNOWN_LINTS); + diag_lint_level( + self.sess, + lint, + level, + Some(span.into()), + UnknownLint { sess: &self.sess, lint_id, feature, lint_from_cli }, + ); } false @@ -967,17 +1000,15 @@ pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource { /// Used to emit a lint-related diagnostic based on the current state of /// this lint context. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature #[track_caller] - pub(crate) fn opt_span_lint( + pub(crate) fn opt_span_diag_lint( &self, lint: &'static Lint, span: Option, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), + decorator: impl for<'a> Diagnostic<'a, ()>, ) { let level = self.lint_level(lint); - lint_level(self.sess, lint, level, span, decorate) + diag_lint_level(self.sess, lint, level, span, decorator) } #[track_caller] @@ -985,20 +1016,16 @@ pub fn emit_span_lint( &self, lint: &'static Lint, span: MultiSpan, - decorate: impl for<'a> LintDiagnostic<'a, ()>, + decorator: impl for<'a> Diagnostic<'a, ()>, ) { let level = self.lint_level(lint); - lint_level(self.sess, lint, level, Some(span), |lint| { - decorate.decorate_lint(lint); - }); + diag_lint_level(self.sess, lint, level, Some(span), decorator); } #[track_caller] - pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> LintDiagnostic<'a, ()>) { + pub fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a, ()>) { let level = self.lint_level(lint); - lint_level(self.sess, lint, level, None, |lint| { - decorate.decorate_lint(lint); - }); + diag_lint_level(self.sess, lint, level, None, decorator); } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 94dc566d75f1..1790eac7bef5 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -21,9 +21,7 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] -#![feature(if_let_guard)] #![feature(iter_order_by)] #![feature(rustc_attrs)] #![feature(try_blocks)] @@ -37,6 +35,7 @@ mod dangling; mod default_could_be_derived; mod deref_into_dyn_supertrait; +mod disallowed_pass_by_ref; mod drop_forget_useless; mod early; mod enum_intrinsics_non_enums; @@ -65,7 +64,6 @@ mod nonstandard_style; mod noop_method_call; mod opaque_hidden_inferred_bound; -mod pass_by_value; mod passes; mod precedence; mod ptr_nulls; @@ -78,7 +76,7 @@ mod types; mod unit_bindings; mod unqualified_local_imports; -mod unused; +pub mod unused; mod utils; use async_closures::AsyncClosureUsage; @@ -88,6 +86,7 @@ use dangling::*; use default_could_be_derived::DefaultCouldBeDerived; use deref_into_dyn_supertrait::*; +use disallowed_pass_by_ref::*; use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; @@ -109,7 +108,6 @@ use nonstandard_style::*; use noop_method_call::*; use opaque_hidden_inferred_bound::*; -use pass_by_value::*; use precedence::*; use ptr_nulls::*; use redundant_semicolon::*; @@ -125,12 +123,13 @@ use types::*; use unit_bindings::*; use unqualified_local_imports::*; +use unused::must_use::*; use unused::*; #[rustfmt::skip] pub use builtin::{MissingDoc, SoftLints}; pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore}; -pub use early::diagnostics::{decorate_attribute_lint, decorate_builtin_lint}; +pub use early::diagnostics::{DecorateAttrLint, DecorateBuiltinLint}; pub use early::{EarlyCheckNode, check_ast_node}; pub use late::{check_crate, late_lint_mod, unerased_lint_store}; pub use levels::LintLevelsBuilder; @@ -249,6 +248,7 @@ fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { FunctionCastsAsInteger: FunctionCastsAsInteger, CheckTransmutes: CheckTransmutes, LifetimeSyntax: LifetimeSyntax, + InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, ] ] ); @@ -657,14 +657,16 @@ fn register_internals(store: &mut LintStore) { store.register_late_mod_pass(|_| Box::new(TypeIr)); store.register_lints(&BadOptAccess::lint_vec()); store.register_late_mod_pass(|_| Box::new(BadOptAccess)); - store.register_lints(&PassByValue::lint_vec()); - store.register_late_mod_pass(|_| Box::new(PassByValue)); + store.register_lints(&DisallowedPassByRef::lint_vec()); + store.register_late_mod_pass(|_| Box::new(DisallowedPassByRef)); store.register_lints(&SpanUseEqCtxt::lint_vec()); store.register_late_mod_pass(|_| Box::new(SpanUseEqCtxt)); store.register_lints(&SymbolInternStringLiteral::lint_vec()); store.register_late_mod_pass(|_| Box::new(SymbolInternStringLiteral)); store.register_lints(&ImplicitSysrootCrateImport::lint_vec()); store.register_early_pass(|| Box::new(ImplicitSysrootCrateImport)); + store.register_lints(&BadUseOfFindAttr::lint_vec()); + store.register_early_pass(|| Box::new(BadUseOfFindAttr)); store.register_group( false, "rustc::internal", @@ -674,7 +676,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(POTENTIAL_QUERY_INSTABILITY), LintId::of(UNTRACKED_QUERY_INFORMATION), LintId::of(USAGE_OF_TY_TYKIND), - LintId::of(PASS_BY_VALUE), + LintId::of(DISALLOWED_PASS_BY_REF), LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), LintId::of(USAGE_OF_QUALIFIED_TY), LintId::of(NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT), @@ -684,6 +686,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(SPAN_USE_EQ_CTXT), LintId::of(DIRECT_USE_OF_RUSTC_TYPE_IR), LintId::of(IMPLICIT_SYSROOT_CRATE_IMPORT), + LintId::of(BAD_USE_OF_FIND_ATTR), ], ); } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 3b34a217edfd..d8b62e81b0cb 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3,14 +3,16 @@ use std::num::NonZero; use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ - Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag, - EmissionGuarantee, LintDiagnostic, MultiSpan, Subdiagnostic, SuggestionStyle, msg, + Applicability, Diag, DiagArgValue, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, + ElidedLifetimeInPathSubdiag, EmissionGuarantee, Level, MultiSpan, Subdiagnostic, + SuggestionStyle, msg, }; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::VisitorExt; -use rustc_macros::{LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::inhabitedness::InhabitedPredicate; use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_session::Session; @@ -23,7 +25,7 @@ use crate::lifetime_syntax::LifetimeSyntaxCategories; // array_into_iter.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<{$target} as IntoIterator>::into_iter` in Rust {$edition}" )] @@ -64,7 +66,7 @@ pub(crate) enum ShadowedIntoIterDiagSub { } // autorefs.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("implicit autoref creates a reference to the dereference of a raw pointer")] #[note( "creating a reference requires the pointer target to be valid and imposes aliasing requirements" @@ -118,7 +120,7 @@ pub(crate) struct ImplicitUnsafeAutorefsSuggestion { } // builtin.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("denote infinite loops with `loop {\"{\"} ... {\"}\"}`")] pub(crate) struct BuiltinWhileTrue { #[suggestion( @@ -131,7 +133,7 @@ pub(crate) struct BuiltinWhileTrue { pub replace: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("the `{$ident}:` in this pattern is redundant")] pub(crate) struct BuiltinNonShorthandFieldPatterns { pub ident: Ident, @@ -144,7 +146,7 @@ pub(crate) struct BuiltinNonShorthandFieldPatterns { pub prefix: &'static str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum BuiltinUnsafe { #[diag( "`allow_internal_unsafe` allows defining macros using unsafe without triggering the `unsafe_code` lint at their call site" @@ -209,14 +211,14 @@ pub(crate) enum BuiltinUnsafe { GlobalAsm, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("missing documentation for {$article} {$desc}")] pub(crate) struct BuiltinMissingDoc<'a> { pub article: &'a str, pub desc: &'a str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("type could implement `Copy`; consider adding `impl Copy`")] pub(crate) struct BuiltinMissingCopyImpl; @@ -226,14 +228,18 @@ pub(crate) struct BuiltinMissingDebugImpl<'a> { } // Needed for def_path_str -impl<'a> LintDiagnostic<'a, ()> for BuiltinMissingDebugImpl<'_> { - fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { - diag.primary_message(msg!("type does not implement `{$debug}`; consider adding `#[derive(Debug)]` or a manual implementation")); - diag.arg("debug", self.tcx.def_path_str(self.def_id)); +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, + level, + msg!("type does not implement `{$debug}`; consider adding `#[derive(Debug)]` or a manual implementation"), + ).with_arg("debug", tcx.def_path_str(def_id)) } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("anonymous parameters are deprecated and will be removed in the next edition")] pub(crate) struct BuiltinAnonymousParams<'a> { #[suggestion("try naming the parameter or explicitly ignoring it", code = "_: {ty_snip}")] @@ -241,7 +247,7 @@ pub(crate) struct BuiltinAnonymousParams<'a> { pub ty_snip: &'a str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused doc comment")] pub(crate) struct BuiltinUnusedDocComment<'a> { pub kind: &'a str, @@ -259,7 +265,7 @@ pub(crate) enum BuiltinUnusedDocCommentSub { BlockHelp, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("functions generic over types or consts must be mangled")] pub(crate) struct BuiltinNoMangleGeneric { // Use of `#[no_mangle]` suggests FFI intent; correct @@ -273,20 +279,20 @@ pub(crate) struct BuiltinNoMangleGeneric { pub suggestion: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("const items should never be `#[no_mangle]`")] pub(crate) struct BuiltinConstNoMangle { #[suggestion("try a static value", code = "pub static ", applicability = "machine-applicable")] pub suggestion: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell" )] pub(crate) struct BuiltinMutablesTransmutes; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("use of an unstable feature")] pub(crate) struct BuiltinUnstableFeatures; @@ -296,19 +302,20 @@ pub(crate) struct BuiltinUngatedAsyncFnTrackCaller<'a> { pub session: &'a Session, } -impl<'a> LintDiagnostic<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(msg!("`#[track_caller]` on async functions is a no-op")); - diag.span_label(self.label, msg!("this function will not propagate the caller location")); +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::parse::add_feature_diagnostics( - diag, + &mut diag, self.session, sym::async_fn_track_caller, ); + diag } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unreachable `pub` {$what}")] pub(crate) struct BuiltinUnreachablePub<'a> { pub what: &'a str, @@ -319,7 +326,7 @@ pub(crate) struct BuiltinUnreachablePub<'a> { pub help: bool, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("the `expr` fragment specifier will accept more expressions in the 2024 edition")] pub(crate) struct MacroExprFragment2024 { #[suggestion( @@ -339,18 +346,18 @@ pub(crate) struct BuiltinTypeAliasBounds<'hir> { pub ty: Option<&'hir hir::Ty<'hir>>, } -impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(if self.in_where_clause { +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 { msg!("bounds on generic parameters in type aliases are not enforced") - }); - diag.span_label(self.label, msg!("will not be checked at usage sites of the type alias")); - diag.note(msg!( - "this is a known limitation of the type checker that may be lifted in a future edition. - see issue #112792 for more information" - )); + }) + .with_span_label(self.label, msg!("will not be checked at usage sites of the type alias")) + .with_note(msg!( + "this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information" + )); if self.enable_feat_help { diag.help(msg!("add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics")); } @@ -412,10 +419,11 @@ fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { Applicability::HasPlaceholders, ); } + diag } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "{$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters" )] @@ -424,7 +432,7 @@ pub(crate) struct BuiltinTrivialBounds<'a> { pub predicate: Clause<'a>, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("use of a double negation")] #[note( "the prefix `--` could be misinterpreted as a decrement operator which exists in other languages" @@ -444,7 +452,7 @@ pub(crate) struct BuiltinDoubleNegationsAddParens { pub end_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum BuiltinEllipsisInclusiveRangePatternsLint { #[diag("`...` range patterns are deprecated")] Parenthesise { @@ -468,7 +476,7 @@ pub(crate) enum BuiltinEllipsisInclusiveRangePatternsLint { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`{$kw}` is a keyword in the {$next} edition")] pub(crate) struct BuiltinKeywordIdents { pub kw: Ident, @@ -482,10 +490,9 @@ pub(crate) struct BuiltinKeywordIdents { pub prefix: &'static str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("outlives requirements can be inferred")] pub(crate) struct BuiltinExplicitOutlives { - pub count: usize, #[subdiagnostic] pub suggestion: BuiltinExplicitOutlivesSuggestion, } @@ -502,9 +509,10 @@ pub(crate) struct BuiltinExplicitOutlivesSuggestion { pub spans: Vec, #[applicability] pub applicability: Applicability, + pub count: usize, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "the feature `{$name}` is incomplete and may not be safe to use and/or cause compiler crashes" )] @@ -516,7 +524,7 @@ pub(crate) struct BuiltinIncompleteFeatures { pub help: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("the feature `{$name}` is internal to the compiler or standard library")] #[note("using it is strongly discouraged")] pub(crate) struct BuiltinInternalFeatures { @@ -525,7 +533,9 @@ pub(crate) struct BuiltinInternalFeatures { #[derive(Subdiagnostic)] #[help("consider using `min_{$name}` instead, which is more stable and complete")] -pub(crate) struct BuiltinIncompleteFeaturesHelp; +pub(crate) struct BuiltinIncompleteFeaturesHelp { + pub name: Symbol, +} #[derive(Subdiagnostic)] #[note("see issue #{$n} for more information")] @@ -541,11 +551,11 @@ pub(crate) struct BuiltinUnpermittedTypeInit<'a> { pub tcx: TyCtxt<'a>, } -impl<'a> LintDiagnostic<'a, ()> for BuiltinUnpermittedTypeInit<'_> { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(self.msg); - diag.arg("ty", self.ty); - diag.span_label(self.label, msg!("this code causes undefined behavior when executed")); +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")); if let InhabitedPredicate::True = self.ty.inhabited_predicate(self.tcx) { // Only suggest late `MaybeUninit::assume_init` initialization if the type is inhabited. diag.span_label( @@ -553,7 +563,8 @@ fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { msg!("help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done"), ); } - self.sub.add_to_diag(diag); + self.sub.add_to_diag(&mut diag); + diag } } @@ -580,7 +591,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum BuiltinClashingExtern<'a> { #[diag("`{$this}` redeclared with a different signature")] SameName { @@ -623,7 +634,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("dereferencing a null pointer")] pub(crate) struct BuiltinDerefNullptr { #[label("this code causes undefined behavior when executed")] @@ -632,7 +643,7 @@ pub(crate) struct BuiltinDerefNullptr { // FIXME: migrate fluent::lint::builtin_asm_labels -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum BuiltinSpecialModuleNameUsed { #[diag("found module declaration for lib.rs")] #[note("lib.rs is the root of this crate's library target")] @@ -644,7 +655,7 @@ pub(crate) enum BuiltinSpecialModuleNameUsed { } // deref_into_dyn_supertrait.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("this `Deref` implementation is covered by an implicit supertrait coercion")] pub(crate) struct SupertraitAsDerefTarget<'a> { pub self_ty: Ty<'a>, @@ -655,18 +666,19 @@ pub(crate) struct SupertraitAsDerefTarget<'a> { )] pub label: Span, #[subdiagnostic] - pub label2: Option, + pub label2: Option>, } #[derive(Subdiagnostic)] #[label("target type is a supertrait of `{$self_ty}`")] -pub(crate) struct SupertraitAsDerefTargetLabel { +pub(crate) struct SupertraitAsDerefTargetLabel<'a> { #[primary_span] pub label: Span, + pub self_ty: Ty<'a>, } // enum_intrinsics_non_enums.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("the return value of `mem::discriminant` is unspecified when called with a non-enum type")] pub(crate) struct EnumIntrinsicsMemDiscriminate<'a> { pub ty_param: Ty<'a>, @@ -676,7 +688,7 @@ pub(crate) struct EnumIntrinsicsMemDiscriminate<'a> { pub note: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("the return value of `mem::variant_count` is unspecified when called with a non-enum type")] #[note( "the type parameter of `variant_count` should be an enum, but it was instantiated with the type `{$ty_param}`, which is not an enum" @@ -686,7 +698,7 @@ pub(crate) struct EnumIntrinsicsMemVariant<'a> { } // expect.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("this lint expectation is unfulfilled")] pub(crate) struct Expectation { #[subdiagnostic] @@ -704,7 +716,7 @@ pub(crate) struct ExpectationNote { } // ptr_nulls.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum UselessPtrNullChecksDiag<'a> { #[diag( "function pointers are not nullable, so checking them for null will always return false" @@ -729,7 +741,7 @@ pub(crate) enum UselessPtrNullChecksDiag<'a> { FnRet { fn_name: Ident }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum InvalidNullArgumentsDiag { #[diag( "calling this function with a null pointer is undefined behavior, even if the result of the function is unused" @@ -754,7 +766,7 @@ pub(crate) enum InvalidNullArgumentsDiag { } // for_loops_over_fallibles.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement" )] @@ -837,7 +849,7 @@ pub(crate) enum UseLetUnderscoreIgnoreSuggestion { } // drop_forget_useless.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("calls to `std::mem::drop` with a reference instead of an owned value does nothing")] pub(crate) struct DropRefDiag<'a> { pub arg_ty: Ty<'a>, @@ -847,7 +859,7 @@ pub(crate) struct DropRefDiag<'a> { pub sugg: UseLetUnderscoreIgnoreSuggestion, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("calls to `std::mem::drop` with a value that implements `Copy` does nothing")] pub(crate) struct DropCopyDiag<'a> { pub arg_ty: Ty<'a>, @@ -857,7 +869,7 @@ pub(crate) struct DropCopyDiag<'a> { pub sugg: UseLetUnderscoreIgnoreSuggestion, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("calls to `std::mem::forget` with a reference instead of an owned value does nothing")] pub(crate) struct ForgetRefDiag<'a> { pub arg_ty: Ty<'a>, @@ -867,7 +879,7 @@ pub(crate) struct ForgetRefDiag<'a> { pub sugg: UseLetUnderscoreIgnoreSuggestion, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("calls to `std::mem::forget` with a value that implements `Copy` does nothing")] pub(crate) struct ForgetCopyDiag<'a> { pub arg_ty: Ty<'a>, @@ -877,7 +889,7 @@ pub(crate) struct ForgetCopyDiag<'a> { pub sugg: UseLetUnderscoreIgnoreSuggestion, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing" )] @@ -902,7 +914,7 @@ pub(crate) struct UndroppedManuallyDropsSuggestion { } // invalid_from_utf8.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum InvalidFromUtf8Diag { #[diag("calls to `{$method}` with an invalid literal are undefined behavior")] Unchecked { @@ -921,7 +933,7 @@ pub(crate) enum InvalidFromUtf8Diag { } // interior_mutable_consts.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("mutation of an interior mutable `const` item with call to `{$method_name}`")] #[note("each usage of a `const` item creates a new temporary")] #[note("only the temporaries and never the original `const {$const_name}` will be modified")] @@ -950,13 +962,14 @@ pub(crate) enum ConstItemInteriorMutationsSuggestionStatic { #[primary_span] const_: Span, before: &'static str, + const_name: Ident, }, #[help("for a shared instance of `{$const_name}`, consider making it a `static` item instead")] - Spanless, + Spanless { const_name: Ident }, } // reference_casting.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum InvalidReferenceCastingDiag<'tcx> { #[diag( "casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`" @@ -1001,7 +1014,7 @@ pub(crate) enum InvalidReferenceCastingDiag<'tcx> { } // map_unit_fn.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`Iterator::map` call that discard the iterator's values")] #[note( "`Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated" @@ -1025,7 +1038,7 @@ pub(crate) struct MappingToUnit { } // internal.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("prefer `{$preferred}` over `{$used}`, it has better performance")] #[note("a `use rustc_data_structures::fx::{$preferred}` may be necessary")] pub(crate) struct DefaultHashTypesDiag<'a> { @@ -1033,7 +1046,7 @@ pub(crate) struct DefaultHashTypesDiag<'a> { pub used: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("using `{$query}` can result in unstable query results")] #[note( "if you believe this case to be fine, allow this lint and add a comment explaining your rationale" @@ -1042,7 +1055,7 @@ pub(crate) struct QueryInstability { pub query: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`{$method}` accesses information that is not tracked by the query system")] #[note( "if you believe this case to be fine, allow this lint and add a comment explaining your rationale" @@ -1051,16 +1064,16 @@ pub(crate) struct QueryUntracked { pub method: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("use `.eq_ctxt()` instead of `.ctxt() == .ctxt()`")] pub(crate) struct SpanUseEqCtxtDiag; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("using `Symbol::intern` on a string literal")] #[help("consider adding the symbol to `compiler/rustc_span/src/symbol.rs`")] pub(crate) struct SymbolInternStringLiteralDiag; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("usage of `ty::TyKind::`")] pub(crate) struct TykindKind { #[suggestion( @@ -1071,12 +1084,12 @@ pub(crate) struct TykindKind { pub suggestion: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("usage of `ty::TyKind`")] #[help("try using `Ty` instead")] pub(crate) struct TykindDiag; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("usage of qualified `ty::{$ty}`")] pub(crate) struct TyQualified { pub ty: String, @@ -1088,14 +1101,14 @@ pub(crate) struct TyQualified { pub suggestion: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("do not use `rustc_type_ir::inherent` unless you're inside of the trait solver")] #[note( "the method or struct you're looking for is likely defined somewhere else downstream in the compiler" )] pub(crate) struct TypeIrInherentUsage; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver" )] @@ -1104,12 +1117,12 @@ pub(crate) struct TyQualified { )] pub(crate) struct TypeIrTraitUsage; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("do not use `rustc_type_ir` unless you are implementing type system internals")] #[note("use `rustc_middle::ty` instead")] pub(crate) struct TypeIrDirectUse; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("non-glob import of `rustc_type_ir::inherent`")] pub(crate) struct NonGlobImportTypeIrInherent { #[suggestion( @@ -1121,18 +1134,18 @@ pub(crate) struct NonGlobImportTypeIrInherent { pub snippet: &'static str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("implementing `LintPass` by hand")] #[help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")] pub(crate) struct LintPassByHand; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$msg}")] pub(crate) struct BadOptAccessDiag<'a> { pub msg: &'a str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "dangerous use of `extern crate {$name}` which is not guaranteed to exist exactly once in the sysroot" )] @@ -1143,8 +1156,14 @@ pub(crate) struct ImplicitSysrootCrateImportDiag<'a> { pub name: &'a str, } +#[derive(Diagnostic)] +#[diag("use of `AttributeKind` in `find_attr!(...)` invocation")] +#[note("`find_attr!(...)` already imports `AttributeKind::*`")] +#[help("remove `AttributeKind`")] +pub(crate) struct AttributeKindInFindAttr; + // let_underscore.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum NonBindingLet { #[diag("non-binding let on a synchronization lock")] SyncLock { @@ -1206,7 +1225,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } // levels.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$lint_level}({$lint_source}) incompatible with previous forbid")] pub(crate) struct OverruledAttributeLint<'a> { #[label("overruled by previous forbid")] @@ -1217,7 +1236,7 @@ pub(crate) struct OverruledAttributeLint<'a> { pub sub: OverruledAttributeSub, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("lint name `{$name}` is deprecated and may not have an effect in the future")] pub(crate) struct DeprecatedLintName<'a> { pub name: String, @@ -1226,7 +1245,7 @@ pub(crate) struct DeprecatedLintName<'a> { pub replace: &'a str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("lint name `{$name}` is deprecated and may not have an effect in the future")] #[help("change it to {$replace}")] pub(crate) struct DeprecatedLintNameFromCommandLine<'a> { @@ -1236,7 +1255,7 @@ pub(crate) struct DeprecatedLintNameFromCommandLine<'a> { pub requested_level: RequestedLevel<'a>, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("lint `{$name}` has been renamed to `{$replace}`")] pub(crate) struct RenamedLint<'a> { pub name: &'a str, @@ -1257,7 +1276,7 @@ pub(crate) enum RenamedLintSuggestion<'a> { WithoutSpan { replace: &'a str }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("lint `{$name}` has been renamed to `{$replace}`")] pub(crate) struct RenamedLintFromCommandLine<'a> { pub name: &'a str, @@ -1268,14 +1287,14 @@ pub(crate) struct RenamedLintFromCommandLine<'a> { pub requested_level: RequestedLevel<'a>, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("lint `{$name}` has been removed: {$reason}")] pub(crate) struct RemovedLint<'a> { pub name: &'a str, pub reason: &'a str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("lint `{$name}` has been removed: {$reason}")] pub(crate) struct RemovedLintFromCommandLine<'a> { pub name: &'a str, @@ -1284,7 +1303,7 @@ pub(crate) struct RemovedLintFromCommandLine<'a> { pub requested_level: RequestedLevel<'a>, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unknown lint: `{$name}`")] pub(crate) struct UnknownLint { pub name: String, @@ -1317,7 +1336,7 @@ pub(crate) enum UnknownLintSuggestion { WithoutSpan { replace: Symbol, from_rustc: bool }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unknown lint: `{$name}`", code = E0602)] pub(crate) struct UnknownLintFromCommandLine<'a> { pub name: String, @@ -1327,7 +1346,7 @@ pub(crate) struct UnknownLintFromCommandLine<'a> { pub requested_level: RequestedLevel<'a>, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$level}({$name}) is ignored unless specified at crate level")] pub(crate) struct IgnoredUnlessCrateSpecified<'a> { pub level: &'a str, @@ -1335,7 +1354,7 @@ pub(crate) struct IgnoredUnlessCrateSpecified<'a> { } // dangling.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("this creates a dangling pointer because temporary `{$ty}` is dropped at end of statement")] #[help("bind the `{$ty}` to a variable such that it outlives the pointer returned by `{$callee}`")] #[note("a dangling pointer is safe, but dereferencing one is undefined behavior")] @@ -1351,7 +1370,7 @@ pub(crate) struct DanglingPointersFromTemporaries<'tcx> { pub temporary_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$fn_kind} returns a dangling pointer to dropped local variable `{$local_var_name}`")] #[note("a dangling pointer is safe, but dereferencing one is undefined behavior")] #[note("for more information, see ")] @@ -1369,18 +1388,18 @@ pub(crate) struct DanglingPointersFromLocals<'tcx> { } // multiple_supertrait_upcastable.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`{$ident}` is dyn-compatible and has multiple supertraits")] pub(crate) struct MultipleSupertraitUpcastable { pub ident: Ident, } // non_ascii_idents.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("identifier contains non-ASCII characters")] pub(crate) struct IdentifierNonAsciiChar; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "identifier contains {$codepoints_len -> [one] { $identifier_type -> @@ -1414,7 +1433,7 @@ pub(crate) struct IdentifierUncommonCodepoints { pub identifier_type: &'static str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("found both `{$existing_sym}` and `{$sym}` as identifiers, which look alike")] pub(crate) struct ConfusableIdentifierPair { pub existing_sym: Symbol, @@ -1425,7 +1444,7 @@ pub(crate) struct ConfusableIdentifierPair { pub main_label: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables" )] @@ -1443,9 +1462,9 @@ pub(crate) struct NonFmtPanicUnused { } // Used because of two suggestions based on one Option -impl<'a> LintDiagnostic<'a, ()> for NonFmtPanicUnused { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(msg!( +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 *[other] unused @@ -1453,9 +1472,9 @@ fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { [one] placeholder *[other] placeholders }" - )); - diag.arg("count", self.count); - diag.note(msg!("this message is not used as a format string when given without arguments, but will be in Rust 2021")); + )) + .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( span.shrink_to_hi(), @@ -1475,10 +1494,11 @@ fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { Applicability::MachineApplicable, ); } + diag } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "panic message contains {$count -> [one] a brace @@ -1497,7 +1517,7 @@ pub(crate) struct NonFmtPanicBraces { } // nonstandard_style.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$sort} `{$name}` should have an upper camel case name")] pub(crate) struct NonCamelCaseType<'a> { pub sort: &'a str, @@ -1525,7 +1545,7 @@ pub(crate) enum NonCamelCaseTypeSub { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$sort} `{$name}` should have a snake case name")] pub(crate) struct NonSnakeCaseDiag<'a> { pub sort: &'a str, @@ -1581,7 +1601,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$sort} `{$name}` should have an upper case name")] pub(crate) struct NonUpperCaseGlobal<'a> { pub sort: &'a str, @@ -1623,7 +1643,7 @@ pub(crate) struct NonUpperCaseGlobalSubTool { } // noop_method_call.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("call to `.{$method}()` on a reference in this situation does nothing")] #[note( "the type `{$orig_ty}` does not implement `{$trait_}`, so calling `{$method}` on `&{$orig_ty}` copies the reference, which does not do anything and can be removed" @@ -1642,7 +1662,7 @@ pub(crate) struct NoopMethodCallDiag<'a> { pub suggest_derive: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "using `.deref()` on a double reference, which returns `{$ty}` instead of dereferencing the inner type" )] @@ -1650,7 +1670,7 @@ pub(crate) struct SuspiciousDoubleRefDerefDiag<'a> { pub ty: Ty<'a>, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "using `.clone()` on a double reference, which returns `{$ty}` instead of cloning the inner type" )] @@ -1678,8 +1698,9 @@ pub(crate) enum NonLocalDefinitionsDiag { }, } -impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag { - fn decorate_lint<'b>(self, diag: &'b mut 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 { depth, @@ -1752,6 +1773,7 @@ fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { } } } + diag } } @@ -1766,7 +1788,7 @@ pub(crate) struct NonLocalDefinitionsCargoUpdateNote { } // precedence.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`-` has lower precedence than method calls, which might be unexpected")] #[note("e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4`")] pub(crate) struct AmbiguousNegativeLiteralsDiag { @@ -1800,17 +1822,17 @@ pub(crate) struct AmbiguousNegativeLiteralsCurrentBehaviorSuggestion { pub end_span: Span, } -// pass_by_value.rs -#[derive(LintDiagnostic)] +// disallowed_pass_by_ref.rs +#[derive(Diagnostic)] #[diag("passing `{$ty}` by reference")] -pub(crate) struct PassByValueDiag { +pub(crate) struct DisallowedPassByRefDiag { pub ty: String, #[suggestion("try passing by value", code = "{ty}", applicability = "maybe-incorrect")] pub suggestion: Span, } // redundant_semicolon.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "unnecessary trailing {$multiple -> [true] semicolons @@ -1846,11 +1868,11 @@ pub(crate) struct DropTraitConstraintsDiag<'a> { } // Needed for def_path_str -impl<'a> LintDiagnostic<'a, ()> for DropTraitConstraintsDiag<'_> { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(msg!("bounds on `{$predicate}` are most likely incorrect, consider instead using `{$needs_drop}` to detect whether a type can be trivially dropped")); - diag.arg("predicate", self.predicate); - diag.arg("needs_drop", self.tcx.def_path_str(self.def_id)); +impl<'a> Diagnostic<'a, ()> for DropTraitConstraintsDiag<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + Diag::new(dcx, level, msg!("bounds on `{$predicate}` are most likely incorrect, consider instead using `{$needs_drop}` to detect whether a type can be trivially dropped")) + .with_arg("predicate", self.predicate) + .with_arg("needs_drop", self.tcx.def_path_str(self.def_id)) } } @@ -1860,15 +1882,15 @@ pub(crate) struct DropGlue<'a> { } // Needed for def_path_str -impl<'a> LintDiagnostic<'a, ()> for DropGlue<'_> { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(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")); - diag.arg("needs_drop", self.tcx.def_path_str(self.def_id)); +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)) } } // transmute.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("transmuting an integer to a pointer creates a pointer without provenance")] #[note("this is dangerous because dereferencing the resulting pointer is undefined behavior")] #[note( @@ -1918,7 +1940,7 @@ pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> { } // types.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("range endpoint is out of range for `{$ty}`")] pub(crate) struct RangeEndpointOutOfRange<'a> { pub ty: &'a str, @@ -1951,15 +1973,12 @@ pub(crate) enum UseInclusiveRange<'a> { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("literal out of range for `{$ty}`")] pub(crate) struct OverflowingBinHex<'a> { pub ty: &'a str, - pub lit: String, - pub dec: u128, - pub actually: String, #[subdiagnostic] - pub sign: OverflowingBinHexSign, + pub sign: OverflowingBinHexSign<'a>, #[subdiagnostic] pub sub: Option>, #[subdiagnostic] @@ -1967,14 +1986,14 @@ pub(crate) struct OverflowingBinHex<'a> { } #[derive(Subdiagnostic)] -pub(crate) enum OverflowingBinHexSign { +pub(crate) enum OverflowingBinHexSign<'a> { #[note( "the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` and will become `{$actually}{$ty}`" )] - Positive, + Positive { lit: String, ty: &'a str, actually: String, dec: u128 }, #[note("the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}`")] #[note("and the value `-{$lit}` will become `{$actually}{$ty}`")] - Negative, + Negative { lit: String, ty: &'a str, actually: String, dec: u128 }, } #[derive(Subdiagnostic)] @@ -2009,7 +2028,7 @@ pub(crate) struct OverflowingBinHexSignBitSub<'a> { pub int_ty: &'a str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("literal out of range for `{$ty}`")] #[note("the literal `{$lit}` does not fit into the type `{$ty}` whose range is `{$min}..={$max}`")] pub(crate) struct OverflowingInt<'a> { @@ -2027,7 +2046,7 @@ pub(crate) struct OverflowingIntHelp<'a> { pub suggestion_ty: &'a str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("only `u8` can be cast into `char`")] pub(crate) struct OnlyCastu8ToChar { #[suggestion( @@ -2039,7 +2058,7 @@ pub(crate) struct OnlyCastu8ToChar { pub literal: u128, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("literal out of range for `{$ty}`")] #[note("the literal `{$lit}` does not fit into the type `{$ty}` whose range is `{$min}..={$max}`")] pub(crate) struct OverflowingUInt<'a> { @@ -2049,7 +2068,7 @@ pub(crate) struct OverflowingUInt<'a> { pub max: u128, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("literal out of range for `{$ty}`")] #[note( "the literal `{$lit}` does not fit into the type `{$ty}` and will be converted to `{$ty}::INFINITY`" @@ -2059,31 +2078,31 @@ pub(crate) struct OverflowingLiteral<'a> { pub lit: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("surrogate values are not valid for `char`")] #[note("`0xD800..=0xDFFF` are reserved for Unicode surrogates and are not valid `char` values")] pub(crate) struct SurrogateCharCast { pub literal: u128, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("value exceeds maximum `char` value")] #[note("maximum valid `char` value is `0x10FFFF`")] pub(crate) struct TooLargeCharCast { pub literal: u128, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type" )] pub(crate) struct UsesPowerAlignment; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("comparison is useless due to type limits")] pub(crate) struct UnusedComparisons; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum InvalidNanComparisons { #[diag("incorrect NaN comparison, NaN cannot be directly compared to itself")] EqNe { @@ -2113,7 +2132,7 @@ pub(crate) enum InvalidNanComparisonsSuggestion { Spanless, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum AmbiguousWidePointerComparisons<'a> { #[diag( "ambiguous wide pointer comparison, the comparison includes metadata which may not be expected" @@ -2226,7 +2245,7 @@ pub(crate) struct AmbiguousWidePointerComparisonsExpectSuggestion<'a> { pub(crate) after: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum UnpredictableFunctionPointerComparisons<'a, 'tcx> { #[diag( "function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique" @@ -2302,12 +2321,16 @@ pub(crate) struct ImproperCTypes<'a> { } // Used because of the complexity of Option, DiagMessage, and Option -impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(msg!("`extern` {$desc} uses type `{$ty}`, which is not FFI-safe")); - diag.arg("ty", self.ty); - diag.arg("desc", self.desc); - diag.span_label(self.label, msg!("not FFI-safe")); +impl<'a> Diagnostic<'a, ()> for ImproperCTypes<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let mut diag = Diag::new( + dcx, + level, + msg!("`extern` {$desc} uses type `{$ty}`, which is not FFI-safe"), + ) + .with_arg("ty", self.ty) + .with_arg("desc", self.desc) + .with_span_label(self.label, msg!("not FFI-safe")); if let Some(help) = self.help { diag.help(help); } @@ -2315,44 +2338,45 @@ fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { if let Some(note) = self.span_note { diag.span_note(note, msg!("the type is defined here")); } + diag } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("passing type `{$ty}` to a function with \"gpu-kernel\" ABI may have unexpected behavior")] #[help("use primitive types and raw pointers to get reliable behavior")] pub(crate) struct ImproperGpuKernelArg<'a> { pub ty: Ty<'a>, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("function with the \"gpu-kernel\" ABI has a mangled name")] #[help("use `unsafe(no_mangle)` or `unsafe(export_name = \"\")`")] #[note("mangled names make it hard to find the kernel, this is usually not intended")] pub(crate) struct MissingGpuKernelExportName; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("enum variant is more than three times larger ({$largest} bytes) than the next largest")] pub(crate) struct VariantSizeDifferencesDiag { pub largest: u64, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("atomic loads cannot have `Release` or `AcqRel` ordering")] #[help("consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`")] pub(crate) struct AtomicOrderingLoad; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("atomic stores cannot have `Acquire` or `AcqRel` ordering")] #[help("consider using ordering modes `Release`, `SeqCst` or `Relaxed`")] pub(crate) struct AtomicOrderingStore; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("memory fences cannot have `Relaxed` ordering")] #[help("consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`")] pub(crate) struct AtomicOrderingFence; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "`{$method}`'s failure ordering may not be `Release` or `AcqRel`, since a failed `{$method}` does not result in a write" )] @@ -2364,7 +2388,7 @@ pub(crate) struct InvalidAtomicOrderingDiag { } // unused.rs -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused {$op} that must be used")] pub(crate) struct UnusedOp<'a> { pub op: &'a str, @@ -2399,7 +2423,7 @@ pub(crate) enum UnusedOpSuggestion { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused result of type `{$ty}`")] pub(crate) struct UnusedResult<'a> { pub ty: Ty<'a>, @@ -2407,7 +2431,7 @@ pub(crate) struct UnusedResult<'a> { // FIXME(davidtwco): this isn't properly translatable because of the // pre/post strings -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "unused {$pre}{$count -> [one] closure @@ -2423,7 +2447,7 @@ pub(crate) struct UnusedClosure<'a> { // FIXME(davidtwco): this isn't properly translatable because of the // pre/post strings -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "unused {$pre}{$count -> [one] coroutine @@ -2474,12 +2498,13 @@ pub(crate) enum UnusedDefSuggestion { } // Needed because of def_path_str -impl<'a> LintDiagnostic<'a, ()> for UnusedDef<'_, '_> { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(msg!("unused {$pre}`{$def}`{$post} that must be used")); - diag.arg("pre", self.pre); - diag.arg("post", self.post); - diag.arg("def", self.cx.tcx.def_path_str(self.def_id)); +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) + .with_arg("post", self.post) + .with_arg("def", self.cx.tcx.def_path_str(self.def_id)); // check for #[must_use = "..."] if let Some(note) = self.note { diag.note(note.to_string()); @@ -2487,10 +2512,11 @@ fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { if let Some(sugg) = self.suggestion { diag.subdiagnostic(sugg); } + diag } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("path statement drops value")] pub(crate) struct PathStatementDrop { #[subdiagnostic] @@ -2516,11 +2542,11 @@ pub(crate) enum PathStatementDropSub { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("path statement with no effect")] pub(crate) struct PathStatementNoEffect; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unnecessary {$delim} around {$item}")] pub(crate) struct UnusedDelim<'a> { pub delim: &'static str, @@ -2538,19 +2564,20 @@ pub(crate) struct UnusedDelimSuggestion { #[suggestion_part(code = "{end_replace}")] pub end_span: Span, pub end_replace: &'static str, + pub delim: &'static str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("braces around {$node} is unnecessary")] pub(crate) struct UnusedImportBracesDiag { pub node: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unnecessary allocation, use `&` instead")] pub(crate) struct UnusedAllocationDiag; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unnecessary allocation, use `&mut` instead")] pub(crate) struct UnusedAllocationMutDiag; @@ -2558,24 +2585,29 @@ pub(crate) struct AsyncFnInTraitDiag { pub sugg: Option>, } -impl<'a> LintDiagnostic<'a, ()> for AsyncFnInTraitDiag { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(msg!("use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified")); - diag.note(msg!("you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`")); +impl<'a> Diagnostic<'a, ()> for AsyncFnInTraitDiag { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let mut diag = Diag::new( + dcx, + level, + "use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified", + ); + diag.note("you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`"); if let Some(sugg) = self.sugg { - diag.multipart_suggestion(msg!("you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send`, but these cannot be relaxed without a breaking API change"), sugg, Applicability::MaybeIncorrect); + diag.multipart_suggestion("you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send`, but these cannot be relaxed without a breaking API change", sugg, Applicability::MaybeIncorrect); } + diag } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("binding has unit type `()`")] pub(crate) struct UnitBindingsDiag { #[label("this pattern is inferred to be the unit type `()`")] pub label: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum InvalidAsmLabel { #[diag("avoid using named labels in inline assembly")] #[help("only local labels of the form `:` should be used in inline asm")] @@ -2681,7 +2713,7 @@ pub(crate) struct UnexpectedCfgCargoMacroHelp { pub crate_name: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unexpected `cfg` condition name: `{$name}`")] pub(crate) struct UnexpectedCfgName { #[subdiagnostic] @@ -2828,7 +2860,7 @@ pub(crate) enum InvocationHelp { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "unexpected `cfg` condition value: {$has_value -> [true] `{$value}` @@ -2963,7 +2995,7 @@ pub(crate) enum CargoHelp { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("extern crate `{$extern_crate}` is unused in crate `{$local_crate}`")] #[help("remove the dependency or add `use {$extern_crate} as _;` to the crate root")] pub(crate) struct UnusedCrateDependency { @@ -2972,7 +3004,7 @@ pub(crate) struct UnusedCrateDependency { } // FIXME(jdonszelmann): duplicated in rustc_attr_parsing, should be moved there completely. -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "{$num_suggestions -> [1] attribute must be of the form {$suggestions} @@ -2987,7 +3019,7 @@ pub(crate) struct IllFormedAttributeInput { pub docs: &'static str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unicode codepoint changing visible direction of text present in comment")] #[note( "these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen" @@ -3027,7 +3059,7 @@ pub(crate) struct UnicodeTextFlowSuggestion { pub spans: Vec, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition" )] @@ -3046,14 +3078,14 @@ pub(crate) struct AbsPathWithModuleSugg { pub replacement: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("hidden lifetime parameters in types are deprecated")] pub(crate) struct ElidedLifetimesInPaths { #[subdiagnostic] pub subdiag: ElidedLifetimeInPathSubdiag, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "{$num_snippets -> [one] unused import: {$span_snippets} @@ -3097,28 +3129,43 @@ pub(crate) enum UnusedImportsSugg { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("the item `{$ident}` is imported redundantly")] pub(crate) struct RedundantImport { #[subdiagnostic] pub subs: Vec, - pub ident: Ident, } #[derive(Subdiagnostic)] pub(crate) enum RedundantImportSub { #[label("the item `{$ident}` is already imported here")] - ImportedHere(#[primary_span] Span), + ImportedHere { + #[primary_span] + span: Span, + ident: Ident, + }, #[label("the item `{$ident}` is already defined here")] - DefinedHere(#[primary_span] Span), + DefinedHere { + #[primary_span] + span: Span, + ident: Ident, + }, #[label("the item `{$ident}` is already imported by the extern prelude")] - ImportedPrelude(#[primary_span] Span), + ImportedPrelude { + #[primary_span] + span: Span, + ident: Ident, + }, #[label("the item `{$ident}` is already defined by the extern prelude")] - DefinedPrelude(#[primary_span] Span), + DefinedPrelude { + #[primary_span] + span: Span, + ident: Ident, + }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum PatternsInFnsWithoutBody { #[diag("patterns aren't allowed in foreign function declarations")] Foreign { @@ -3145,7 +3192,7 @@ pub(crate) struct PatternsInFnsWithoutBodySub { pub ident: Ident, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("prefix `{$prefix}` is unknown")] pub(crate) struct ReservedPrefix { #[label("unknown prefix")] @@ -3160,7 +3207,7 @@ pub(crate) struct ReservedPrefix { pub prefix: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("prefix `'r` is reserved")] pub(crate) struct RawPrefix { #[label("reserved prefix")] @@ -3173,7 +3220,7 @@ pub(crate) struct RawPrefix { pub suggestion: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression" )] @@ -3191,7 +3238,7 @@ pub(crate) struct BreakWithLabelAndLoopSub { pub right: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("where clause not allowed here")] #[note("see issue #89122 for more information")] pub(crate) struct DeprecatedWhereClauseLocation { @@ -3220,7 +3267,7 @@ pub(crate) enum DeprecatedWhereClauseLocationSugg { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("lifetime parameter `{$ident}` only used once")] pub(crate) struct SingleUseLifetime { #[label("this lifetime...")] @@ -3244,7 +3291,7 @@ pub(crate) struct SingleUseLifetimeSugg { pub replace_lt: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("lifetime parameter `{$ident}` never used")] pub(crate) struct UnusedLifetime { #[suggestion("elide the unused lifetime", code = "", applicability = "machine-applicable")] @@ -3253,7 +3300,7 @@ pub(crate) struct UnusedLifetime { pub ident: Ident, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("named argument `{$named_arg_name}` is not used by name")] pub(crate) struct NamedArgumentUsedPositionally { #[label("this named argument is referred to by position in formatting string")] @@ -3272,7 +3319,7 @@ pub(crate) struct NamedArgumentUsedPositionally { pub named_arg_name: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("ambiguous glob re-exports")] pub(crate) struct AmbiguousGlobReexports { #[label("the name `{$name}` in the {$namespace} namespace is first re-exported here")] @@ -3284,7 +3331,7 @@ pub(crate) struct AmbiguousGlobReexports { pub namespace: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("private item shadows public glob re-export")] pub(crate) struct HiddenGlobReexports { #[note( @@ -3298,7 +3345,7 @@ pub(crate) struct HiddenGlobReexports { pub namespace: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unnecessary qualification")] pub(crate) struct UnusedQualifications { #[suggestion( @@ -3310,7 +3357,7 @@ pub(crate) struct UnusedQualifications { pub removal_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "{$elided -> [true] `&` without an explicit lifetime name cannot be used here @@ -3332,7 +3379,7 @@ pub(crate) struct AssociatedConstElidedLifetime { pub lifetimes_in_scope: MultiSpan, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("creating a {$shared_label}reference to mutable static")] pub(crate) struct RefOfMutStatic<'a> { #[label("{$shared_label}reference to mutable static")] @@ -3372,11 +3419,11 @@ pub(crate) enum MutRefSugg { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`use` of a local item without leading `self::`, `super::`, or `crate::`")] -pub(crate) struct UnqualifiedLocalImportsDiag {} +pub(crate) struct UnqualifiedLocalImportsDiag; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("will be parsed as a guarded string in Rust 2024")] pub(crate) struct ReservedString { #[suggestion( @@ -3387,7 +3434,7 @@ pub(crate) struct ReservedString { pub suggestion: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("reserved token in Rust 2024")] pub(crate) struct ReservedMultihash { #[suggestion( @@ -3398,7 +3445,7 @@ pub(crate) struct ReservedMultihash { pub suggestion: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("direct cast of function item into an integer")] pub(crate) struct FunctionCastsAsIntegerDiag<'tcx> { #[subdiagnostic] @@ -3426,8 +3473,8 @@ pub(crate) struct MismatchedLifetimeSyntaxes { pub suggestions: Vec, } -impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for MismatchedLifetimeSyntaxes { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) { +impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MismatchedLifetimeSyntaxes { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let counts = self.inputs.len() + self.outputs.len(); let message = match counts { LifetimeSyntaxCategories { hidden: 0, elided: 0, named: 0 } => { @@ -3450,7 +3497,7 @@ fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) { msg!("hiding or eliding a lifetime that's named elsewhere is confusing") } }; - diag.primary_message(message); + let mut diag = Diag::new(dcx, level, message); for s in self.inputs.hidden { diag.span_label(s, msg!("the lifetime is hidden here")); @@ -3485,6 +3532,7 @@ fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) { diag.subdiagnostic(s); } } + diag } } @@ -3581,9 +3629,9 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } Explicit { lifetime_name, suggestions, optional_alternative } => { - diag.arg("lifetime_name", lifetime_name); - let msg = diag.eagerly_translate(msg!("consistently use `{$lifetime_name}`")); - diag.remove_arg("lifetime_name"); + let msg = msg!("consistently use `{$lifetime_name}`") + .arg("lifetime_name", lifetime_name) + .format(); diag.multipart_suggestion_with_style( msg, suggestions, @@ -3595,7 +3643,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused attribute")] #[note( "{$valid_without_list -> @@ -3617,7 +3665,7 @@ pub(crate) struct EmptyAttributeList { pub valid_without_list: bool, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`#[{$name}]` attribute cannot be used on {$target}")] #[warning( "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" @@ -3637,7 +3685,7 @@ pub(crate) struct InvalidTargetLint { pub attr_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "{$is_used_as_inner -> [false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]` @@ -3652,7 +3700,7 @@ pub(crate) struct InvalidAttrStyle { pub target: &'static str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused attribute")] pub(crate) struct UnusedDuplicate { #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] @@ -3665,28 +3713,28 @@ pub(crate) struct UnusedDuplicate { pub warning: bool, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("malformed `doc` attribute input")] #[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 MalformedDoc; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("didn't expect any arguments here")] #[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 ExpectedNoArgs; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("expected this to be of the form `... = \"...\"`")] #[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 ExpectedNameValue; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unsafe attribute used without unsafe")] pub(crate) struct UnsafeAttrOutsideUnsafeLint { #[label("usage of unsafe attribute")] @@ -3704,7 +3752,7 @@ pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { pub right: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("visibility qualifiers have no effect on `const _` declarations")] #[note("`const _` does not declare a name, so there is nothing for the qualifier to apply to")] pub(crate) struct UnusedVisibility { @@ -3717,38 +3765,38 @@ pub(crate) struct UnusedVisibility { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("doc alias is duplicated")] pub(crate) struct DocAliasDuplicated { #[label("first defined here")] pub first_defn: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`")] pub(crate) struct DocAutoCfgExpectsHideOrShow; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("there exists a built-in attribute with the same name")] pub(crate) struct AmbiguousDeriveHelpers; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items")] pub(crate) struct DocAutoCfgHideShowUnexpectedItem { pub attr_name: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items")] pub(crate) struct DocAutoCfgHideShowExpectsList { pub attr_name: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("invalid `doc` attribute")] pub(crate) struct DocInvalid; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unknown `doc` attribute `include`")] pub(crate) struct DocUnknownInclude { pub inner: &'static str, @@ -3760,7 +3808,7 @@ pub(crate) struct DocUnknownInclude { pub sugg: (Span, Applicability), } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unknown `doc` attribute `spotlight`")] #[note("`doc(spotlight)` was renamed to `doc(notable_trait)`")] #[note("`doc(spotlight)` is now a no-op")] @@ -3774,7 +3822,7 @@ pub(crate) struct DocUnknownSpotlight { pub sugg_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unknown `doc` attribute `{$name}`")] #[note( "`doc` attribute `{$name}` no longer functions; see issue #44136 " @@ -3786,7 +3834,7 @@ pub(crate) struct DocUnknownPasses { pub note_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unknown `doc` attribute `plugins`")] #[note( "`doc` attribute `plugins` no longer functions; see issue #44136 and CVE-2018-1000622 " @@ -3797,42 +3845,42 @@ pub(crate) struct DocUnknownPlugins { pub label_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unknown `doc` attribute `{$name}`")] pub(crate) struct DocUnknownAny { pub name: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("expected boolean for `#[doc(auto_cfg = ...)]`")] pub(crate) struct DocAutoCfgWrongLiteral; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`#[doc(test(...)]` takes a list of attributes")] pub(crate) struct DocTestTakesList; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unknown `doc(test)` attribute `{$name}`")] pub(crate) struct DocTestUnknown { pub name: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`#![doc(test(...)]` does not take a literal")] pub(crate) struct DocTestLiteral; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("this attribute can only be applied at the crate level")] #[note( "read for more information" )] pub(crate) struct AttrCrateLevelOnly; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`#[diagnostic::do_not_recommend]` does not expect any arguments")] pub(crate) struct DoNotRecommendDoesNotExpectArgs; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("invalid `crate_type` value")] pub(crate) struct UnknownCrateTypes { #[subdiagnostic] @@ -3847,14 +3895,14 @@ pub(crate) struct UnknownCrateTypesSuggestion { pub snippet: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unreachable configuration predicate")] pub(crate) struct UnreachableCfgSelectPredicate { #[label("this configuration predicate is never reached")] pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unreachable configuration predicate")] pub(crate) struct UnreachableCfgSelectPredicateWildcard { #[label("this configuration predicate is never reached")] @@ -3863,3 +3911,65 @@ pub(crate) struct UnreachableCfgSelectPredicateWildcard { #[label("always matches")] pub wildcard_span: Span, } + +#[derive(Diagnostic)] +#[diag("positional format arguments are not allowed here")] +#[help( + "only named format arguments with the name of one of the generic types are allowed in this context" +)] +pub(crate) struct DisallowedPositionalArgument; + +#[derive(Diagnostic)] +#[diag("invalid format specifier")] +#[help("no format specifier are supported in this position")] +pub(crate) struct InvalidFormatSpecifier; + +#[derive(Diagnostic)] +#[diag("{$description}")] +pub(crate) struct WrappedParserError<'a> { + pub description: &'a str, + #[label("{$label}")] + pub span: Span, + pub label: &'a str, +} + +#[derive(Diagnostic)] +#[diag("`{$option_name}` is ignored due to previous definition of `{$option_name}`")] +pub(crate) struct IgnoredDiagnosticOption { + pub option_name: Symbol, + #[label("`{$option_name}` is first declared here")] + pub first_span: Span, + #[label("`{$option_name}` is later redundantly declared here")] + pub later_span: Span, +} + +#[derive(Diagnostic)] +#[diag("missing options for `on_unimplemented` attribute")] +#[help("at least one of the `message`, `note` and `label` options are expected")] +pub(crate) struct MissingOptionsForOnUnimplementedAttr; + +#[derive(Diagnostic)] +#[diag("missing options for `on_const` attribute")] +#[help("at least one of the `message`, `note` and `label` options are expected")] +pub(crate) struct MissingOptionsForOnConstAttr; + +#[derive(Diagnostic)] +#[diag("malformed `on_unimplemented` attribute")] +#[help("only `message`, `note` and `label` are allowed as options")] +pub(crate) struct MalformedOnUnimplementedAttrLint { + #[label("invalid option found here")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag("malformed `on_const` attribute")] +#[help("only `message`, `note` and `label` are allowed as options")] +pub(crate) struct MalformedOnConstAttrLint { + #[label("invalid option found here")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")] +#[note("this method was used to add checks to the `Eq` derive macro")] +pub(crate) struct EqInternalMethodImplemented; diff --git a/compiler/rustc_lint/src/map_unit_fn.rs b/compiler/rustc_lint/src/map_unit_fn.rs index 18a947dc1ee0..50471b40ecde 100644 --- a/compiler/rustc_lint/src/map_unit_fn.rs +++ b/compiler/rustc_lint/src/map_unit_fn.rs @@ -1,5 +1,5 @@ use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; -use rustc_middle::ty::{self}; +use rustc_middle::ty; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index bb04da96140a..7f137d5bf060 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -1,5 +1,6 @@ use rustc_ast as ast; -use rustc_errors::{Applicability, msg}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level, msg}; +use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::{bug, ty}; @@ -85,49 +86,26 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { } } -fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Lit(lit) = &arg.kind { - if let ast::LitKind::Str(sym, _) = lit.node { - // The argument is a string literal. - check_panic_str(cx, f, arg, sym.as_str()); - return; - } - } +struct PanicMessageNotLiteral<'a, 'tcx> { + arg_span: Span, + symbol: Symbol, + span: Span, + arg_macro: Option, + cx: &'a LateContext<'tcx>, + arg: &'tcx hir::Expr<'tcx>, + panic: Option, +} - // The argument is *not* a string literal. - - let (span, panic, symbol) = panic_call(cx, f); - - if span.in_external_macro(cx.sess().source_map()) { - // Nothing that can be done about it in the current crate. - return; - } - - // Find the span of the argument to `panic!()` or `unreachable!`, before expansion in the - // case of `panic!(some_macro!())` or `unreachable!(some_macro!())`. - // We don't use source_callsite(), because this `panic!(..)` might itself - // be expanded from another macro, in which case we want to stop at that - // expansion. - let mut arg_span = arg.span; - let mut arg_macro = None; - while !span.contains(arg_span) { - let ctxt = arg_span.ctxt(); - if ctxt.is_root() { - break; - } - let expn = ctxt.outer_expn_data(); - arg_macro = expn.macro_def_id; - arg_span = expn.call_site; - } - - cx.span_lint(NON_FMT_PANICS, arg_span, |lint| { - lint.primary_message(msg!("panic message is not a string literal")); - lint.arg("name", symbol); - lint.note(msg!("this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021")); - lint.note(msg!("for more information, see ")); +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) + .with_note(msg!("this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021")) + .with_note("for more information, see "); if !is_arg_inside_call(arg_span, span) { // No clue where this argument is coming from. - return; + return lint; } if arg_macro.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) { // A case of `panic!(format!(..))`. @@ -215,7 +193,50 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc } } } - }); + lint + } +} + +fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::Lit(lit) = &arg.kind { + if let ast::LitKind::Str(sym, _) = lit.node { + // The argument is a string literal. + check_panic_str(cx, f, arg, sym.as_str()); + return; + } + } + + // The argument is *not* a string literal. + + let (span, panic, symbol) = panic_call(cx, f); + + if span.in_external_macro(cx.sess().source_map()) { + // Nothing that can be done about it in the current crate. + return; + } + + // Find the span of the argument to `panic!()` or `unreachable!`, before expansion in the + // case of `panic!(some_macro!())` or `unreachable!(some_macro!())`. + // We don't use source_callsite(), because this `panic!(..)` might itself + // be expanded from another macro, in which case we want to stop at that + // expansion. + let mut arg_span = arg.span; + let mut arg_macro = None; + while !span.contains(arg_span) { + let ctxt = arg_span.ctxt(); + if ctxt.is_root() { + break; + } + let expn = ctxt.outer_expn_data(); + arg_macro = expn.macro_def_id; + arg_span = expn.call_site; + } + + cx.emit_span_lint( + NON_FMT_PANICS, + arg_span, + PanicMessageNotLiteral { arg_span, symbol, span, arg_macro, cx, arg, panic }, + ); } fn check_panic_str<'tcx>( diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 8fb6532e68c4..ac7155eabfdc 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,5 +1,4 @@ use rustc_errors::{MultiSpan, msg}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind, find_attr}; @@ -79,7 +78,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { // Per RFC we (currently) ignore anon-const (`const _: Ty = ...`) in top-level module. if self.body_depth == 1 - && parent_def_kind == DefKind::Const + && matches!(parent_def_kind, DefKind::Const { .. }) && parent_opt_item_name == Some(kw::Underscore) { return; @@ -160,7 +159,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { // for impl, otherwise the item-def and impl-def won't have the same parent. let outermost_impl_parent = peel_parent_while(cx.tcx, parent, |tcx, did| { tcx.def_kind(did) == DefKind::Mod - || (tcx.def_kind(did) == DefKind::Const + || (matches!(tcx.def_kind(did), DefKind::Const { .. }) && tcx.opt_item_name(did) == Some(kw::Underscore)) }); @@ -180,20 +179,22 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { // Get the span of the parent const item ident (if it's a not a const anon). // // Used to suggest changing the const item to a const anon. - let span_for_const_anon_suggestion = if parent_def_kind == DefKind::Const - && parent_opt_item_name != Some(kw::Underscore) - && let Some(parent) = parent.as_local() - && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) - && let ItemKind::Const(ident, _, ty, _) = item.kind - && let TyKind::Tup(&[]) = ty.kind - { - Some(ident.span) - } else { - None - }; + let span_for_const_anon_suggestion = + if matches!(parent_def_kind, DefKind::Const { .. }) + && parent_opt_item_name != Some(kw::Underscore) + && let Some(parent) = parent.as_local() + && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) + && let ItemKind::Const(ident, _, ty, _) = item.kind + && let TyKind::Tup(&[]) = ty.kind + { + Some(ident.span) + } else { + None + }; - let const_anon = matches!(parent_def_kind, DefKind::Const | DefKind::Static { .. }) - .then_some(span_for_const_anon_suggestion); + let const_anon = + matches!(parent_def_kind, DefKind::Const { .. } | DefKind::Static { .. }) + .then_some(span_for_const_anon_suggestion); let impl_span = item.span.shrink_to_lo().to(impl_.self_ty.span); let mut ms = MultiSpan::from_span(impl_span); @@ -243,10 +244,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { ) } ItemKind::Macro(_, _macro, _kinds) - if find_attr!( - cx.tcx.get_all_attrs(item.owner_id.def_id), - AttributeKind::MacroExport { .. } - ) => + if find_attr!(cx.tcx, item.owner_id.def_id, MacroExport { .. }) => { cx.emit_span_lint( NON_LOCAL_DEFINITIONS, @@ -319,7 +317,7 @@ fn did_has_local_parent( peel_parent_while(tcx, parent_did, |tcx, did| { tcx.def_kind(did) == DefKind::Mod - || (tcx.def_kind(did) == DefKind::Const + || (matches!(tcx.def_kind(did), DefKind::Const { .. }) && tcx.opt_item_name(did) == Some(kw::Underscore)) }) .map(|parent_did| parent_did == impl_parent || Some(parent_did) == outermost_impl_parent) diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index b90abe0a24f1..1fd6699e2506 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,6 +1,8 @@ use rustc_abi::ExternAbi; +use rustc_ast as ast; use rustc_attr_parsing::AttributeParser; -use rustc_errors::Applicability; +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; @@ -12,7 +14,6 @@ use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::{BytePos, Ident, Span, sym}; -use {rustc_ast as ast, rustc_hir as hir}; use crate::lints::{ NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub, @@ -322,7 +323,7 @@ fn check_mod(&mut self, cx: &LateContext<'_>, _: &'tcx hir::Mod<'tcx>, id: hir:: let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name { Some(Ident::from_str(name)) } else { - find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::CrateName{name, name_span,..} => (name, name_span)).map( + find_attr!(cx.tcx, crate, CrateName{name, name_span,..} => (name, name_span)).map( |(&name, &span)| { // Discard the double quotes surrounding the literal. let sp = cx @@ -335,8 +336,7 @@ fn check_mod(&mut self, cx: &LateContext<'_>, _: &'tcx hir::Mod<'tcx>, id: hir:: let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?; Some( - span - .with_lo(span.lo() + BytePos(left as u32 + 1)) + span.with_lo(span.lo() + BytePos(left as u32 + 1)) .with_hi(span.hi() - BytePos(right as u32)), ) }) @@ -370,9 +370,7 @@ fn check_fn( match &fk { FnKind::Method(ident, sig, ..) => match cx.tcx.associated_item(id).container { AssocContainer::InherentImpl => { - if sig.header.abi != ExternAbi::Rust - && find_attr!(cx.tcx.get_all_attrs(id), AttributeKind::NoMangle(..)) - { + if sig.header.abi != ExternAbi::Rust && find_attr!(cx.tcx, id, NoMangle(..)) { return; } self.check_snake_case(cx, "method", ident); @@ -384,9 +382,7 @@ fn check_fn( }, FnKind::ItemFn(ident, _, header) => { // Skip foreign-ABI #[no_mangle] functions (Issue #31924) - if header.abi != ExternAbi::Rust - && find_attr!(cx.tcx.get_all_attrs(id), AttributeKind::NoMangle(..)) - { + if header.abi != ExternAbi::Rust && find_attr!(cx.tcx, id, NoMangle(..)) { return; } self.check_snake_case(cx, "function", ident); @@ -466,6 +462,19 @@ fn check_struct_def(&mut self, cx: &LateContext<'_>, s: &hir::VariantData<'_>) { declare_lint_pass!(NonUpperCaseGlobals => [NON_UPPER_CASE_GLOBALS]); +struct NonUpperCaseGlobalGenerator<'a, F: FnOnce() -> NonUpperCaseGlobal<'a>> { + callback: F, +} + +impl<'a, 'b, F: FnOnce() -> NonUpperCaseGlobal<'b>> Diagnostic<'a, ()> + for NonUpperCaseGlobalGenerator<'b, F> +{ + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { callback } = self; + callback().into_diag(dcx, level) + } +} + impl NonUpperCaseGlobals { fn check_upper_case(cx: &LateContext<'_>, sort: &str, did: Option, ident: &Ident) { let name = ident.name.as_str(); @@ -522,7 +531,7 @@ fn visit_path( } } - cx.emit_span_lint_lazy(NON_UPPER_CASE_GLOBALS, ident.span, || { + let callback = || { // Compute usages lazily as it can expansive and useless when the lint is allowed. // cf. https://github.com/rust-lang/rust/pull/142645#issuecomment-2993024625 let usages = if can_change_usages @@ -542,7 +551,12 @@ fn visit_path( }; NonUpperCaseGlobal { sort, name, sub, usages } - }); + }; + cx.emit_span_lint( + NON_UPPER_CASE_GLOBALS, + ident.span, + NonUpperCaseGlobalGenerator { callback }, + ); } } } @@ -551,9 +565,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { let attrs = cx.tcx.hir_attrs(it.hir_id()); match it.kind { - hir::ItemKind::Static(_, ident, ..) - if !find_attr!(attrs, AttributeKind::NoMangle(..)) => - { + hir::ItemKind::Static(_, ident, ..) if !find_attr!(attrs, NoMangle(..)) => { NonUpperCaseGlobals::check_upper_case( cx, "static variable", @@ -594,7 +606,7 @@ fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) { .. }) = p.kind { - if let Res::Def(DefKind::Const, _) = path.res + if let Res::Def(DefKind::Const { .. }, _) = path.res && let [segment] = path.segments { NonUpperCaseGlobals::check_upper_case( diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index b5b57eaa3389..5e43b8c65db4 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -1,6 +1,6 @@ use rustc_hir::{self as hir, AmbigArg}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_macros::{LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::{PrintTraitPredicateExt as _, TraitPredPrintModifiersAndPath}; use rustc_middle::ty::{self, BottomUpFolder, Ty, TypeFoldable}; use rustc_session::{declare_lint, declare_lint_pass}; @@ -201,7 +201,7 @@ fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, AmbigArg> } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("opaque type `{$ty}` does not satisfy its associated type bounds")] struct OpaqueHiddenInferredBoundLint<'tcx> { ty: Ty<'tcx>, diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs index b89e00dcbaae..34c569232fc0 100644 --- a/compiler/rustc_lint/src/ptr_nulls.rs +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -1,5 +1,4 @@ use rustc_ast::LitKind; -use rustc_hir::attrs::AttributeKind; use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind, find_attr}; use rustc_middle::ty::RawPtr; use rustc_session::{declare_lint, declare_lint_pass}; @@ -60,7 +59,7 @@ declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS, INVALID_NULL_ARGUMENTS]); /// This function checks if the expression is from a series of consecutive casts, -/// ie. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either +/// i.e. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either /// a fn ptr, a reference, or a function call whose definition is /// annotated with `#![rustc_never_returns_null_ptr]`. /// If this situation is present, the function returns the appropriate diagnostic. @@ -73,14 +72,14 @@ fn useless_check<'a, 'tcx: 'a>( e = e.peel_blocks(); if let ExprKind::MethodCall(_, _expr, [], _) = e.kind && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcNeverReturnsNullPointer) + && find_attr!(cx.tcx, def_id, RustcNeverReturnsNullPtr) && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) { return Some(UselessPtrNullChecksDiag::FnRet { fn_name }); } else if let ExprKind::Call(path, _args) = e.kind && let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() - && find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcNeverReturnsNullPointer) + && find_attr!(cx.tcx, def_id, RustcNeverReturnsNullPtr) && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) { return Some(UselessPtrNullChecksDiag::FnRet { fn_name }); diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs index 6ef9d4e9769c..6052a16a7f11 100644 --- a/compiler/rustc_lint/src/reference_casting.rs +++ b/compiler/rustc_lint/src/reference_casting.rs @@ -1,7 +1,7 @@ use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, UnOp}; +use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf as _, TyAndLayout}; -use rustc_middle::ty::{self}; use rustc_session::{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 e4716c869c5f..cbb9ca6aa00f 100644 --- a/compiler/rustc_lint/src/transmute.rs +++ b/compiler/rustc_lint/src/transmute.rs @@ -1,9 +1,9 @@ use rustc_ast::LitKind; use rustc_errors::Applicability; +use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{self as hir}; -use rustc_macros::LintDiagnostic; +use rustc_macros::Diagnostic; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::sym; @@ -216,7 +216,7 @@ fn check_ptr_transmute_in_const<'tcx>( dst: Ty<'tcx>, ) { if matches!(const_context, Some(hir::ConstContext::ConstFn)) - || matches!(cx.tcx.def_kind(body_owner_def_id), DefKind::AssocConst) + || matches!(cx.tcx.def_kind(body_owner_def_id), DefKind::AssocConst { .. }) { if src.is_raw_ptr() && dst.is_integral() { cx.tcx.emit_node_span_lint( @@ -368,7 +368,7 @@ fn check_unnecessary_transmute<'tcx>( }); } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("pointers cannot be transmuted to integers during const eval")] #[note("at compile-time, pointers do not have an integer value")] #[note( diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 47e1fef8b82e..2daf2a150354 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,14 +1,15 @@ use std::iter; use rustc_abi::{BackendRepr, TagEncoding, Variants, WrappingRange}; -use rustc_hir::{Expr, ExprKind, HirId, LangItem}; +use rustc_ast as ast; +use rustc_hir as hir; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, find_attr}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::{Span, Symbol, sym}; use tracing::debug; -use {rustc_ast as ast, rustc_hir as hir}; mod improper_ctypes; // these files do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations pub(crate) use improper_ctypes::ImproperCTypesLint; @@ -686,7 +687,7 @@ pub(crate) fn nonnull_optimization_guaranteed<'tcx>( tcx: TyCtxt<'tcx>, def: ty::AdtDef<'tcx>, ) -> bool { - tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed) + find_attr!(tcx, def.did(), RustcNonnullOptimizationGuaranteed) } /// `repr(transparent)` structs can have a single non-1-ZST field, this function returns that @@ -1036,31 +1037,13 @@ fn inherent_atomic_method_call<'hir>( expr: &Expr<'hir>, recognized_names: &[Symbol], // used for fast path calculation ) -> Option<(Symbol, &'hir [Expr<'hir>])> { - const ATOMIC_TYPES: &[Symbol] = &[ - sym::AtomicBool, - sym::AtomicPtr, - sym::AtomicUsize, - sym::AtomicU8, - sym::AtomicU16, - sym::AtomicU32, - sym::AtomicU64, - sym::AtomicU128, - sym::AtomicIsize, - sym::AtomicI8, - sym::AtomicI16, - sym::AtomicI32, - sym::AtomicI64, - sym::AtomicI128, - ]; if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind && recognized_names.contains(&method_path.ident.name) && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) // skip extension traits, only lint functions from the standard library && let Some(impl_did) = cx.tcx.inherent_impl_of_assoc(m_def_id) && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() - && let parent = cx.tcx.parent(adt.did()) - && cx.tcx.is_diagnostic_item(sym::atomic_mod, parent) - && ATOMIC_TYPES.contains(&cx.tcx.item_name(adt.did())) + && cx.tcx.is_diagnostic_item(sym::Atomic, adt.did()) { return Some((method_path.ident.name, args)); } diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index b6c67549c47e..45d79fec75ae 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -2,12 +2,13 @@ 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_hir as hir; use rustc_hir::{HirId, attrs}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::{bug, ty}; use rustc_span::{Span, Symbol}; -use {rustc_ast as ast, rustc_hir as hir}; use crate::LateContext; use crate::context::LintContext; @@ -157,8 +158,21 @@ fn report_bin_hex_error( (t.name_str(), actually.to_string()) } }; - let sign = - if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive }; + let sign = if negative { + OverflowingBinHexSign::Negative { + lit: repr_str.clone(), + dec: val, + actually: actually.clone(), + ty: t, + } + } else { + OverflowingBinHexSign::Positive { + lit: repr_str.clone(), + dec: val, + actually: actually.clone(), + ty: t, + } + }; let sub = get_type_suggestion(cx.typeck_results().node_type(hir_id), val, negative).map( |suggestion_ty| { if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { @@ -194,7 +208,7 @@ fn report_bin_hex_error( Some(OverflowingBinHexSignBitSub { span, lit_no_suffix, - negative_val: actually.clone(), + negative_val: actually, int_ty: int_ty.name_str(), uint_ty: Integer::fit_unsigned(val).uint_ty_str(), }) @@ -204,15 +218,7 @@ fn report_bin_hex_error( cx.emit_span_lint( OVERFLOWING_LITERALS, span, - OverflowingBinHex { - ty: t, - lit: repr_str.clone(), - dec: val, - actually, - sign, - sub, - sign_bit_sub, - }, + OverflowingBinHex { ty: t, sign, sub, sign_bit_sub }, ) } diff --git a/compiler/rustc_lint/src/unqualified_local_imports.rs b/compiler/rustc_lint/src/unqualified_local_imports.rs index 0076cae3cff3..888bf026b4ed 100644 --- a/compiler/rustc_lint/src/unqualified_local_imports.rs +++ b/compiler/rustc_lint/src/unqualified_local_imports.rs @@ -1,4 +1,4 @@ -use rustc_hir::{self as hir}; +use rustc_hir as hir; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::kw; @@ -77,7 +77,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { cx.emit_span_lint( UNQUALIFIED_LOCAL_IMPORTS, first_seg.ident.span, - lints::UnqualifiedLocalImportsDiag {}, + lints::UnqualifiedLocalImportsDiag, ); } } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 8b2a6d1d2ab5..03a566efc8a5 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1,580 +1,20 @@ -use std::iter; - use rustc_ast::util::{classify, parser}; use rustc_ast::{self as ast, ExprKind, FnRetTy, HasAttrs as _, StmtKind}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{MultiSpan, pluralize}; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem, find_attr}; -use rustc_infer::traits::util::elaborate; -use rustc_middle::ty::{self, Ty, adjustment}; +use rustc_errors::MultiSpan; +use rustc_hir::{self as hir}; +use rustc_middle::ty::{self, adjustment}; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::edition::Edition::Edition2015; -use rustc_span::{BytePos, Span, Symbol, kw, sym}; -use tracing::instrument; +use rustc_span::{BytePos, Span, kw, sym}; use crate::lints::{ PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag, - UnusedAllocationMutDiag, UnusedClosure, UnusedCoroutine, UnusedDef, UnusedDefSuggestion, - UnusedDelim, UnusedDelimSuggestion, UnusedImportBracesDiag, UnusedOp, UnusedOpSuggestion, - UnusedResult, + UnusedAllocationMutDiag, UnusedDelim, UnusedDelimSuggestion, UnusedImportBracesDiag, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Lint, LintContext}; -declare_lint! { - /// The `unused_must_use` lint detects unused result of a type flagged as - /// `#[must_use]`. - /// - /// ### Example - /// - /// ```rust - /// fn returns_result() -> Result<(), ()> { - /// Ok(()) - /// } - /// - /// fn main() { - /// returns_result(); - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// The `#[must_use]` attribute is an indicator that it is a mistake to - /// ignore the value. See [the reference] for more details. - /// - /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - pub UNUSED_MUST_USE, - Warn, - "unused result of a type flagged as `#[must_use]`", - report_in_external_macro -} - -declare_lint! { - /// The `unused_results` lint checks for the unused result of an - /// expression in a statement. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![deny(unused_results)] - /// fn foo() -> T { panic!() } - /// - /// fn main() { - /// foo::(); - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Ignoring the return value of a function may indicate a mistake. In - /// cases were it is almost certain that the result should be used, it is - /// recommended to annotate the function with the [`must_use` attribute]. - /// Failure to use such a return value will trigger the [`unused_must_use` - /// lint] which is warn-by-default. The `unused_results` lint is - /// essentially the same, but triggers for *all* return values. - /// - /// This lint is "allow" by default because it can be noisy, and may not be - /// an actual problem. For example, calling the `remove` method of a `Vec` - /// or `HashMap` returns the previous value, which you may not care about. - /// Using this lint would require explicitly ignoring or discarding such - /// values. - /// - /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use - pub UNUSED_RESULTS, - Allow, - "unused result of an expression in a statement" -} - -declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]); - -impl<'tcx> LateLintPass<'tcx> for UnusedResults { - fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { - let hir::StmtKind::Semi(mut expr) = s.kind else { - return; - }; - - let mut expr_is_from_block = false; - while let hir::ExprKind::Block(blk, ..) = expr.kind - && let hir::Block { expr: Some(e), .. } = blk - { - expr = e; - expr_is_from_block = true; - } - - if let hir::ExprKind::Ret(..) = expr.kind { - return; - } - - 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::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind() - && cx.tcx.ty_is_opaque_future(ty) - && let async_fn_def_id = cx.tcx.parent(*future_def_id) - && matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn) - // Check that this `impl Future` actually comes from an `async fn` - && cx.tcx.asyncness(async_fn_def_id).is_async() - && check_must_use_def( - cx, - async_fn_def_id, - expr.span, - "output of future returned by ", - "", - expr_is_from_block, - ) - { - // We have a bare `foo().await;` on an opaque type from an async function that was - // annotated with `#[must_use]`. - return; - } - - let ty = cx.typeck_results().expr_ty(expr); - - let must_use_result = is_ty_must_use(cx, ty, expr, expr.span); - let type_lint_emitted_or_suppressed = match must_use_result { - Some(path) => { - emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block); - true - } - None => false, - }; - - let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block); - - if !fn_warned && type_lint_emitted_or_suppressed { - // We don't warn about unused unit or uninhabited types. - // (See https://github.com/rust-lang/rust/issues/43806 for details.) - return; - } - - let must_use_op = match expr.kind { - // Hardcoding operators here seemed more expedient than the - // refactoring that would be needed to look up the `#[must_use]` - // attribute which does exist on the comparison trait methods - hir::ExprKind::Binary(bin_op, ..) => match bin_op.node { - hir::BinOpKind::Eq - | hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Ne - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt => Some("comparison"), - hir::BinOpKind::Add - | hir::BinOpKind::Sub - | hir::BinOpKind::Div - | hir::BinOpKind::Mul - | hir::BinOpKind::Rem => Some("arithmetic operation"), - hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"), - hir::BinOpKind::BitXor - | hir::BinOpKind::BitAnd - | hir::BinOpKind::BitOr - | hir::BinOpKind::Shl - | hir::BinOpKind::Shr => Some("bitwise operation"), - }, - hir::ExprKind::AddrOf(..) => Some("borrow"), - hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"), - hir::ExprKind::Unary(..) => Some("unary operation"), - // The `offset_of` macro wraps its contents inside a `const` block. - hir::ExprKind::ConstBlock(block) => { - let body = cx.tcx.hir_body(block.body); - if let hir::ExprKind::Block(block, _) = body.value.kind - && let Some(expr) = block.expr - && let hir::ExprKind::OffsetOf(..) = expr.kind - { - Some("`offset_of` call") - } else { - None - } - } - _ => None, - }; - - let mut op_warned = false; - - if let Some(must_use_op) = must_use_op { - let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span); - cx.emit_span_lint( - UNUSED_MUST_USE, - expr.span, - UnusedOp { - op: must_use_op, - label: expr.span, - suggestion: if expr_is_from_block { - UnusedOpSuggestion::BlockTailExpr { - before_span: span.shrink_to_lo(), - after_span: span.shrink_to_hi(), - } - } else { - UnusedOpSuggestion::NormalExpr { span: span.shrink_to_lo() } - }, - }, - ); - op_warned = true; - } - - if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) { - cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty }); - } - - fn check_fn_must_use( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - expr_is_from_block: bool, - ) -> bool { - let maybe_def_id = match expr.kind { - hir::ExprKind::Call(callee, _) => { - match callee.kind { - hir::ExprKind::Path(ref qpath) => { - match cx.qpath_res(qpath, callee.hir_id) { - Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id), - // `Res::Local` if it was a closure, for which we - // do not currently support must-use linting - _ => None, - } - } - _ => None, - } - } - hir::ExprKind::MethodCall(..) => { - cx.typeck_results().type_dependent_def_id(expr.hir_id) - } - _ => None, - }; - if let Some(def_id) = maybe_def_id { - check_must_use_def( - cx, - def_id, - expr.span, - "return value of ", - "", - expr_is_from_block, - ) - } else { - false - } - } - - /// A path through a type to a must_use source. Contains useful info for the lint. - #[derive(Debug)] - enum MustUsePath { - /// Suppress must_use checking. - Suppressed, - /// The root of the normal must_use lint with an optional message. - Def(Span, DefId, Option), - Boxed(Box), - Pinned(Box), - Opaque(Box), - TraitObject(Box), - TupleElement(Vec<(usize, Self)>), - Array(Box, u64), - /// The root of the unused_closures lint. - Closure(Span), - /// The root of the unused_coroutines lint. - Coroutine(Span), - } - - #[instrument(skip(cx, expr), level = "debug", ret)] - fn is_ty_must_use<'tcx>( - cx: &LateContext<'tcx>, - ty: Ty<'tcx>, - expr: &hir::Expr<'_>, - span: Span, - ) -> Option { - if ty.is_unit() { - return Some(MustUsePath::Suppressed); - } - let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id(); - let is_uninhabited = - |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env()); - if is_uninhabited(ty) { - return Some(MustUsePath::Suppressed); - } - - match *ty.kind() { - ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => { - is_ty_must_use(cx, boxed, expr, span) - .map(|inner| MustUsePath::Boxed(Box::new(inner))) - } - ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => { - let pinned_ty = args.type_at(0); - is_ty_must_use(cx, pinned_ty, expr, span) - .map(|inner| MustUsePath::Pinned(Box::new(inner))) - } - // Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`). - ty::Adt(def, args) - if cx.tcx.is_diagnostic_item(sym::Result, def.did()) - && args.type_at(0).is_unit() - && is_uninhabited(args.type_at(1)) => - { - Some(MustUsePath::Suppressed) - } - // Suppress warnings on `ControlFlow` (e.g. `ControlFlow`). - ty::Adt(def, args) - if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) - && args.type_at(1).is_unit() - && is_uninhabited(args.type_at(0)) => - { - Some(MustUsePath::Suppressed) - } - ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), - ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { - elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied()) - // We only care about self bounds for the impl-trait - .filter_only_self() - .find_map(|(pred, _span)| { - // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::ClauseKind::Trait(ref poly_trait_predicate) = - pred.kind().skip_binder() - { - let def_id = poly_trait_predicate.trait_ref.def_id; - - is_def_must_use(cx, def_id, span) - } else { - None - } - }) - .map(|inner| MustUsePath::Opaque(Box::new(inner))) - } - ty::Dynamic(binders, _) => binders.iter().find_map(|predicate| { - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() - { - let def_id = trait_ref.def_id; - is_def_must_use(cx, def_id, span) - .map(|inner| MustUsePath::TraitObject(Box::new(inner))) - } else { - None - } - }), - ty::Tuple(tys) => { - let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind { - debug_assert_eq!(elem_exprs.len(), tys.len()); - elem_exprs - } else { - &[] - }; - - // Default to `expr`. - let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr)); - - let nested_must_use = tys - .iter() - .zip(elem_exprs) - .enumerate() - .filter_map(|(i, (ty, expr))| { - is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path)) - }) - .collect::>(); - - if !nested_must_use.is_empty() { - Some(MustUsePath::TupleElement(nested_must_use)) - } else { - None - } - } - ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) { - // If the array is empty we don't lint, to avoid false positives - Some(0) | None => None, - // If the array is definitely non-empty, we can do `#[must_use]` checking. - Some(len) => is_ty_must_use(cx, ty, expr, span) - .map(|inner| MustUsePath::Array(Box::new(inner), len)), - }, - ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)), - ty::Coroutine(def_id, ..) => { - // async fn should be treated as "implementor of `Future`" - let must_use = if cx.tcx.coroutine_is_async(def_id) { - let def_id = cx.tcx.lang_items().future_trait()?; - is_def_must_use(cx, def_id, span) - .map(|inner| MustUsePath::Opaque(Box::new(inner))) - } else { - None - }; - must_use.or(Some(MustUsePath::Coroutine(span))) - } - _ => None, - } - } - - fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option { - if let Some(reason) = find_attr!( - cx.tcx.get_all_attrs(def_id), - AttributeKind::MustUse { reason, .. } => reason - ) { - // check for #[must_use = "..."] - Some(MustUsePath::Def(span, def_id, *reason)) - } else { - None - } - } - - // Returns whether further errors should be suppressed because either a lint has been - // emitted or the type should be ignored. - fn check_must_use_def( - cx: &LateContext<'_>, - def_id: DefId, - span: Span, - descr_pre_path: &str, - descr_post_path: &str, - expr_is_from_block: bool, - ) -> bool { - is_def_must_use(cx, def_id, span) - .map(|must_use_path| { - emit_must_use_untranslated( - cx, - &must_use_path, - descr_pre_path, - descr_post_path, - 1, - false, - expr_is_from_block, - ) - }) - .is_some() - } - - #[instrument(skip(cx), level = "debug")] - fn emit_must_use_untranslated( - cx: &LateContext<'_>, - path: &MustUsePath, - descr_pre: &str, - descr_post: &str, - plural_len: usize, - is_inner: bool, - expr_is_from_block: bool, - ) { - let plural_suffix = pluralize!(plural_len); - - match path { - MustUsePath::Suppressed => {} - MustUsePath::Boxed(path) => { - let descr_pre = &format!("{descr_pre}boxed "); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::Pinned(path) => { - let descr_pre = &format!("{descr_pre}pinned "); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::Opaque(path) => { - let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of "); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::TraitObject(path) => { - let descr_post = &format!(" trait object{plural_suffix}{descr_post}"); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::TupleElement(elems) => { - for (index, path) in elems { - let descr_post = &format!(" in tuple element {index}"); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - } - MustUsePath::Array(path, len) => { - let descr_pre = &format!("{descr_pre}array{plural_suffix} of "); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)), - true, - expr_is_from_block, - ); - } - MustUsePath::Closure(span) => { - cx.emit_span_lint( - UNUSED_MUST_USE, - *span, - UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post }, - ); - } - MustUsePath::Coroutine(span) => { - cx.emit_span_lint( - UNUSED_MUST_USE, - *span, - UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post }, - ); - } - MustUsePath::Def(span, def_id, reason) => { - let ancenstor_span = span.find_ancestor_not_from_macro().unwrap_or(*span); - let is_redundant_let_ignore = cx - .sess() - .source_map() - .span_to_prev_source(ancenstor_span) - .ok() - .map(|prev| prev.trim_end().ends_with("let _ =")) - .unwrap_or(false); - let suggestion_span = - if is_redundant_let_ignore { *span } else { ancenstor_span }; - cx.emit_span_lint( - UNUSED_MUST_USE, - ancenstor_span, - UnusedDef { - pre: descr_pre, - post: descr_post, - 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(), - } - } else { - UnusedDefSuggestion::NormalExpr { - span: suggestion_span.shrink_to_lo(), - } - }), - }, - ); - } - } - } - } -} +pub mod must_use; declare_lint! { /// The `path_statements` lint detects path statements with no effect. @@ -910,6 +350,7 @@ fn emit_unused_delims( start_replace: lo_replace, end_span: hi, end_replace: hi_replace, + delim: Self::DELIM_STR, } }); cx.emit_span_lint( diff --git a/compiler/rustc_lint/src/unused/must_use.rs b/compiler/rustc_lint/src/unused/must_use.rs new file mode 100644 index 000000000000..9eb45666442f --- /dev/null +++ b/compiler/rustc_lint/src/unused/must_use.rs @@ -0,0 +1,645 @@ +use std::iter; + +use rustc_errors::pluralize; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, LangItem, find_attr}; +use rustc_infer::traits::util::elaborate; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::{Span, Symbol, sym}; +use tracing::instrument; + +use crate::lints::{ + UnusedClosure, UnusedCoroutine, UnusedDef, UnusedDefSuggestion, UnusedOp, UnusedOpSuggestion, + UnusedResult, +}; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `unused_must_use` lint detects unused result of a type flagged as + /// `#[must_use]`. + /// + /// ### Example + /// + /// ```rust + /// fn returns_result() -> Result<(), ()> { + /// Ok(()) + /// } + /// + /// fn main() { + /// returns_result(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The `#[must_use]` attribute is an indicator that it is a mistake to + /// ignore the value. See [the reference] for more details. + /// + /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + pub UNUSED_MUST_USE, + Warn, + "unused result of a type flagged as `#[must_use]`", + report_in_external_macro +} + +declare_lint! { + /// The `unused_results` lint checks for the unused result of an + /// expression in a statement. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unused_results)] + /// fn foo() -> T { panic!() } + /// + /// fn main() { + /// foo::(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Ignoring the return value of a function may indicate a mistake. In + /// cases were it is almost certain that the result should be used, it is + /// recommended to annotate the function with the [`must_use` attribute]. + /// Failure to use such a return value will trigger the [`unused_must_use` + /// lint] which is warn-by-default. The `unused_results` lint is + /// essentially the same, but triggers for *all* return values. + /// + /// This lint is "allow" by default because it can be noisy, and may not be + /// an actual problem. For example, calling the `remove` method of a `Vec` + /// or `HashMap` returns the previous value, which you may not care about. + /// Using this lint would require explicitly ignoring or discarding such + /// values. + /// + /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use + pub UNUSED_RESULTS, + Allow, + "unused result of an expression in a statement" +} + +declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]); + +/// Must the type be used? +#[derive(Debug)] +pub enum IsTyMustUse { + /// Yes, `MustUsePath` contains an explanation for why the type must be used. + /// This will result in `unused_must_use` lint. + Yes(MustUsePath), + /// No, an ordinary type that may be ignored. + /// This will result in `unused_results` lint. + No, + /// No, the type is trivial and thus should always be ignored. + /// (this suppresses `unused_results` lint) + Trivial, +} + +impl IsTyMustUse { + fn map(self, f: impl FnOnce(MustUsePath) -> MustUsePath) -> Self { + match self { + Self::Yes(must_use_path) => Self::Yes(f(must_use_path)), + _ => self, + } + } +} + +/// A path through a type to a `must_use` source. Contains useful info for the lint. +#[derive(Debug)] +pub enum MustUsePath { + /// The root of the normal `must_use` lint with an optional message. + Def(Span, DefId, Option), + Boxed(Box), + Pinned(Box), + Opaque(Box), + TraitObject(Box), + TupleElement(Vec<(usize, Self)>), + /// `Result` + Result(Box), + /// `ControlFlow` + ControlFlow(Box), + Array(Box, u64), + /// The root of the unused_closures lint. + Closure(Span), + /// The root of the unused_coroutines lint. + Coroutine(Span), +} + +/// Returns `Some(path)` if `ty` should be considered as "`must_use`" in the context of `expr` +/// (`expr` is used to get the parent module, which can affect which types are considered uninhabited). +/// +/// If `simplify_uninhabited` is true, this function considers `Result` and +/// `ControlFlow` the same as `T` (we don't set this *yet* in rustc, but expose this +/// so clippy can use this). +// +// FIXME: remove `simplify_uninhabited` once clippy had a release with the new semantics. +#[instrument(skip(cx, expr), level = "debug", ret)] +pub fn is_ty_must_use<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + expr: &hir::Expr<'_>, + simplify_uninhabited: bool, +) -> IsTyMustUse { + if ty.is_unit() { + return IsTyMustUse::Trivial; + } + + let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id(); + let is_uninhabited = + |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env()); + + match *ty.kind() { + _ if is_uninhabited(ty) => IsTyMustUse::Trivial, + ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => { + is_ty_must_use(cx, boxed, expr, simplify_uninhabited) + .map(|inner| MustUsePath::Boxed(Box::new(inner))) + } + ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => { + let pinned_ty = args.type_at(0); + is_ty_must_use(cx, pinned_ty, expr, simplify_uninhabited) + .map(|inner| MustUsePath::Pinned(Box::new(inner))) + } + // Consider `Result` (e.g. `Result<(), !>`) equivalent to `T`. + ty::Adt(def, args) + if simplify_uninhabited + && cx.tcx.is_diagnostic_item(sym::Result, def.did()) + && is_uninhabited(args.type_at(1)) => + { + let ok_ty = args.type_at(0); + is_ty_must_use(cx, ok_ty, expr, simplify_uninhabited) + .map(|path| MustUsePath::Result(Box::new(path))) + } + // Consider `ControlFlow` (e.g. `ControlFlow`) equivalent to `T`. + ty::Adt(def, args) + if simplify_uninhabited + && cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) + && is_uninhabited(args.type_at(0)) => + { + let continue_ty = args.type_at(1); + is_ty_must_use(cx, continue_ty, expr, simplify_uninhabited) + .map(|path| MustUsePath::ControlFlow(Box::new(path))) + } + // Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`). + ty::Adt(def, args) + if cx.tcx.is_diagnostic_item(sym::Result, def.did()) + && args.type_at(0).is_unit() + && is_uninhabited(args.type_at(1)) => + { + IsTyMustUse::Trivial + } + // Suppress warnings on `ControlFlow` (e.g. `ControlFlow`). + ty::Adt(def, args) + if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) + && args.type_at(1).is_unit() + && is_uninhabited(args.type_at(0)) => + { + IsTyMustUse::Trivial + } + ty::Adt(def, _) => { + is_def_must_use(cx, def.did(), expr.span).map_or(IsTyMustUse::No, IsTyMustUse::Yes) + } + ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { + elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied()) + // We only care about self bounds for the impl-trait + .filter_only_self() + .find_map(|(pred, _span)| { + // We only look at the `DefId`, so it is safe to skip the binder here. + if let ty::ClauseKind::Trait(ref poly_trait_predicate) = + pred.kind().skip_binder() + { + let def_id = poly_trait_predicate.trait_ref.def_id; + + is_def_must_use(cx, def_id, expr.span) + } else { + None + } + }) + .map(|inner| MustUsePath::Opaque(Box::new(inner))) + .map_or(IsTyMustUse::No, IsTyMustUse::Yes) + } + ty::Dynamic(binders, _) => binders + .iter() + .find_map(|predicate| { + if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { + let def_id = trait_ref.def_id; + is_def_must_use(cx, def_id, expr.span) + .map(|inner| MustUsePath::TraitObject(Box::new(inner))) + } else { + None + } + }) + .map_or(IsTyMustUse::No, IsTyMustUse::Yes), + // NB: unit is checked up above; this is only reachable for tuples with at least one element + ty::Tuple(tys) => { + let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind { + debug_assert_eq!(elem_exprs.len(), tys.len()); + elem_exprs + } else { + &[] + }; + + // Default to `expr`. + let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr)); + + let mut all_trivial = true; + let mut nested_must_use = Vec::new(); + + tys.iter().zip(elem_exprs).enumerate().for_each(|(i, (ty, expr))| { + let must_use = is_ty_must_use(cx, ty, expr, simplify_uninhabited); + + all_trivial &= matches!(must_use, IsTyMustUse::Trivial); + if let IsTyMustUse::Yes(path) = must_use { + nested_must_use.push((i, path)); + } + }); + + if all_trivial { + // If all tuple elements are trivial, mark the whole tuple as such. + // i.e. don't emit `unused_results` for types such as `((), ())` + IsTyMustUse::Trivial + } else if !nested_must_use.is_empty() { + IsTyMustUse::Yes(MustUsePath::TupleElement(nested_must_use)) + } else { + IsTyMustUse::No + } + } + ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) { + // If the array is empty we don't lint, to avoid false positives + Some(0) | None => IsTyMustUse::No, + // If the array is definitely non-empty, we can do `#[must_use]` checking. + Some(len) => is_ty_must_use(cx, ty, expr, simplify_uninhabited) + .map(|inner| MustUsePath::Array(Box::new(inner), len)), + }, + ty::Closure(..) | ty::CoroutineClosure(..) => { + IsTyMustUse::Yes(MustUsePath::Closure(expr.span)) + } + ty::Coroutine(def_id, ..) => { + // async fn should be treated as "implementor of `Future`" + if cx.tcx.coroutine_is_async(def_id) + && let Some(def_id) = cx.tcx.lang_items().future_trait() + { + IsTyMustUse::Yes(MustUsePath::Opaque(Box::new( + is_def_must_use(cx, def_id, expr.span) + .expect("future trait is marked as `#[must_use]`"), + ))) + } else { + IsTyMustUse::Yes(MustUsePath::Coroutine(expr.span)) + } + } + _ => IsTyMustUse::No, + } +} + +impl<'tcx> LateLintPass<'tcx> for UnusedResults { + fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { + let hir::StmtKind::Semi(mut expr) = s.kind else { + return; + }; + + let mut expr_is_from_block = false; + while let hir::ExprKind::Block(blk, ..) = expr.kind + && let hir::Block { expr: Some(e), .. } = blk + { + expr = e; + expr_is_from_block = true; + } + + if let hir::ExprKind::Ret(..) = expr.kind { + return; + } + + 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::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind() + && cx.tcx.ty_is_opaque_future(ty) + && let async_fn_def_id = cx.tcx.parent(*future_def_id) + && matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn) + // Check that this `impl Future` actually comes from an `async fn` + && cx.tcx.asyncness(async_fn_def_id).is_async() + && check_must_use_def( + cx, + async_fn_def_id, + expr.span, + "output of future returned by ", + "", + expr_is_from_block, + ) + { + // We have a bare `foo().await;` on an opaque type from an async function that was + // annotated with `#[must_use]`. + return; + } + + let ty = cx.typeck_results().expr_ty(expr); + + let must_use_result = is_ty_must_use(cx, ty, expr, false); + 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); + true + } + IsTyMustUse::Trivial => true, + IsTyMustUse::No => false, + }; + + let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block); + + if !fn_warned && type_lint_emitted_or_trivial { + // We don't warn about unused unit or uninhabited types. + // (See https://github.com/rust-lang/rust/issues/43806 for details.) + return; + } + + let must_use_op = match expr.kind { + // Hardcoding operators here seemed more expedient than the + // refactoring that would be needed to look up the `#[must_use]` + // attribute which does exist on the comparison trait methods + hir::ExprKind::Binary(bin_op, ..) => match bin_op.node { + hir::BinOpKind::Eq + | hir::BinOpKind::Lt + | hir::BinOpKind::Le + | hir::BinOpKind::Ne + | hir::BinOpKind::Ge + | hir::BinOpKind::Gt => Some("comparison"), + hir::BinOpKind::Add + | hir::BinOpKind::Sub + | hir::BinOpKind::Div + | hir::BinOpKind::Mul + | hir::BinOpKind::Rem => Some("arithmetic operation"), + hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"), + hir::BinOpKind::BitXor + | hir::BinOpKind::BitAnd + | hir::BinOpKind::BitOr + | hir::BinOpKind::Shl + | hir::BinOpKind::Shr => Some("bitwise operation"), + }, + hir::ExprKind::AddrOf(..) => Some("borrow"), + hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"), + hir::ExprKind::Unary(..) => Some("unary operation"), + // The `offset_of` macro wraps its contents inside a `const` block. + hir::ExprKind::ConstBlock(block) => { + let body = cx.tcx.hir_body(block.body); + if let hir::ExprKind::Block(block, _) = body.value.kind + && let Some(expr) = block.expr + && let hir::ExprKind::OffsetOf(..) = expr.kind + { + Some("`offset_of` call") + } else { + None + } + } + _ => None, + }; + + let op_warned = match must_use_op { + Some(must_use_op) => { + let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span); + cx.emit_span_lint( + UNUSED_MUST_USE, + expr.span, + UnusedOp { + op: must_use_op, + label: expr.span, + suggestion: if expr_is_from_block { + UnusedOpSuggestion::BlockTailExpr { + before_span: span.shrink_to_lo(), + after_span: span.shrink_to_hi(), + } + } else { + UnusedOpSuggestion::NormalExpr { span: span.shrink_to_lo() } + }, + }, + ); + true + } + None => false, + }; + + // Only emit unused results lint if we haven't emitted any of the more specific lints and the expression type is non trivial. + if !(type_lint_emitted_or_trivial || fn_warned || op_warned) { + cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty }); + } + } +} + +/// 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 { + let maybe_def_id = match expr.kind { + hir::ExprKind::Call(callee, _) => { + if let hir::ExprKind::Path(ref qpath) = callee.kind + // `Res::Local` if it was a closure, for which we + // do not currently support must-use linting + && let Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) = + cx.qpath_res(qpath, callee.hir_id) + { + Some(def_id) + } else { + None + } + } + hir::ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), + _ => None, + }; + + match maybe_def_id { + Some(def_id) => { + check_must_use_def(cx, def_id, expr.span, "return value of ", "", expr_is_from_block) + } + None => false, + } +} + +fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option { + // check for #[must_use = "..."] + find_attr!(cx.tcx, def_id, MustUse { reason, .. } => reason) + .map(|reason| MustUsePath::Def(span, def_id, *reason)) +} + +/// Returns whether further errors should be suppressed because a lint has been emitted. +fn check_must_use_def( + cx: &LateContext<'_>, + def_id: DefId, + span: Span, + descr_pre_path: &str, + descr_post_path: &str, + expr_is_from_block: bool, +) -> bool { + is_def_must_use(cx, def_id, span) + .map(|must_use_path| { + emit_must_use_untranslated( + cx, + &must_use_path, + descr_pre_path, + descr_post_path, + 1, + false, + expr_is_from_block, + ) + }) + .is_some() +} + +#[instrument(skip(cx), level = "debug")] +fn emit_must_use_untranslated( + cx: &LateContext<'_>, + path: &MustUsePath, + descr_pre: &str, + descr_post: &str, + plural_len: usize, + is_inner: bool, + expr_is_from_block: bool, +) { + let plural_suffix = pluralize!(plural_len); + + match path { + MustUsePath::Boxed(path) => { + let descr_pre = &format!("{descr_pre}boxed "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::Pinned(path) => { + let descr_pre = &format!("{descr_pre}pinned "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::Opaque(path) => { + let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::TraitObject(path) => { + let descr_post = &format!(" trait object{plural_suffix}{descr_post}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::TupleElement(elems) => { + for (index, path) in elems { + let descr_post = &format!(" in tuple element {index}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + } + MustUsePath::Result(path) => { + let descr_post = &format!(" in a `Result` with an uninhabited error{descr_post}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::ControlFlow(path) => { + let descr_post = &format!(" in a `ControlFlow` with an uninhabited break {descr_post}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::Array(path, len) => { + let descr_pre = &format!("{descr_pre}array{plural_suffix} of "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)), + true, + expr_is_from_block, + ); + } + MustUsePath::Closure(span) => { + cx.emit_span_lint( + UNUSED_MUST_USE, + *span, + UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post }, + ); + } + MustUsePath::Coroutine(span) => { + cx.emit_span_lint( + UNUSED_MUST_USE, + *span, + UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post }, + ); + } + MustUsePath::Def(span, def_id, reason) => { + let ancenstor_span = span.find_ancestor_not_from_macro().unwrap_or(*span); + let is_redundant_let_ignore = cx + .sess() + .source_map() + .span_to_prev_source(ancenstor_span) + .ok() + .map(|prev| prev.trim_end().ends_with("let _ =")) + .unwrap_or(false); + let suggestion_span = if is_redundant_let_ignore { *span } else { ancenstor_span }; + cx.emit_span_lint( + UNUSED_MUST_USE, + ancenstor_span, + UnusedDef { + pre: descr_pre, + post: descr_post, + 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(), + } + } else { + UnusedDefSuggestion::NormalExpr { span: suggestion_span.shrink_to_lo() } + }), + }, + ); + } + } +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9e80ddbd551f..38ffecbafa06 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -61,6 +61,7 @@ LARGE_ASSIGNMENTS, LATE_BOUND_LIFETIME_ARGUMENTS, LEGACY_DERIVE_HELPERS, + LINKER_INFO, LINKER_MESSAGES, LONG_RUNNING_CONST_EVAL, LOSSY_PROVENANCE_CASTS, @@ -864,7 +865,6 @@ /// ### Example /// /// ```rust - /// #![feature(cfg_select)] /// cfg_select! { /// _ => (), /// windows => (), @@ -882,7 +882,6 @@ pub UNREACHABLE_CFG_SELECT_PREDICATES, Warn, "detects unreachable configuration predicates in the cfg_select macro", - @feature_gate = cfg_select; } declare_lint! { @@ -1089,11 +1088,6 @@ /// crate-level [`feature` attributes]. /// /// [`feature` attributes]: https://doc.rust-lang.org/nightly/unstable-book/ - /// - /// Note: This lint is currently not functional, see [issue #44232] for - /// more details. - /// - /// [issue #44232]: https://github.com/rust-lang/rust/issues/44232 pub UNUSED_FEATURES, Warn, "unused features found in crate-level `#[feature]` directives" @@ -3659,10 +3653,10 @@ /// `stdcall`, `fastcall`, and `cdecl` calling conventions (or their unwind /// variants) on targets that cannot meaningfully be supported for the requested target. /// - /// For example `stdcall` does not make much sense for a x86_64 or, more apparently, powerpc + /// For example, `stdcall` does not make much sense for a x86_64 or, more apparently, powerpc /// code, because this calling convention was never specified for those targets. /// - /// Historically MSVC toolchains have fallen back to the regular C calling convention for + /// Historically, MSVC toolchains have fallen back to the regular C calling convention for /// targets other than x86, but Rust doesn't really see a similar need to introduce a similar /// hack across many more targets. /// @@ -3689,7 +3683,7 @@ /// /// ### Explanation /// - /// On most of the targets the behaviour of `stdcall` and similar calling conventions is not + /// On most of the targets, the behaviour of `stdcall` and similar calling conventions is not /// defined at all, but was previously accepted due to a bug in the implementation of the /// compiler. pub UNSUPPORTED_CALLING_CONVENTIONS, @@ -4068,6 +4062,40 @@ "warnings emitted at runtime by the target-specific linker program" } +declare_lint! { + /// The `linker_info` lint forwards warnings from the linker that are known to be informational-only. + /// + /// ### Example + /// + /// ```rust,ignore (needs CLI args, platform-specific) + /// #[warn(linker_info)] + /// fn main () {} + /// ``` + /// + /// On MacOS, using `-C link-arg=-lc` and the default linker, this will produce + /// + /// ```text + /// warning: linker stderr: ld: ignoring duplicate libraries: '-lc' + /// | + /// note: the lint level is defined here + /// --> ex.rs:1:9 + /// | + /// 1 | #![warn(linker_info)] + /// | ^^^^^^^^^^^^^^^ + /// ``` + /// + /// ### Explanation + /// + /// Many linkers are very "chatty" and print lots of information that is not necessarily + /// indicative of an issue. This output has been ignored for many years and is often not + /// actionable by developers. It is silenced unless the developer specifically requests for it + /// to be printed. See this tracking issue for more details: + /// . + pub LINKER_INFO, + Allow, + "linker warnings known to be informational-only and not indicative of a problem" +} + declare_lint! { /// The `named_arguments_used_positionally` lint detects cases where named arguments are only /// used positionally in format strings. This usage is valid but potentially very confusing. @@ -4566,7 +4594,7 @@ /// /// [future-incompatible]: ../index.md#future-incompatible-lints pub AMBIGUOUS_GLOB_IMPORTS, - Warn, + Deny, "detects certain glob imports that require reporting an ambiguity error", @future_incompatible = FutureIncompatibleInfo { reason: fcw!(FutureReleaseError #114095), diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index a1b8b135819a..1492df50a418 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -834,6 +834,33 @@ pub enum AttributeLintKind { MalformedDoc, ExpectedNoArgs, ExpectedNameValue, + MalformedOnUnimplementedAttr { + span: Span, + }, + MalformedOnConstAttr { + span: Span, + }, + MalformedDiagnosticFormat { + warning: FormatWarning, + }, + DiagnosticWrappedParserError { + description: String, + label: String, + span: Span, + }, + IgnoredDiagnosticOption { + option_name: Symbol, + first_span: Span, + later_span: Span, + }, + MissingOptionsForOnUnimplemented, + MissingOptionsForOnConst, +} + +#[derive(Debug, Clone, HashStable_Generic)] +pub enum FormatWarning { + PositionalArgument { span: Span, help: String }, + InvalidSpecifier { name: String, span: Span }, } pub type RegisteredTools = FxIndexSet; diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index ad93c7453813..c9f8cd949583 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -12,6 +12,7 @@ libc = "0.2.73" # tidy-alphabetical-start # `cc` updates often break things, so we pin it here. cc = "=1.2.16" +shlex = "1.3.0" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index c58dd64cca5f..38c16fb1cd6d 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -1,8 +1,12 @@ +use std::borrow::Cow; use std::env; use std::ffi::{OsStr, OsString}; use std::fmt::Display; use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; +use std::process::{Command, Output, Stdio}; +use std::str::SplitWhitespace; + +use shlex::Shlex; const OPTIONAL_COMPONENTS: &[&str] = &[ "x86", @@ -86,8 +90,8 @@ fn rerun_if_changed_anything_in_dir(dir: &Path) { } #[track_caller] -fn output(cmd: &mut Command) -> String { - let output = match cmd.stderr(Stdio::inherit()).output() { +fn execute(cmd: &mut Command) -> Output { + let output = match cmd.output() { Ok(status) => status, Err(e) => { println!("\n\nfailed to execute command: {cmd:?}\nerror: {e}\n\n"); @@ -101,7 +105,59 @@ fn output(cmd: &mut Command) -> String { cmd, output.status ); } - String::from_utf8(output.stdout).unwrap() + output +} + +#[track_caller] +fn output(cmd: &mut Command) -> String { + String::from_utf8(execute(cmd.stderr(Stdio::inherit())).stdout).unwrap() +} +#[track_caller] +fn stderr(cmd: &mut Command) -> String { + String::from_utf8(execute(cmd).stderr).unwrap() +} + +enum LlvmConfigOutput { + QuotedPaths(String), + UnquotedPaths(String), +} + +enum SplitLlvmConfigOutput<'a> { + QuotedPaths(Shlex<'a>), + UnquotedPaths(SplitWhitespace<'a>), +} + +impl<'a> Iterator for SplitLlvmConfigOutput<'a> { + type Item = Cow<'a, str>; + fn next(&mut self) -> Option> { + match self { + Self::QuotedPaths(iter) => iter.next().map(Cow::Owned), + Self::UnquotedPaths(iter) => iter.next().map(Cow::Borrowed), + } + } +} + +impl Drop for SplitLlvmConfigOutput<'_> { + fn drop(&mut self) { + if let Self::QuotedPaths(shlex) = self { + assert!(!shlex.had_error, "error parsing llvm-config output"); + } + } +} + +impl<'a> IntoIterator for &'a LlvmConfigOutput { + type Item = Cow<'a, str>; + type IntoIter = SplitLlvmConfigOutput<'a>; + fn into_iter(self) -> Self::IntoIter { + match self { + LlvmConfigOutput::QuotedPaths(output) => { + SplitLlvmConfigOutput::QuotedPaths(Shlex::new(output)) + } + LlvmConfigOutput::UnquotedPaths(output) => { + SplitLlvmConfigOutput::UnquotedPaths(output.split_whitespace()) + } + } + } } fn main() { @@ -125,6 +181,19 @@ fn main() { println!("cargo:rerun-if-changed={}", llvm_config.display()); + // FIXME: `--quote-paths` was added to llvm-config in LLVM 22, so this test (and all its ensuing + // fallback paths) can be removed once we bump the minimum llvm_version >= (22, 0, 0). + let llvm_config_supports_quote_paths = + stderr(Command::new(&llvm_config).arg("--help")).contains("quote-paths"); + + let quoted_split = |mut cmd: Command| { + if llvm_config_supports_quote_paths { + LlvmConfigOutput::QuotedPaths(output(cmd.arg("--quote-paths"))) + } else { + LlvmConfigOutput::UnquotedPaths(output(&mut cmd)) + } + }; + // Test whether we're cross-compiling LLVM. This is a pretty rare case // currently where we're producing an LLVM for a different platform than // what this build script is currently running on. @@ -167,7 +236,7 @@ fn main() { // Link in our own LLVM shims, compiled with the same flags as LLVM let mut cmd = Command::new(&llvm_config); cmd.arg("--cxxflags"); - let cxxflags = output(&mut cmd); + let cxxflags = quoted_split(cmd); let mut cfg = cc::Build::new(); cfg.warnings(false); @@ -180,7 +249,7 @@ fn main() { if std::env::var_os("CI").is_some() && !target.contains("msvc") { cfg.warnings_into_errors(true); } - for flag in cxxflags.split_whitespace() { + for flag in &cxxflags { // Ignore flags like `-m64` when we're doing a cross build if is_crossed && flag.starts_with("-m") { continue; @@ -201,7 +270,7 @@ fn main() { continue; } - cfg.flag(flag); + cfg.flag(&*flag); } for component in &components { @@ -289,13 +358,13 @@ fn main() { } cmd.args(&components); - for lib in output(&mut cmd).split_whitespace() { + for lib in "ed_split(cmd) { let mut is_static = false; let name = if let Some(stripped) = lib.strip_prefix("-l") { stripped } else if let Some(stripped) = lib.strip_prefix('-') { stripped - } else if Path::new(lib).exists() { + } else if Path::new(&*lib).exists() { // On MSVC llvm-config will print the full name to libraries, but // we're only interested in the name part // On Unix when we get a static library llvm-config will print the @@ -306,7 +375,7 @@ fn main() { // and we transform the zstd part into // cargo:rustc-link-search-native=/usr/local/lib // cargo:rustc-link-lib=static=zstd - let path = Path::new(lib); + let path = Path::new(&*lib); if lib.ends_with(".a") { is_static = true; println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); @@ -351,7 +420,7 @@ fn main() { // that those -L directories are the same! let mut cmd = Command::new(&llvm_config); cmd.arg(llvm_link_arg).arg("--ldflags"); - for lib in output(&mut cmd).split_whitespace() { + for lib in "ed_split(cmd) { if is_crossed { if let Some(stripped) = lib.strip_prefix("-LIBPATH:") { println!("cargo:rustc-link-search=native={}", stripped.replace(&host, &target)); @@ -373,13 +442,16 @@ fn main() { // dependencies. let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS"); if let Some(s) = llvm_linker_flags { - for lib in s.into_string().unwrap().split_whitespace() { + let linker_flags = s.into_string().unwrap(); + let mut shlex = Shlex::new(&linker_flags); + for lib in shlex.by_ref() { if let Some(stripped) = lib.strip_prefix("-l") { println!("cargo:rustc-link-lib={stripped}"); } else if let Some(stripped) = lib.strip_prefix("-L") { println!("cargo:rustc-link-search=native={stripped}"); } } + assert!(!shlex.had_error, "error parsing LLVM_LINKER_FLAGS"); } let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP"); @@ -414,7 +486,7 @@ fn main() { // C++ runtime library if !target.contains("msvc") { if let Some(s) = llvm_static_stdcpp { - assert!(!cxxflags.contains("stdlib=libc++")); + assert!(cxxflags.into_iter().all(|flag| flag != "-stdlib=libc++")); let path = PathBuf::from(s); println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); if target.contains("windows") { @@ -422,7 +494,7 @@ fn main() { } else { println!("cargo:rustc-link-lib=static={stdcppname}"); } - } else if cxxflags.contains("stdlib=libc++") { + } else if cxxflags.into_iter().any(|flag| flag == "-stdlib=libc++") { println!("cargo:rustc-link-lib=c++"); } else { println!("cargo:rustc-link-lib={stdcppname}"); diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index f6598f9faf52..0cbda23f384c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -46,4 +46,8 @@ public: } }; +struct LLVMRustBuffer { + std::string data; +}; + #endif // INCLUDED_RUSTC_LLVM_LLVMWRAPPER_H diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 7aa4ddea78e1..a47179c14d27 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -87,19 +87,6 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char *FileName) { timeTraceProfilerCleanup(); } -// This struct and various functions are sort of a hack right now, but the -// problem is that we've got in-memory LLVM modules after we generate and -// optimize all codegen-units for one compilation in rustc. To be compatible -// with the LTO support above we need to serialize the modules plus their -// ThinLTO summary into memory. -// -// This structure is basically an owned version of a serialize module, with -// a ThinLTO summary attached. -struct LLVMRustThinLTOBuffer { - std::string data; - std::string thin_link_data; -}; - extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM, const char *Feature) { TargetMachine *Target = unwrap(TM); @@ -360,7 +347,13 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( // option causes bugs in the LLVM WebAssembly backend. You should be able to // remove this check when Rust's minimum supported LLVM version is >= 18 // https://github.com/llvm/llvm-project/pull/65876 - if (!Trip.isWasm()) { + // + // Also keep traps after noreturn calls on Windows, because the trap is + // needed to keep the return address within the calling function's + // .pdata range. Without it, RtlLookupFunctionEntry resolves the wrong + // function and SEH unwinding (used for backtraces) terminates early. + // See https://github.com/rust-lang/rust/issues/140489 + if (!Trip.isWasm() && !Trip.isOSWindows()) { Options.NoTrapAfterNoreturn = true; } } @@ -566,11 +559,12 @@ extern "C" LLVMRustResult LLVMRustOptimize( LLVMModuleRef ModuleRef, LLVMTargetMachineRef TMRef, LLVMRustPassBuilderOptLevel OptLevelRust, LLVMRustOptStage OptStage, bool IsLinkerPluginLTO, bool NoPrepopulatePasses, bool VerifyIR, - bool LintIR, LLVMRustThinLTOBuffer **ThinLTOBufferRef, bool EmitThinLTO, - bool EmitThinLTOSummary, bool MergeFunctions, bool UnrollLoops, - bool SLPVectorize, bool LoopVectorize, bool DisableSimplifyLibCalls, - bool EmitLifetimeMarkers, registerEnzymeAndPassPipelineFn EnzymePtr, - bool PrintBeforeEnzyme, bool PrintAfterEnzyme, bool PrintPasses, + bool LintIR, LLVMRustBuffer **ThinLTOBufferRef, + LLVMRustBuffer **ThinLTOSummaryBufferRef, bool MergeFunctions, + bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, + bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers, + registerEnzymeAndPassPipelineFn EnzymePtr, bool PrintBeforeEnzyme, + bool PrintAfterEnzyme, bool PrintPasses, LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, const char *PGOUsePath, bool InstrumentCoverage, const char *InstrProfileOutput, const char *PGOSampleUsePath, @@ -808,31 +802,27 @@ extern "C" LLVMRustResult LLVMRustOptimize( } ModulePassManager MPM; - bool NeedThinLTOBufferPasses = EmitThinLTO; - auto ThinLTOBuffer = std::make_unique(); + bool NeedThinLTOBufferPasses = true; + auto ThinLTOBuffer = std::make_unique(); + auto ThinLTOSummaryBuffer = std::make_unique(); raw_string_ostream ThinLTODataOS(ThinLTOBuffer->data); - raw_string_ostream ThinLinkDataOS(ThinLTOBuffer->thin_link_data); + raw_string_ostream ThinLinkDataOS(ThinLTOSummaryBuffer->data); bool IsLTO = OptStage == LLVMRustOptStage::ThinLTO || OptStage == LLVMRustOptStage::FatLTO; if (!NoPrepopulatePasses) { + for (const auto &C : PipelineStartEPCallbacks) + PB.registerPipelineStartEPCallback(C); + for (const auto &C : OptimizerLastEPCallbacks) + PB.registerOptimizerLastEPCallback(C); + // The pre-link pipelines don't support O0 and require using // buildO0DefaultPipeline() instead. At the same time, the LTO pipelines do // support O0 and using them is required. if (OptLevel == OptimizationLevel::O0 && !IsLTO) { - for (const auto &C : PipelineStartEPCallbacks) - PB.registerPipelineStartEPCallback(C); - for (const auto &C : OptimizerLastEPCallbacks) - PB.registerOptimizerLastEPCallback(C); - // We manually schedule ThinLTOBufferPasses below, so don't pass the value // to enable it here. MPM = PB.buildO0DefaultPipeline(OptLevel); } else { - for (const auto &C : PipelineStartEPCallbacks) - PB.registerPipelineStartEPCallback(C); - for (const auto &C : OptimizerLastEPCallbacks) - PB.registerOptimizerLastEPCallback(C); - switch (OptStage) { case LLVMRustOptStage::PreLinkNoLTO: if (ThinLTOBufferRef) { @@ -840,13 +830,13 @@ extern "C" LLVMRustResult LLVMRustOptimize( // bitcode for embedding is obtained after performing // `ThinLTOPreLinkDefaultPipeline`. MPM.addPass(PB.buildThinLTOPreLinkDefaultPipeline(OptLevel)); - if (EmitThinLTO) { - MPM.addPass(ThinLTOBitcodeWriterPass( - ThinLTODataOS, EmitThinLTOSummary ? &ThinLinkDataOS : nullptr)); - } else { - MPM.addPass(BitcodeWriterPass(ThinLTODataOS)); - } + MPM.addPass(ThinLTOBitcodeWriterPass( + ThinLTODataOS, + ThinLTOSummaryBufferRef ? &ThinLinkDataOS : nullptr)); *ThinLTOBufferRef = ThinLTOBuffer.release(); + if (ThinLTOSummaryBufferRef) { + *ThinLTOSummaryBufferRef = ThinLTOSummaryBuffer.release(); + } MPM.addPass(PB.buildModuleOptimizationPipeline( OptLevel, ThinOrFullLTOPhase::None)); MPM.addPass( @@ -856,11 +846,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( } break; case LLVMRustOptStage::PreLinkThinLTO: - MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel); - NeedThinLTOBufferPasses = false; - break; case LLVMRustOptStage::PreLinkFatLTO: - MPM = PB.buildLTOPreLinkDefaultPipeline(OptLevel); + MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel); NeedThinLTOBufferPasses = false; break; case LLVMRustOptStage::ThinLTO: @@ -870,6 +857,7 @@ extern "C" LLVMRustResult LLVMRustOptimize( break; case LLVMRustOptStage::FatLTO: MPM = PB.buildLTODefaultPipeline(OptLevel, nullptr); + NeedThinLTOBufferPasses = false; break; } } @@ -895,15 +883,20 @@ extern "C" LLVMRustResult LLVMRustOptimize( MPM.addPass(CanonicalizeAliasesPass()); MPM.addPass(NameAnonGlobalPass()); } - // For `-Copt-level=0`, ThinLTO, or LTO. + // For `-Copt-level=0`, and the pre-link fat/thin LTO stages. if (ThinLTOBufferRef && *ThinLTOBufferRef == nullptr) { - if (EmitThinLTO) { + // thin lto summaries prevent fat lto, so do not emit them if fat + // lto is requested. See PR #136840 for background information. + if (OptStage != LLVMRustOptStage::PreLinkFatLTO) { MPM.addPass(ThinLTOBitcodeWriterPass( - ThinLTODataOS, EmitThinLTOSummary ? &ThinLinkDataOS : nullptr)); + ThinLTODataOS, ThinLTOSummaryBufferRef ? &ThinLinkDataOS : nullptr)); } else { MPM.addPass(BitcodeWriterPass(ThinLTODataOS)); } *ThinLTOBufferRef = ThinLTOBuffer.release(); + if (ThinLTOSummaryBufferRef) { + *ThinLTOSummaryBufferRef = ThinLTOSummaryBuffer.release(); + } } // now load "-enzyme" pass: @@ -1414,9 +1407,9 @@ extern "C" bool LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, return true; } -extern "C" LLVMRustThinLTOBuffer *LLVMRustThinLTOBufferCreate(LLVMModuleRef M, - bool is_thin) { - auto Ret = std::make_unique(); +extern "C" LLVMRustBuffer *LLVMRustModuleSerialize(LLVMModuleRef M, + bool is_thin) { + auto Ret = std::make_unique(); { auto OS = raw_string_ostream(Ret->data); { @@ -1442,30 +1435,6 @@ extern "C" LLVMRustThinLTOBuffer *LLVMRustThinLTOBufferCreate(LLVMModuleRef M, return Ret.release(); } -extern "C" void LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) { - delete Buffer; -} - -extern "C" const void * -LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) { - return Buffer->data.data(); -} - -extern "C" size_t -LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) { - return Buffer->data.length(); -} - -extern "C" const void * -LLVMRustThinLTOBufferThinLinkDataPtr(const LLVMRustThinLTOBuffer *Buffer) { - return Buffer->thin_link_data.data(); -} - -extern "C" size_t -LLVMRustThinLTOBufferThinLinkDataLen(const LLVMRustThinLTOBuffer *Buffer) { - return Buffer->thin_link_data.length(); -} - // This is what we used to parse upstream bitcode for actual ThinLTO // processing. We'll call this once per module optimized through ThinLTO, and // it'll be called concurrently on many threads. diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index f68f4e71520b..eabc1c94f26e 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1529,29 +1529,13 @@ extern "C" void LLVMRustSetDSOLocal(LLVMValueRef Global, bool is_dso_local) { unwrap(Global)->setDSOLocal(is_dso_local); } -struct LLVMRustModuleBuffer { - std::string data; -}; +extern "C" void LLVMRustBufferFree(LLVMRustBuffer *Buffer) { delete Buffer; } -extern "C" LLVMRustModuleBuffer *LLVMRustModuleBufferCreate(LLVMModuleRef M) { - auto Ret = std::make_unique(); - { - auto OS = raw_string_ostream(Ret->data); - WriteBitcodeToFile(*unwrap(M), OS); - } - return Ret.release(); -} - -extern "C" void LLVMRustModuleBufferFree(LLVMRustModuleBuffer *Buffer) { - delete Buffer; -} - -extern "C" const void * -LLVMRustModuleBufferPtr(const LLVMRustModuleBuffer *Buffer) { +extern "C" const void *LLVMRustBufferPtr(const LLVMRustBuffer *Buffer) { return Buffer->data.data(); } -extern "C" size_t LLVMRustModuleBufferLen(const LLVMRustModuleBuffer *Buffer) { +extern "C" size_t LLVMRustBufferLen(const LLVMRustBuffer *Buffer) { return Buffer->data.length(); } diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml index a243dc5da9e9..d407351fd23d 100644 --- a/compiler/rustc_log/Cargo.toml +++ b/compiler/rustc_log/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" # tidy-alphabetical-start tracing = "0.1.41" tracing-core = "0.1.34" -tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } +tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi", "json"] } tracing-tree = "0.3.1" # tidy-alphabetical-end diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 9e25a07044f7..7dafbd8ffc08 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -35,15 +35,18 @@ use std::env::{self, VarError}; use std::fmt::{self, Display}; +use std::fs::File; use std::io::{self, IsTerminal}; +use std::sync::Mutex; use tracing::dispatcher::SetGlobalDefaultError; use tracing::{Event, Subscriber}; -use tracing_subscriber::Registry; use tracing_subscriber::filter::{Directive, EnvFilter, LevelFilter}; use tracing_subscriber::fmt::FmtContext; -use tracing_subscriber::fmt::format::{self, FormatEvent, FormatFields}; +use tracing_subscriber::fmt::format::{self, FmtSpan, FormatEvent, FormatFields}; +use tracing_subscriber::fmt::writer::BoxMakeWriter; use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::{Layer, Registry}; // Re-export tracing pub use {tracing, tracing_core, tracing_subscriber}; @@ -55,12 +58,15 @@ pub struct LoggerConfig { pub verbose_entry_exit: Result, pub verbose_thread_ids: Result, pub backtrace: Result, + pub json: Result, + pub output_target: Result, pub wraptree: Result, pub lines: Result, } impl LoggerConfig { pub fn from_env(env: &str) -> Self { + // NOTE: documented in the dev guide. If you change this, also update it! LoggerConfig { filter: env::var(env), color_logs: env::var(format!("{env}_COLOR")), @@ -69,6 +75,8 @@ pub fn from_env(env: &str) -> Self { backtrace: env::var(format!("{env}_BACKTRACE")), wraptree: env::var(format!("{env}_WRAPTREE")), lines: env::var(format!("{env}_LINES")), + json: env::var(format!("{env}_FORMAT_JSON")), + output_target: env::var(format!("{env}_OUTPUT_TARGET")), } } } @@ -136,23 +144,59 @@ pub fn init_logger_with_additional_layer( Err(_) => false, }; - let mut layer = tracing_tree::HierarchicalLayer::default() - .with_writer(io::stderr) - .with_ansi(color_logs) - .with_targets(true) - .with_verbose_exit(verbose_entry_exit) - .with_verbose_entry(verbose_entry_exit) - .with_indent_amount(2) - .with_indent_lines(lines) - .with_thread_ids(verbose_thread_ids) - .with_thread_names(verbose_thread_ids); + let json = match cfg.json { + Ok(v) => &v == "1", + Err(_) => false, + }; - if let Ok(v) = cfg.wraptree { - match v.parse::() { - Ok(v) => layer = layer.with_wraparound(v), - Err(_) => return Err(Error::InvalidWraptree(v)), + let output_target: BoxMakeWriter = match cfg.output_target { + Ok(v) => match File::options().create(true).write(true).truncate(true).open(&v) { + Ok(i) => BoxMakeWriter::new(Mutex::new(i)), + Err(e) => { + eprintln!("couldn't open {v} as a log target: {e:?}"); + BoxMakeWriter::new(io::stderr) + } + }, + Err(_) => BoxMakeWriter::new(io::stderr), + }; + + let layer = if json { + let format = tracing_subscriber::fmt::format() + .json() + .with_span_list(true) + .with_source_location(true); + + let fmt_layer = tracing_subscriber::fmt::layer() + .json() + .event_format(format) + .with_writer(output_target) + .with_target(true) + .with_ansi(false) + .with_thread_ids(verbose_thread_ids) + .with_thread_names(verbose_thread_ids) + .with_span_events(FmtSpan::ACTIVE); + Layer::boxed(fmt_layer) + } else { + let mut layer = tracing_tree::HierarchicalLayer::default() + .with_writer(output_target) + .with_ansi(color_logs) + .with_targets(true) + .with_verbose_exit(verbose_entry_exit) + .with_verbose_entry(verbose_entry_exit) + .with_indent_amount(2) + .with_indent_lines(lines) + .with_thread_ids(verbose_thread_ids) + .with_thread_names(verbose_thread_ids); + + if let Ok(v) = cfg.wraptree { + match v.parse::() { + Ok(v) => layer = layer.with_wraparound(v), + Err(_) => return Err(Error::InvalidWraptree(v)), + } } - } + + Layer::boxed(layer) + }; let subscriber = build_subscriber(); // NOTE: It is important to make sure that the filter is applied on the last layer diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index dc8231e5f0b0..2f7c3cc6a46d 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -4,7 +4,7 @@ use quote::quote; use synstructure::Structure; -use crate::diagnostics::diagnostic_builder::DiagnosticDeriveKind; +use crate::diagnostics::diagnostic_builder::each_variant; use crate::diagnostics::error::DiagnosticDeriveError; /// The central struct for constructing the `into_diag` method from an annotated struct. @@ -19,8 +19,7 @@ pub(crate) fn new(structure: Structure<'a>) -> Self { pub(crate) fn into_tokens(self) -> TokenStream { let DiagnosticDerive { mut structure } = self; - let kind = DiagnosticDeriveKind::Diagnostic; - let implementation = kind.each_variant(&mut structure, |mut builder, variant| { + let implementation = each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(variant); let body = builder.body(variant); @@ -64,52 +63,3 @@ fn into_diag( }) } } - -/// The central struct for constructing the `decorate_lint` method from an annotated struct. -pub(crate) struct LintDiagnosticDerive<'a> { - structure: Structure<'a>, -} - -impl<'a> LintDiagnosticDerive<'a> { - pub(crate) fn new(structure: Structure<'a>) -> Self { - Self { structure } - } - - pub(crate) fn into_tokens(self) -> TokenStream { - let LintDiagnosticDerive { mut structure } = self; - let kind = DiagnosticDeriveKind::LintDiagnostic; - let implementation = kind.each_variant(&mut structure, |mut builder, variant| { - let preamble = builder.preamble(variant); - let body = builder.body(variant); - - let Some(message) = builder.primary_message() else { - return DiagnosticDeriveError::ErrorHandled.to_compile_error(); - }; - let message = message.diag_message(Some(variant)); - let primary_message = quote! { - diag.primary_message(#message); - }; - - let formatting_init = &builder.formatting_init; - quote! { - #primary_message - #preamble - #formatting_init - #body - diag - } - }); - - structure.gen_impl(quote! { - gen impl<'__a> rustc_errors::LintDiagnostic<'__a, ()> for @Self { - #[track_caller] - fn decorate_lint<'__b>( - self, - diag: &'__b mut rustc_errors::Diag<'__a, ()> - ) { - #implementation; - } - } - }) - } -} diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index de8ee42caf45..e335037f2c4c 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -18,20 +18,54 @@ should_generate_arg, type_is_bool, type_is_unit, type_matches_path, }; -/// What kind of diagnostic is being derived - a fatal/error/warning or a lint? -#[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) enum DiagnosticDeriveKind { - Diagnostic, - LintDiagnostic, +pub(crate) fn each_variant<'s, F>(structure: &mut Structure<'s>, f: F) -> TokenStream +where + F: for<'v> Fn(DiagnosticDeriveVariantBuilder, &VariantInfo<'v>) -> TokenStream, +{ + let ast = structure.ast(); + let span = ast.span().unwrap(); + match ast.data { + syn::Data::Struct(..) | syn::Data::Enum(..) => (), + syn::Data::Union(..) => { + span_err(span, "diagnostic derives can only be used on structs and enums").emit(); + } + } + + if matches!(ast.data, syn::Data::Enum(..)) { + for attr in &ast.attrs { + span_err(attr.span().unwrap(), "unsupported type attribute for diagnostic derive enum") + .emit(); + } + } + + structure.bind_with(|_| synstructure::BindStyle::Move); + let variants = structure.each_variant(|variant| { + let span = match structure.ast().data { + syn::Data::Struct(..) => span, + // There isn't a good way to get the span of the variant, so the variant's + // name will need to do. + _ => variant.ast().ident.span().unwrap(), + }; + let builder = DiagnosticDeriveVariantBuilder { + span, + field_map: build_field_mapping(variant), + formatting_init: TokenStream::new(), + message: None, + code: None, + }; + f(builder, variant) + }); + + quote! { + match self { + #variants + } + } } /// Tracks persistent information required for a specific variant when building up individual calls -/// to diagnostic methods for generated diagnostic derives - both `Diagnostic` for -/// fatal/errors/warnings and `LintDiagnostic` for lints. +/// to diagnostic methods for generated diagnostic derives. pub(crate) struct DiagnosticDeriveVariantBuilder { - /// The kind for the entire type. - pub kind: DiagnosticDeriveKind, - /// Initialization of format strings for code suggestions. pub formatting_init: TokenStream, @@ -51,60 +85,6 @@ pub(crate) struct DiagnosticDeriveVariantBuilder { pub code: SpannedOption<()>, } -impl DiagnosticDeriveKind { - /// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the - /// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions - /// or attributes on the type itself when input is an enum. - pub(crate) fn each_variant<'s, F>(self, structure: &mut Structure<'s>, f: F) -> TokenStream - where - F: for<'v> Fn(DiagnosticDeriveVariantBuilder, &VariantInfo<'v>) -> TokenStream, - { - let ast = structure.ast(); - let span = ast.span().unwrap(); - match ast.data { - syn::Data::Struct(..) | syn::Data::Enum(..) => (), - syn::Data::Union(..) => { - span_err(span, "diagnostic derives can only be used on structs and enums").emit(); - } - } - - if matches!(ast.data, syn::Data::Enum(..)) { - for attr in &ast.attrs { - span_err( - attr.span().unwrap(), - "unsupported type attribute for diagnostic derive enum", - ) - .emit(); - } - } - - structure.bind_with(|_| synstructure::BindStyle::Move); - let variants = structure.each_variant(|variant| { - let span = match structure.ast().data { - syn::Data::Struct(..) => span, - // There isn't a good way to get the span of the variant, so the variant's - // name will need to do. - _ => variant.ast().ident.span().unwrap(), - }; - let builder = DiagnosticDeriveVariantBuilder { - kind: self, - span, - field_map: build_field_mapping(variant), - formatting_init: TokenStream::new(), - message: None, - code: None, - }; - f(builder, variant) - }); - - quote! { - match self { - #variants - } - } - } -} - impl DiagnosticDeriveVariantBuilder { pub(crate) fn primary_message(&self) -> Option<&Message> { match self.message.as_ref() { @@ -358,20 +338,11 @@ fn generate_inner_field_code( // `arg` call will not be generated. (Meta::Path(_), "skip_arg") => return Ok(quote! {}), (Meta::Path(_), "primary_span") => { - match self.kind { - DiagnosticDeriveKind::Diagnostic => { - report_error_if_not_applied_to_span(attr, &info)?; + report_error_if_not_applied_to_span(attr, &info)?; - return Ok(quote! { - diag.span(#binding); - }); - } - DiagnosticDeriveKind::LintDiagnostic => { - throw_invalid_attr!(attr, |diag| { - diag.help("the `primary_span` field attribute is not valid for lint diagnostics") - }) - } - } + return Ok(quote! { + diag.span(#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 dfc5c7e800c6..11bf904c18e1 100644 --- a/compiler/rustc_macros/src/diagnostics/message.rs +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -64,26 +64,46 @@ fn verify_variables_used(msg_span: Span, message_str: &str, variant: Option<&Var fn variable_references<'a>(msg: &fluent_syntax::ast::Message<&'a str>) -> Vec<&'a str> { let mut refs = vec![]; + if let Some(Pattern { elements }) = &msg.value { for elt in elements { - if let PatternElement::Placeable { - expression: Expression::Inline(InlineExpression::VariableReference { id }), - } = elt - { - refs.push(id.name); - } + traverse_pattern(elt, &mut refs); } } for attr in &msg.attributes { for elt in &attr.value.elements { - if let PatternElement::Placeable { - expression: Expression::Inline(InlineExpression::VariableReference { id }), - } = elt - { - refs.push(id.name); + traverse_pattern(elt, &mut refs); + } + } + + fn traverse_pattern<'a>(elem: &PatternElement<&'a str>, refs: &mut Vec<&'a str>) { + match elem { + PatternElement::TextElement { .. } => {} + PatternElement::Placeable { expression } => traverse_expression(expression, refs), + } + } + fn traverse_expression<'a>(expr: &Expression<&'a str>, refs: &mut Vec<&'a str>) { + match expr { + Expression::Select { selector, variants } => { + traverse_inline_expr(selector, refs); + for variant in variants { + for pattern in &variant.value.elements { + traverse_pattern(pattern, refs); + } + } + } + Expression::Inline(expr) => { + traverse_inline_expr(expr, refs); } } } + fn traverse_inline_expr<'a>(elem: &InlineExpression<&'a str>, refs: &mut Vec<&'a str>) { + match elem { + InlineExpression::VariableReference { id } => refs.push(id.name), + _ => {} + } + } + refs } diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index c0512e86bbcb..a5828cd715a8 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -6,7 +6,7 @@ mod subdiagnostic; mod utils; -use diagnostic::{DiagnosticDerive, LintDiagnosticDerive}; +use diagnostic::DiagnosticDerive; pub(super) use msg_macro::msg_macro; use proc_macro2::TokenStream; use subdiagnostic::SubdiagnosticDerive; @@ -51,38 +51,6 @@ pub(super) fn diagnostic_derive(s: Structure<'_>) -> TokenStream { DiagnosticDerive::new(s).into_tokens() } -/// Implements `#[derive(LintDiagnostic)]`, which allows for lints to be specified as a struct, -/// independent from the actual lint emitting code. -/// -/// ```ignore (rust) -/// #[derive(LintDiagnostic)] -/// #[diag("unused attribute")] -/// pub(crate) struct UnusedAttribute { -/// #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] -/// pub this: Span, -/// #[note("attribute also specified here")] -/// pub other: Span, -/// #[warning( -/// "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" -/// )] -/// pub warning: bool, -/// } -/// ``` -/// -/// Then, later, to emit the error: -/// -/// ```ignore (rust) -/// cx.emit_span_lint(UNUSED_ATTRIBUTES, span, UnusedAttribute { -/// ... -/// }); -/// ``` -/// -/// See rustc dev guide for more examples on using the `#[derive(LintDiagnostic)]`: -/// -pub(super) fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { - LintDiagnosticDerive::new(s).into_tokens() -} - /// Implements `#[derive(Subdiagnostic)]`, which allows for labels, notes, helps and /// suggestions to be specified as a structs or enums, independent from the actual diagnostics /// emitting code or diagnostic derives. @@ -99,7 +67,7 @@ pub(super) fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { /// Then, later, use the subdiagnostic in a diagnostic: /// /// ```ignore (rust) -/// #[derive(LintDiagnostic)] +/// #[derive(Diagnostic)] /// #[diag("unused doc comment")] /// pub(crate) struct BuiltinUnusedDocComment<'a> { /// pub kind: &'a str, diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 7eb170ec236a..3e094ee8d42b 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -227,9 +227,9 @@ fn generate_field_arg(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream let ident = format_ident!("{}", ident); // strip `r#` prefix, if present quote! { - #diag.arg( - stringify!(#ident), - #field_binding + sub_args.insert( + stringify!(#ident).into(), + rustc_errors::IntoDiagArg::into_diag_arg(#field_binding, &mut #diag.long_ty_path) ); } } @@ -529,14 +529,25 @@ pub(crate) fn into_tokens(&mut self) -> Result Result Result { - parse_quote!(::rustc_query_system::ich::StableHashingContext<'__ctx>) + parse_quote!(::rustc_middle::ich::StableHashingContext<'__ctx>) } HashStableMode::Generic | HashStableMode::NoContext => parse_quote!(__CTX), }; diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index e4f43a22738d..44969908b3f4 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -64,7 +64,7 @@ pub fn extension(attr: TokenStream, input: TokenStream) -> TokenStream { hash_stable::hash_stable_generic_derive ); decl_derive!( - [HashStable_NoContext] => + [HashStable_NoContext, attributes(stable_hasher)] => /// `HashStable` implementation that has no `HashStableContext` bound and /// which adds `where` bounds for `HashStable` based off of fields and not /// generics. This is suitable for use in crates like `rustc_type_ir`. @@ -196,25 +196,6 @@ pub fn extension(attr: TokenStream, input: TokenStream) -> TokenStream { suggestion_hidden, suggestion_verbose)] => diagnostics::diagnostic_derive ); -decl_derive!( - [LintDiagnostic, attributes( - // struct attributes - diag, - help, - help_once, - note, - note_once, - warning, - // field attributes - skip_arg, - primary_span, - label, - subdiagnostic, - suggestion, - suggestion_short, - suggestion_hidden, - suggestion_verbose)] => diagnostics::lint_diagnostic_derive -); decl_derive!( [Subdiagnostic, attributes( // struct/variant attributes @@ -232,7 +213,6 @@ pub fn extension(attr: TokenStream, input: TokenStream) -> TokenStream { multipart_suggestion, multipart_suggestion_short, multipart_suggestion_hidden, - multipart_suggestion_verbose, // field attributes skip_arg, primary_span, diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 5b869dc3409a..05a28c48f806 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -10,6 +10,7 @@ }; mod kw { + syn::custom_keyword!(non_query); syn::custom_keyword!(query); } @@ -30,35 +31,78 @@ fn check_attributes(attrs: Vec) -> Result> { attrs.into_iter().map(inner).collect() } -/// A compiler query. `query ... { ... }` +/// Declaration of a compiler query. +/// +/// ```ignore (illustrative) +/// /// Doc comment for `my_query`. +/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doc_comments +/// query my_query(key: DefId) -> Value { anon } +/// // ^^^^^^^^ name +/// // ^^^ key_pat +/// // ^^^^^ key_ty +/// // ^^^^^^^^ return_ty +/// // ^^^^ modifiers +/// ``` struct Query { doc_comments: Vec, - modifiers: QueryModifiers, name: Ident, - key: Pat, - arg: Type, - result: ReturnType, + + /// Parameter name for the key, or an arbitrary irrefutable pattern (e.g. `_`). + key_pat: Pat, + key_ty: Type, + return_ty: ReturnType, + + modifiers: QueryModifiers, } -impl Parse for Query { +/// Declaration of a non-query dep kind. +/// ```ignore (illustrative) +/// /// Doc comment for `MyNonQuery`. +/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doc_comments +/// non_query MyNonQuery +/// // ^^^^^^^^^^ name +/// ``` +struct NonQuery { + doc_comments: Vec, + name: Ident, +} + +enum QueryEntry { + Query(Query), + NonQuery(NonQuery), +} + +impl Parse for QueryEntry { fn parse(input: ParseStream<'_>) -> Result { let mut doc_comments = check_attributes(input.call(Attribute::parse_outer)?)?; + // Try the non-query case first. + if input.parse::().is_ok() { + let name: Ident = input.parse()?; + return Ok(QueryEntry::NonQuery(NonQuery { doc_comments, name })); + } + // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>` - input.parse::()?; + if input.parse::().is_err() { + return Err(input.error("expected `query` or `non_query`")); + } let name: Ident = input.parse()?; - let arg_content; - parenthesized!(arg_content in input); - let key = Pat::parse_single(&arg_content)?; - arg_content.parse::()?; - let arg = arg_content.parse()?; - let _ = arg_content.parse::>()?; - let result = input.parse()?; + + // `(key: DefId)` + let parens_content; + parenthesized!(parens_content in input); + let key_pat = Pat::parse_single(&parens_content)?; + parens_content.parse::()?; + let key_ty = parens_content.parse::()?; + let _trailing_comma = parens_content.parse::>()?; + + // `-> Value` + let return_ty = input.parse::()?; // Parse the query modifiers - let content; - braced!(content in input); - let modifiers = parse_query_modifiers(&content)?; + let braces_content; + braced!(braces_content in input); + let modifiers = parse_query_modifiers(&braces_content)?; // If there are no doc-comments, give at least some idea of what // it does by showing the query description. @@ -66,7 +110,7 @@ fn parse(input: ParseStream<'_>) -> Result { doc_comments.push(doc_comment_from_desc(&modifiers.desc.expr_list)?); } - Ok(Query { doc_comments, modifiers, name, key, arg, result }) + Ok(QueryEntry::Query(Query { doc_comments, modifiers, name, key_pat, key_ty, return_ty })) } } @@ -85,71 +129,35 @@ fn parse(input: ParseStream<'_>) -> Result { struct Desc { modifier: Ident, - tcx_binding: Option, expr_list: Punctuated, } struct CacheOnDiskIf { modifier: Ident, - tcx_binding: Option, block: Block, } +/// See `rustc_middle::query::modifiers` for documentation of each query modifier. struct QueryModifiers { - /// The description of the query. - desc: Desc, - - /// Use this type for the in-memory cache. - arena_cache: Option, - - /// Cache the query to disk if the `Block` returns true. - cache_on_disk_if: Option, - - /// A cycle error for this query aborting the compilation with a fatal error. - cycle_fatal: Option, - - /// A cycle error results in a delay_bug call - cycle_delay_bug: Option, - - /// A cycle error results in a stashed cycle error that can be unstashed and canceled later - cycle_stash: Option, - - /// Don't hash the result, instead just mark a query red if it runs - no_hash: Option, - - /// Generate a dep node based on the dependencies of the query + // tidy-alphabetical-start anon: Option, - - /// Always evaluate the query, ignoring its dependencies - eval_always: Option, - - /// Whether the query has a call depth limit + arena_cache: Option, + cache_on_disk_if: Option, + cycle_delay_bug: Option, + cycle_stash: Option, depth_limit: Option, - - /// Use a separate query provider for local and extern crates - separate_provide_extern: Option, - - /// Generate a `feed` method to set the query's value from another query. + desc: Desc, + eval_always: Option, feedable: Option, - - /// When this query is called via `tcx.ensure_ok()`, it returns - /// `Result<(), ErrorGuaranteed>` instead of `()`. If the query needs to - /// be executed, and that execution returns an error, the error result is - /// returned to the caller. - /// - /// If execution is skipped, a synthetic `Ok(())` is returned, on the - /// assumption that a query with all-green inputs must have succeeded. - /// - /// Can only be applied to queries with a return value of - /// `Result<_, ErrorGuaranteed>`. - return_result_from_ensure_ok: Option, + no_hash: Option, + separate_provide_extern: Option, + // tidy-alphabetical-end } fn parse_query_modifiers(input: ParseStream<'_>) -> Result { let mut arena_cache = None; let mut cache_on_disk_if = None; let mut desc = None; - let mut cycle_fatal = None; let mut cycle_delay_bug = None; let mut cycle_stash = None; let mut no_hash = None; @@ -158,7 +166,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { let mut depth_limit = None; let mut separate_provide_extern = None; let mut feedable = None; - let mut return_result_from_ensure_ok = None; while !input.is_empty() { let modifier: Ident = input.parse()?; @@ -174,39 +181,18 @@ macro_rules! try_insert { if modifier == "desc" { // Parse a description modifier like: - // `desc { |tcx| "foo {}", tcx.item_path(key) }` + // `desc { "foo {}", tcx.item_path(key) }` let attr_content; braced!(attr_content in input); - let tcx_binding = if attr_content.peek(Token![|]) { - attr_content.parse::()?; - let tcx = attr_content.parse()?; - attr_content.parse::()?; - Some(tcx) - } else { - None - }; let expr_list = attr_content.parse_terminated(Expr::parse, Token![,])?; - try_insert!(desc = Desc { modifier, tcx_binding, expr_list }); + try_insert!(desc = Desc { modifier, expr_list }); } else if modifier == "cache_on_disk_if" { // Parse a cache-on-disk modifier like: - // - // `cache_on_disk_if { true }` - // `cache_on_disk_if { key.is_local() }` - // `cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) }` - let tcx_binding = if input.peek(token::Paren) { - let args; - parenthesized!(args in input); - let tcx = Pat::parse_single(&args)?; - Some(tcx) - } else { - None - }; + // `cache_on_disk_if { tcx.is_typeck_child(key.to_def_id()) }` let block = input.parse()?; - try_insert!(cache_on_disk_if = CacheOnDiskIf { modifier, tcx_binding, block }); + try_insert!(cache_on_disk_if = CacheOnDiskIf { modifier, block }); } else if modifier == "arena_cache" { try_insert!(arena_cache = modifier); - } else if modifier == "cycle_fatal" { - try_insert!(cycle_fatal = modifier); } else if modifier == "cycle_delay_bug" { try_insert!(cycle_delay_bug = modifier); } else if modifier == "cycle_stash" { @@ -223,8 +209,6 @@ macro_rules! try_insert { try_insert!(separate_provide_extern = modifier); } else if modifier == "feedable" { try_insert!(feedable = modifier); - } else if modifier == "return_result_from_ensure_ok" { - try_insert!(return_result_from_ensure_ok = modifier); } else { return Err(Error::new(modifier.span(), "unknown query modifier")); } @@ -236,7 +220,6 @@ macro_rules! try_insert { arena_cache, cache_on_disk_if, desc, - cycle_fatal, cycle_delay_bug, cycle_stash, no_hash, @@ -245,10 +228,86 @@ macro_rules! try_insert { depth_limit, separate_provide_extern, feedable, - return_result_from_ensure_ok, }) } +// Does `ret_ty` match `Result<_, ErrorGuaranteed>`? +fn returns_error_guaranteed(ret_ty: &ReturnType) -> bool { + use ::syn::*; + if let ReturnType::Type(_, ret_ty) = ret_ty + && let Type::Path(type_path) = ret_ty.as_ref() + && let Some(PathSegment { ident, arguments }) = type_path.path.segments.last() + && ident == "Result" + && let PathArguments::AngleBracketed(args) = arguments + && args.args.len() == 2 + && let GenericArgument::Type(ty) = &args.args[1] + && let Type::Path(type_path) = ty + && type_path.path.is_ident("ErrorGuaranteed") + { + true + } else { + false + } +} + +fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { + let QueryModifiers { + // tidy-alphabetical-start + anon, + arena_cache, + cache_on_disk_if, + cycle_delay_bug, + cycle_stash, + depth_limit, + desc: _, + eval_always, + feedable, + no_hash, + separate_provide_extern, + // tidy-alphabetical-end + } = &query.modifiers; + + let anon = anon.is_some(); + let arena_cache = arena_cache.is_some(); + let cache_on_disk = cache_on_disk_if.is_some(); + + let cycle_error_handling = if cycle_delay_bug.is_some() { + quote! { DelayBug } + } else if cycle_stash.is_some() { + quote! { Stash } + } else { + quote! { Error } + }; + + let depth_limit = depth_limit.is_some(); + let eval_always = eval_always.is_some(); + let feedable = feedable.is_some(); + let no_hash = no_hash.is_some(); + let returns_error_guaranteed = returns_error_guaranteed(&query.return_ty); + let separate_provide_extern = separate_provide_extern.is_some(); + + // Giving an input span to the modifier names in the modifier list seems + // to give slightly more helpful errors when one of the callback macros + // fails to parse the modifier list. + let query_name_span = query.name.span(); + quote_spanned! { + query_name_span => + // Search for (QMODLIST) to find all occurrences of this query modifier list. + // tidy-alphabetical-start + anon: #anon, + arena_cache: #arena_cache, + cache_on_disk: #cache_on_disk, + cycle_error_handling: #cycle_error_handling, + depth_limit: #depth_limit, + eval_always: #eval_always, + feedable: #feedable, + no_hash: #no_hash, + returns_error_guaranteed: #returns_error_guaranteed, + separate_provide_extern: #separate_provide_extern, + // tidy-alphabetical-end + } +} + fn doc_comment_from_desc(list: &Punctuated) -> Result { use ::syn::*; let mut iter = list.iter(); @@ -288,31 +347,27 @@ struct HelperTokenStreams { } fn make_helpers_for_query(query: &Query, streams: &mut HelperTokenStreams) { - let Query { name, key, modifiers, arg, .. } = &query; + let Query { name, key_pat, key_ty, modifiers, .. } = &query; // Replace span for `name` to make rust-analyzer ignore it. let mut erased_name = name.clone(); erased_name.set_span(Span::call_site()); // Generate a function to check whether we should cache the query to disk, for some key. - if let Some(CacheOnDiskIf { tcx_binding, block, .. }) = modifiers.cache_on_disk_if.as_ref() { - let tcx = tcx_binding.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ }); - // we're taking `key` by reference, but some rustc types usually prefer being passed by value + if let Some(CacheOnDiskIf { block, .. }) = modifiers.cache_on_disk_if.as_ref() { streams.cache_on_disk_if_fns_stream.extend(quote! { - #[allow(unused_variables, rustc::pass_by_value)] + #[allow(unused_variables)] #[inline] - pub fn #erased_name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::queries::#name::Key<'tcx>) -> bool + pub fn #erased_name<'tcx>(tcx: TyCtxt<'tcx>, #key_pat: #key_ty) -> bool #block }); } - let Desc { tcx_binding, expr_list, .. } = &modifiers.desc; - let tcx = tcx_binding.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t }); + let Desc { expr_list, .. } = &modifiers.desc; let desc = quote! { #[allow(unused_variables)] - pub fn #erased_name<'tcx>(tcx: TyCtxt<'tcx>, key: #arg) -> String { - let (#tcx, #key) = (tcx, key); + pub fn #erased_name<'tcx>(tcx: TyCtxt<'tcx>, #key_pat: #key_ty) -> String { format!(#expr_list) } }; @@ -355,7 +410,6 @@ macro_rules! doc_link { doc_link!( arena_cache, - cycle_fatal, cycle_delay_bug, cycle_stash, no_hash, @@ -364,7 +418,6 @@ macro_rules! doc_link { depth_limit, separate_provide_extern, feedable, - return_result_from_ensure_ok, ); let name = &query.name; @@ -373,16 +426,16 @@ macro_rules! doc_link { let mut erased_name = name.clone(); erased_name.set_span(Span::call_site()); - let result = &query.result; + let result = &query.return_ty; // This dead code exists to instruct rust-analyzer about the link between the `rustc_queries` // query names and the corresponding produced provider. The issue is that by nature of this // macro producing a higher order macro that has all its token in the macro declaration we lose // any meaningful spans, resulting in rust-analyzer being unable to make the connection between // the query name and the corresponding providers field. The trick to fix this is to have - // `rustc_queries` emit a field access with the given name's span which allows it to successfully - // show references / go to definition to the corresponding provider assignment which is usually - // the more interesting place. + // `rustc_queries` emit a field access with the given name's span which allows it to + // successfully show references / go to definition to the corresponding provider assignment + // which is usually the more interesting place. let ra_hint = quote! { let crate::query::Providers { #name: _, .. }; }; @@ -398,11 +451,11 @@ fn #erased_name<'tcx>() #result { } pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { - let queries = parse_macro_input!(input as List); + let queries = parse_macro_input!(input as List); let mut query_stream = quote! {}; + let mut non_query_stream = quote! {}; let mut helpers = HelperTokenStreams::default(); - let mut feedable_queries = quote! {}; let mut analyzer_stream = quote! {}; let mut errors = quote! {}; @@ -417,57 +470,33 @@ macro_rules! assert { } for query in queries.0 { - let Query { name, arg, modifiers, .. } = &query; - let result_full = &query.result; - let result = match query.result { - ReturnType::Default => quote! { -> () }, - _ => quote! { #result_full }, + let query = match query { + QueryEntry::Query(query) => query, + QueryEntry::NonQuery(NonQuery { doc_comments, name }) => { + // Get the exceptional non-query case out of the way first. + non_query_stream.extend(quote! { + #(#doc_comments)* + #name, + }); + continue; + } }; - let mut attributes = Vec::new(); + let Query { doc_comments, name, key_ty, return_ty, modifiers, .. } = &query; - macro_rules! passthrough { - ( $( $modifier:ident ),+ $(,)? ) => { - $( if let Some($modifier) = &modifiers.$modifier { - attributes.push(quote! { (#$modifier) }); - }; )+ - } - } + // Normalize an absent return type into `-> ()` to make macro-rules parsing easier. + let return_ty = match return_ty { + ReturnType::Default => quote! { -> () }, + ReturnType::Type(..) => quote! { #return_ty }, + }; - passthrough!( - arena_cache, - cycle_fatal, - cycle_delay_bug, - cycle_stash, - no_hash, - anon, - eval_always, - feedable, - depth_limit, - separate_provide_extern, - return_result_from_ensure_ok, - ); + let modifiers_stream = make_modifiers_stream(&query); - // If there was a `cache_on_disk_if` modifier in the real input, pass - // on a synthetic `(cache_on_disk)` modifier that can be inspected by - // macro-rules macros. - if modifiers.cache_on_disk_if.is_some() { - attributes.push(quote! { (cache_on_disk) }); - } - - // This uses the span of the query definition for the commas, - // which can be important if we later encounter any ambiguity - // errors with any of the numerous macro_rules! macros that - // we use. Using the call-site span would result in a span pointing - // at the entire `rustc_queries!` invocation, which wouldn't - // be very useful. - let span = name.span(); - let attribute_stream = quote_spanned! {span=> #(#attributes),*}; - let doc_comments = &query.doc_comments; // Add the query to the group query_stream.extend(quote! { #(#doc_comments)* - [#attribute_stream] fn #name(#arg) #result, + fn #name(#key_ty) #return_ty + { #modifiers_stream } }); if let Some(feedable) = &modifiers.feedable { @@ -481,9 +510,6 @@ macro_rules! passthrough { feedable.span(), "Query {name} cannot be both `feedable` and `eval_always`." ); - feedable_queries.extend(quote! { - [#attribute_stream] fn #name(#arg) #result, - }); } add_to_analyzer_stream(&query, &mut analyzer_stream); @@ -493,32 +519,21 @@ macro_rules! passthrough { let HelperTokenStreams { description_fns_stream, cache_on_disk_if_fns_stream } = helpers; TokenStream::from(quote! { - /// Higher-order macro that invokes the specified macro with a prepared - /// list of all query signatures (including modifiers). - /// - /// This allows multiple simpler macros to each have access to the list - /// of queries. + /// Higher-order macro that invokes the specified macro with (a) a list of all query + /// signatures (including modifiers), and (b) a list of non-query names. This allows + /// multiple simpler macros to each have access to these lists. #[macro_export] macro_rules! rustc_with_all_queries { ( - // The macro to invoke once, on all queries (plus extras). + // The macro to invoke once, on all queries and non-queries. $macro:ident! - - // Within [], an optional list of extra "query" signatures to - // pass to the given macro, in addition to the actual queries. - $( [$($extra_fake_queries:tt)*] )? ) => { $macro! { - $( $($extra_fake_queries)* )? - #query_stream + queries { #query_stream } + non_queries { #non_query_stream } } } } - macro_rules! rustc_feedable_queries { - ( $macro:ident! ) => { - $macro!(#feedable_queries); - } - } // Add hints for rust-analyzer mod _analyzer_hints { diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index e3a70372110a..44cd63e57af4 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -189,7 +189,14 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let mut keyword_stream = quote! {}; let mut symbols_stream = quote! {}; let mut prefill_stream = quote! {}; - let mut entries = Entries::with_capacity(input.keywords.len() + input.symbols.len() + 10); + let prefill_ints = 0..=9; + let prefill_letters = ('A'..='Z').chain('a'..='z'); + let mut entries = Entries::with_capacity( + input.keywords.len() + + input.symbols.len() + + prefill_ints.clone().count() + + prefill_letters.clone().count(), + ); // Generate the listed keywords. for keyword in input.keywords.iter() { @@ -234,12 +241,11 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { }); } - // Generate symbols for the strings "0", "1", ..., "9". - for n in 0..10 { - let n = n.to_string(); - entries.insert(Span::call_site(), &n, &mut errors); + // Generate symbols for ascii letters and digits + for s in prefill_ints.map(|n| n.to_string()).chain(prefill_letters.map(|c| c.to_string())) { + entries.insert(Span::call_site(), &s, &mut errors); prefill_stream.extend(quote! { - #n, + #s, }); } @@ -285,9 +291,13 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { } let symbol_digits_base = entries.map["0"].idx; + let symbol_uppercase_letters_base = entries.map["A"].idx; + let symbol_lowercase_letters_base = entries.map["a"].idx; let predefined_symbols_count = entries.len(); let output = quote! { const SYMBOL_DIGITS_BASE: u32 = #symbol_digits_base; + const SYMBOL_UPPERCASE_LETTERS_BASE: u32 = #symbol_uppercase_letters_base; + const SYMBOL_LOWERCASE_LETTERS_BASE: u32 = #symbol_lowercase_letters_base; /// The number of predefined symbols; this is the first index for /// extra pre-interned symbols in an Interner created via diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index a74d387ad5a4..1547648100d5 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -146,7 +146,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { && !sess.target.crt_static_allows_dylibs) { for &cnum in tcx.crates(()).iter() { - if tcx.dep_kind(cnum).macros_only() { + if tcx.crate_dep_kind(cnum).macros_only() { continue; } let src = tcx.used_crate_source(cnum); @@ -163,7 +163,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { let all_dylibs = || { tcx.crates(()).iter().filter(|&&cnum| { - !tcx.dep_kind(cnum).macros_only() + !tcx.crate_dep_kind(cnum).macros_only() && (tcx.used_crate_source(cnum).dylib.is_some() || tcx.used_crate_source(cnum).sdylib_interface.is_some()) }) @@ -241,7 +241,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { let src = tcx.used_crate_source(cnum); if src.dylib.is_none() && !formats.contains_key(&cnum) - && tcx.dep_kind(cnum) == CrateDepKind::Unconditional + && tcx.crate_dep_kind(cnum) == CrateDepKind::Unconditional { assert!(src.rlib.is_some() || src.rmeta.is_some()); info!("adding staticlib: {}", tcx.crate_name(cnum)); @@ -315,7 +315,7 @@ fn add_library( crate_name: tcx.crate_name(cnum), non_static_deps: unavailable_as_static .drain(..) - .map(|cnum| NonStaticCrateDep { crate_name_: tcx.crate_name(cnum) }) + .map(|cnum| NonStaticCrateDep { sub_crate_name: tcx.crate_name(cnum) }) .collect(), rustc_driver_help: linking_to_rustc_driver, }); @@ -333,7 +333,7 @@ fn attempt_static(tcx: TyCtxt<'_>, unavailable: &mut Vec) -> Option, unavailable: &mut Vec) -> Option Linkage::Static, CrateDepKind::MacrosOnly | CrateDepKind::Conditional => Linkage::NotLinked, }), diff --git a/compiler/rustc_metadata/src/eii.rs b/compiler/rustc_metadata/src/eii.rs index 42497a82ef8c..f3ce07aa75a1 100644 --- a/compiler/rustc_metadata/src/eii.rs +++ b/compiler/rustc_metadata/src/eii.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::FxIndexMap; -use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl, EiiImplResolution}; +use rustc_hir::attrs::{EiiDecl, EiiImpl, EiiImplResolution}; use rustc_hir::def_id::DefId; use rustc_hir::find_attr; use rustc_middle::query::LocalCrate; @@ -25,14 +25,11 @@ pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap // iterate over all items in the current crate for id in tcx.hir_crate_items(()).eiis() { - for i in - find_attr!(tcx.get_all_attrs(id), AttributeKind::EiiImpls(e) => e).into_iter().flatten() - { + for i in find_attr!(tcx, id, EiiImpls(e) => e).into_iter().flatten() { let decl = match i.resolution { EiiImplResolution::Macro(macro_defid) => { // find the decl for this one if it wasn't in yet (maybe it's from the local crate? not very useful but not illegal) - let Some(decl) = find_attr!(tcx.get_all_attrs(macro_defid), AttributeKind::EiiDeclaration(d) => *d) - else { + let Some(decl) = find_attr!(tcx, macro_defid, EiiDeclaration(d) => *d) else { // skip if it doesn't have eii_declaration (if we resolved to another macro that's not an EII) tcx.dcx() .span_delayed_bug(i.span, "resolved to something that's not an EII"); @@ -52,9 +49,7 @@ pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap } // if we find a new declaration, add it to the list without a known implementation - if let Some(decl) = - find_attr!(tcx.get_all_attrs(id), AttributeKind::EiiDeclaration(d) => *d) - { + if let Some(decl) = find_attr!(tcx, id, EiiDeclaration(d) => *d) { eiis.entry(decl.foreign_item).or_insert((decl, Default::default())); } } diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index 7320ad98d113..d4c61441bcea 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -48,10 +48,10 @@ pub struct CrateDepMultiple { } #[derive(Subdiagnostic)] -#[note("`{$crate_name}` was unavailable as a static crate, preventing fully static linking")] +#[note("`{$sub_crate_name}` was unavailable as a static crate, preventing fully static linking")] pub struct NonStaticCrateDep { /// It's different from `crate_name` in main Diagnostic. - pub crate_name_: Symbol, + pub sub_crate_name: Symbol, } #[derive(Diagnostic)] diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 73a97a67bc90..1dff5740ab3b 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -3,7 +3,6 @@ #![feature(error_iter)] #![feature(file_buffered)] #![feature(gen_blocks)] -#![feature(if_let_guard)] #![feature(macro_metavar_expr)] #![feature(min_specialization)] #![feature(never_type)] diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 004d73da8cbd..5af60e9f19da 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -438,7 +438,8 @@ fn find_library_crate( } if let Some(matches) = spf.query(prefix, suffix) { for (hash, spf) in matches { - info!("lib candidate: {}", spf.path.display()); + let spf_path = spf.path(&search_path.dir); + info!("lib candidate: {}", spf_path.display()); let (rlibs, rmetas, dylibs, interfaces) = candidates.entry(hash).or_default(); @@ -447,8 +448,8 @@ fn find_library_crate( // ones we've already seen. This allows us to ignore crates // we know are exactual equal to ones we've already found. // Going to the same crate through different symlinks does not change the result. - let path = try_canonicalize(&spf.path) - .unwrap_or_else(|_| spf.path.to_path_buf()); + let path = + try_canonicalize(&spf_path).unwrap_or_else(|_| spf_path.clone()); if seen_paths.contains(&path) { continue; }; @@ -456,12 +457,11 @@ fn find_library_crate( } // Use the original path (potentially with unresolved symlinks), // filesystem code should not care, but this is nicer for diagnostics. - let path = spf.path.to_path_buf(); match kind { - CrateFlavor::Rlib => rlibs.insert(path), - CrateFlavor::Rmeta => rmetas.insert(path), - CrateFlavor::Dylib => dylibs.insert(path), - CrateFlavor::SDylib => interfaces.insert(path), + CrateFlavor::Rlib => rlibs.insert(spf_path), + CrateFlavor::Rmeta => rmetas.insert(spf_path), + CrateFlavor::Dylib => dylibs.insert(spf_path), + CrateFlavor::SDylib => interfaces.insert(spf_path), }; } } @@ -472,7 +472,7 @@ fn find_library_crate( { for (_, spf) in static_matches { crate_rejections.via_kind.push(CrateMismatch { - path: spf.path.to_path_buf(), + path: spf.path(&search_path.dir), got: "static".to_string(), }); } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 0c06d1be9a3f..52b11615c76f 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -4,7 +4,7 @@ use rustc_abi::ExternAbi; use rustc_attr_parsing::eval_config_entry; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::attrs::{AttributeKind, NativeLibKind, PeImportNameType}; +use rustc_hir::attrs::{NativeLibKind, PeImportNameType}; use rustc_hir::find_attr; use rustc_middle::query::LocalCrate; use rustc_middle::ty::{self, List, Ty, TyCtxt}; @@ -61,6 +61,8 @@ pub fn walk_native_lib_search_dirs( // library directory instead of the self-contained directories. // Sanitizer libraries have the same issue and are also linked by name on Apple targets. // The targets here should be in sync with `copy_third_party_objects` in bootstrap. + // Finally there is shared LLVM library, which unlike compiler libraries, is linked by the name, + // therefore requiring the search path for the linker. // FIXME: implement `-Clink-self-contained=+/-unwind,+/-sanitizers`, move the shipped libunwind // and sanitizers to self-contained directory, and stop adding this search path. // FIXME: On AIX this also has the side-effect of making the list of library search paths @@ -71,6 +73,9 @@ pub fn walk_native_lib_search_dirs( || sess.target.os == Os::Fuchsia || sess.target.is_like_aix || sess.target.is_like_darwin && !sess.sanitizers().is_empty() + || sess.target.os == Os::Windows + && sess.target.env == Env::Gnu + && sess.target.abi == Abi::Llvm { f(&sess.target_tlib_path.dir, false)?; } @@ -209,10 +214,7 @@ fn process_module(&mut self, module: &ForeignModule) { } for attr in - find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::Link(links, _) => links) - .iter() - .map(|v| v.iter()) - .flatten() + find_attr!(self.tcx, def_id, Link(links, _) => links).iter().map(|v| v.iter()).flatten() { let dll_imports = match attr.kind { NativeLibKind::RawDylib { .. } => foreign_items @@ -227,7 +229,8 @@ fn process_module(&mut self, module: &ForeignModule) { .collect(), _ => { for &child_item in foreign_items { - if let Some(span) = find_attr!(self.tcx.get_all_attrs(child_item), AttributeKind::LinkOrdinal {span, ..} => *span) + if let Some(span) = + find_attr!(self.tcx, child_item, LinkOrdinal {span, ..} => *span) { sess.dcx().emit_err(errors::LinkOrdinalRawDylib { span }); } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index bd5b3893e4e8..2fa8e19984b4 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1329,7 +1329,9 @@ fn get_associated_item_or_field_def_ids( fn get_associated_item(self, tcx: TyCtxt<'_>, id: DefIndex) -> ty::AssocItem { let kind = match self.def_kind(tcx, id) { - DefKind::AssocConst => ty::AssocKind::Const { name: self.item_name(id) }, + DefKind::AssocConst { is_type_const } => { + ty::AssocKind::Const { name: self.item_name(id), is_type_const } + } DefKind::AssocFn => ty::AssocKind::Fn { name: self.item_name(id), has_self: self.get_fn_has_self_parameter(tcx, id), diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 6ea9a2852804..01e6d49a51c7 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -147,8 +147,8 @@ fn $name<'tcx>( // External query providers call `crate_hash` in order to register a dependency // on the crate metadata. The exception is `crate_hash` itself, which obviously // doesn't need to do this (and can't, as it would cause a query cycle). - use rustc_middle::dep_graph::dep_kinds; - if dep_kinds::$name != dep_kinds::crate_hash && $tcx.dep_graph.is_fully_enabled() { + use rustc_middle::dep_graph::DepKind; + if DepKind::$name != DepKind::crate_hash && $tcx.dep_graph.is_fully_enabled() { $tcx.ensure_ok().crate_hash($def_id.krate); } @@ -382,7 +382,7 @@ fn into_args(self) -> (DefId, SimplifiedType) { implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) } crate_incoherent_impls => { cdata.get_incoherent_impls(tcx, other) } - dep_kind => { cdata.dep_kind } + crate_dep_kind => { cdata.dep_kind } module_children => { tcx.arena.alloc_from_iter(cdata.get_module_children(tcx, def_id.index)) } @@ -418,7 +418,6 @@ fn into_args(self) -> (DefId, SimplifiedType) { } anon_const_kind => { table } const_of_item => { table } - is_rhs_type_const => { table } } pub(in crate::rmeta) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 3a85b0a05052..fbc7232f3a27 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -734,19 +734,16 @@ macro_rules! stat { has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE), has_alloc_error_handler: tcx.has_alloc_error_handler(LOCAL_CRATE), has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE), - has_default_lib_allocator: ast::attr::contains_name( - attrs, - sym::default_lib_allocator, - ), + has_default_lib_allocator: find_attr!(attrs, DefaultLibAllocator), externally_implementable_items, proc_macro_data, debugger_visualizers, - compiler_builtins: find_attr!(attrs, AttributeKind::CompilerBuiltins), - needs_allocator: find_attr!(attrs, AttributeKind::NeedsAllocator), - needs_panic_runtime: find_attr!(attrs, AttributeKind::NeedsPanicRuntime), - no_builtins: find_attr!(attrs, AttributeKind::NoBuiltins), - panic_runtime: find_attr!(attrs, AttributeKind::PanicRuntime), - profiler_runtime: find_attr!(attrs, AttributeKind::ProfilerRuntime), + compiler_builtins: find_attr!(attrs, CompilerBuiltins), + needs_allocator: find_attr!(attrs, NeedsAllocator), + needs_panic_runtime: find_attr!(attrs, NeedsPanicRuntime), + no_builtins: find_attr!(attrs, NoBuiltins), + panic_runtime: find_attr!(attrs, PanicRuntime), + profiler_runtime: find_attr!(attrs, ProfilerRuntime), symbol_mangling_version: tcx.sess.opts.get_symbol_mangling_version(), crate_deps, @@ -907,11 +904,11 @@ fn should_encode_span(def_kind: DefKind) -> bool { | DefKind::ConstParam | DefKind::LifetimeParam | DefKind::Fn - | DefKind::Const + | DefKind::Const { .. } | DefKind::Static { .. } | DefKind::Ctor(..) | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Macro(_) | DefKind::ExternCrate | DefKind::Use @@ -939,10 +936,10 @@ fn should_encode_attrs(def_kind: DefKind) -> bool { | DefKind::TraitAlias | DefKind::AssocTy | DefKind::Fn - | DefKind::Const + | DefKind::Const { .. } | DefKind::Static { nested: false, .. } | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Macro(_) | DefKind::Field | DefKind::Impl { .. } => true, @@ -982,12 +979,12 @@ fn should_encode_expn_that_defined(def_kind: DefKind) -> bool { | DefKind::AssocTy | DefKind::TyParam | DefKind::Fn - | DefKind::Const + | DefKind::Const { .. } | DefKind::ConstParam | DefKind::Static { .. } | DefKind::Ctor(..) | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Macro(_) | DefKind::ExternCrate | DefKind::Use @@ -1016,11 +1013,11 @@ fn should_encode_visibility(def_kind: DefKind) -> bool { | DefKind::TraitAlias | DefKind::AssocTy | DefKind::Fn - | DefKind::Const + | DefKind::Const { .. } | DefKind::Static { nested: false, .. } | DefKind::Ctor(..) | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Macro(..) | DefKind::Field => true, DefKind::Use @@ -1049,11 +1046,11 @@ fn should_encode_stability(def_kind: DefKind) -> bool { | DefKind::Struct | DefKind::AssocTy | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::TyParam | DefKind::ConstParam | DefKind::Static { .. } - | DefKind::Const + | DefKind::Const { .. } | DefKind::Fn | DefKind::ForeignMod | DefKind::TyAlias @@ -1105,9 +1102,10 @@ fn should_encode_mir( // instance_mir uses mir_for_ctfe rather than optimized_mir for constructors DefKind::Ctor(_, _) => (true, false), // Constants - DefKind::AnonConst | DefKind::InlineConst | DefKind::AssocConst | DefKind::Const => { - (true, false) - } + DefKind::AnonConst { .. } + | DefKind::InlineConst + | DefKind::AssocConst { .. } + | DefKind::Const { .. } => (true, false), // Coroutines require optimized MIR to compute layout. DefKind::Closure if tcx.is_coroutine(def_id.to_def_id()) => (false, true), DefKind::SyntheticCoroutineBody => (false, true), @@ -1143,11 +1141,11 @@ fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: Def DefKind::Mod | DefKind::Variant | DefKind::Field - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::TyParam | DefKind::ConstParam | DefKind::Static { .. } - | DefKind::Const + | DefKind::Const { .. } | DefKind::ForeignMod | DefKind::Impl { .. } | DefKind::Trait @@ -1178,11 +1176,11 @@ fn should_encode_generics(def_kind: DefKind) -> bool { | DefKind::TraitAlias | DefKind::AssocTy | DefKind::Fn - | DefKind::Const + | DefKind::Const { .. } | DefKind::Static { .. } | DefKind::Ctor(..) | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::AnonConst | DefKind::InlineConst | DefKind::OpaqueTy @@ -1211,13 +1209,13 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> | DefKind::Ctor(..) | DefKind::Field | DefKind::Fn - | DefKind::Const + | DefKind::Const { .. } | DefKind::Static { nested: false, .. } | DefKind::TyAlias | DefKind::ForeignTy | DefKind::Impl { .. } | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Closure | DefKind::ConstParam | DefKind::AnonConst @@ -1272,14 +1270,14 @@ fn should_encode_fn_sig(def_kind: DefKind) -> bool { | DefKind::Enum | DefKind::Variant | DefKind::Field - | DefKind::Const + | DefKind::Const { .. } | DefKind::Static { .. } | DefKind::Ctor(..) | DefKind::TyAlias | DefKind::OpaqueTy | DefKind::ForeignTy | DefKind::Impl { .. } - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Closure | DefKind::ConstParam | DefKind::AnonConst @@ -1311,8 +1309,8 @@ fn should_encode_constness(def_kind: DefKind) -> bool { | DefKind::Union | DefKind::Enum | DefKind::Field - | DefKind::Const - | DefKind::AssocConst + | DefKind::Const { .. } + | DefKind::AssocConst { .. } | DefKind::AnonConst | DefKind::Static { .. } | DefKind::TyAlias @@ -1341,7 +1339,10 @@ fn should_encode_constness(def_kind: DefKind) -> bool { fn should_encode_const(def_kind: DefKind) -> bool { match def_kind { // FIXME(mgca): should we remove Const and AssocConst here? - DefKind::Const | DefKind::AssocConst | DefKind::AnonConst | DefKind::InlineConst => true, + DefKind::Const { .. } + | DefKind::AssocConst { .. } + | DefKind::AnonConst + | DefKind::InlineConst => true, DefKind::Struct | DefKind::Union @@ -1376,7 +1377,7 @@ fn should_encode_const(def_kind: DefKind) -> bool { fn should_encode_const_of_item<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: DefKind) -> bool { // AssocConst ==> assoc item has value tcx.is_type_const(def_id) - && (!matches!(def_kind, DefKind::AssocConst) || assoc_item_has_value(tcx, def_id)) + && (!matches!(def_kind, DefKind::AssocConst { .. }) || assoc_item_has_value(tcx, def_id)) } fn assoc_item_has_value<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { @@ -1630,9 +1631,6 @@ fn encode_def_ids(&mut self) { 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); } - if let DefKind::AssocConst | DefKind::Const = def_kind { - record!(self.tables.is_rhs_type_const[def_id] <- self.tcx.is_rhs_type_const(def_id)); - } } for (def_id, impls) in &tcx.crate_inherent_impls(()).0.inherent_impls { @@ -2015,11 +2013,12 @@ fn encode_proc_macros(&mut self) -> Option { // Proc-macros may have attributes like `#[allow_internal_unstable]`, // so downstream crates need access to them. let attrs = tcx.hir_attrs(proc_macro); - let macro_kind = if find_attr!(attrs, AttributeKind::ProcMacro(..)) { + let macro_kind = if find_attr!(attrs, ProcMacro(..)) { MacroKind::Bang - } else if find_attr!(attrs, AttributeKind::ProcMacroAttribute(..)) { + } else if find_attr!(attrs, ProcMacroAttribute(..)) { MacroKind::Attr - } else if let Some(trait_name) = find_attr!(attrs, AttributeKind::ProcMacroDerive { trait_name, ..} => trait_name) + } else if let Some(trait_name) = + find_attr!(attrs, ProcMacroDerive { trait_name, ..} => trait_name) { name = *trait_name; MacroKind::Derive @@ -2075,7 +2074,7 @@ fn encode_crate_deps(&mut self) -> LazyArray { name: self.tcx.crate_name(cnum), hash: self.tcx.crate_hash(cnum), host_hash: self.tcx.crate_host_hash(cnum), - kind: self.tcx.dep_kind(cnum), + kind: self.tcx.crate_dep_kind(cnum), extra_filename: self.tcx.extra_filename(cnum).clone(), is_private: self.tcx.is_private_dep(cnum), }; @@ -2444,7 +2443,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { if tcx.dep_graph.is_fully_enabled() && let work_product_id = WorkProductId::from_cgu_name("metadata") && let Some(work_product) = tcx.dep_graph.previous_work_product(&work_product_id) - && tcx.try_mark_green(&dep_node) + && tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() { let saved_path = &work_product.saved_files["rmeta"]; let incr_comp_session_dir = tcx.sess.incr_comp_session_dir_opt().unwrap(); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 408b50ae48df..80d7ae4e9cb3 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -8,8 +8,10 @@ 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_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; +use rustc_hir as hir; use rustc_hir::attrs::StrippedCfgItem; use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap, MacroKinds}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIndex, DefPathHash, StableCrateId}; @@ -41,7 +43,6 @@ use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Ident, Span, Symbol}; use rustc_target::spec::{PanicStrategy, TargetTuple}; use table::TableBuilder; -use {rustc_ast as ast, rustc_hir as hir}; use crate::creader::CrateMetadataRef; use crate::eii::EiiMapEncodedKeyValue; @@ -476,7 +477,6 @@ fn encode(&self, buf: &mut FileEncoder) -> LazyTables { anon_const_kind: Table>, const_of_item: Table>>>, associated_types_for_impl_traits_in_trait_or_impl: Table>>>, - is_rhs_type_const: Table>, } #[derive(TyEncodable, TyDecodable)] diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 3b5a38181d58..5e256438ad95 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -175,10 +175,12 @@ macro_rules! const_macro_kinds { ( AssocTy ) ( TyParam ) ( Fn ) - ( Const ) + ( Const { is_type_const: true} ) + ( Const { is_type_const: false} ) ( ConstParam ) ( AssocFn ) - ( AssocConst ) + ( AssocConst { is_type_const:true } ) + ( AssocConst { is_type_const:false } ) ( ExternCrate ) ( Use ) ( ForeignMod ) diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 121e77614725..8bad0e291bf8 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -26,7 +26,6 @@ rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } -rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 0f254aaa9fa0..33237ef84041 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -32,7 +32,7 @@ macro_rules! arena_types { rustc_middle::ty::DefinitionSiteHiddenType<'tcx>, >, [] resolver: rustc_data_structures::steal::Steal<( - rustc_middle::ty::ResolverAstLowering, + rustc_middle::ty::ResolverAstLowering<'tcx>, std::sync::Arc, )>, [] crate_for_resolver: rustc_data_structures::steal::Steal<(rustc_ast::Crate, rustc_ast::AttrVec)>, @@ -90,9 +90,8 @@ macro_rules! arena_types { [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<'tcx>, [decode] attribute: rustc_hir::Attribute, [] name_set: rustc_data_structures::unord::UnordSet, - [] autodiff_item: rustc_ast::expand::autodiff_attrs::AutoDiffItem, + [] autodiff_item: rustc_hir::attrs::AutoDiffItem, [] ordered_name_set: rustc_data_structures::fx::FxIndexSet, - [] valtree: rustc_middle::ty::ValTreeKind>, [] stable_order_of_exportable_impls: rustc_data_structures::fx::FxIndexMap, diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 0033a1cd2337..6f85dba23dd4 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -1,10 +1,10 @@ //! This module defines the [`DepNode`] type which the compiler uses to represent //! nodes in the [dependency graph]. A `DepNode` consists of a [`DepKind`] (which //! specifies the kind of thing it represents, like a piece of HIR, MIR, etc.) -//! and a [`Fingerprint`], a 128-bit hash value, the exact meaning of which -//! depends on the node's `DepKind`. Together, the kind and the fingerprint +//! and a "key fingerprint", a 128-bit hash value, the exact meaning of which +//! depends on the node's `DepKind`. Together, the kind and the key fingerprint //! fully identify a dependency node, even across multiple compilation sessions. -//! In other words, the value of the fingerprint does not depend on anything +//! In other words, the value of the key fingerprint does not depend on anything //! that is specific to a given compilation session, like an unpredictable //! interning key (e.g., `NodeId`, `DefId`, `Symbol`) or the numeric value of a //! pointer. The concept behind this could be compared to how git commit hashes @@ -41,105 +41,91 @@ //! `DepNode`s could represent global concepts with only one value. //! * Whether it is possible, in principle, to reconstruct a query key from a //! given `DepNode`. Many `DepKind`s only require a single `DefId` parameter, -//! in which case it is possible to map the node's fingerprint back to the +//! in which case it is possible to map the node's key fingerprint back to the //! `DefId` it was computed from. In other cases, too much information gets -//! lost during fingerprint computation. -//! -//! `make_compile_codegen_unit` and `make_compile_mono_items`, together with -//! `DepNode::new()`, ensure that only valid `DepNode` instances can be -//! constructed. For example, the API does not allow for constructing -//! parameterless `DepNode`s with anything other than a zeroed out fingerprint. -//! More generally speaking, it relieves the user of the `DepNode` API of -//! having to know how to compute the expected fingerprint for a given set of -//! node parameters. +//! lost when computing a key fingerprint. //! //! [dependency graph]: https://rustc-dev-guide.rust-lang.org/query.html use std::fmt; use std::hash::Hash; -use rustc_data_structures::AtomicRef; use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey}; +use rustc_data_structures::stable_hasher::{StableHasher, StableOrd, ToStableHashKey}; use rustc_hir::def_id::DefId; use rustc_hir::definitions::DefPathHash; -use rustc_macros::{Decodable, Encodable}; -use rustc_query_system::ich::StableHashingContext; +use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::Symbol; -use super::{DepContext, FingerprintStyle, SerializedDepNodeIndex}; +use super::{KeyFingerprintStyle, SerializedDepNodeIndex}; +use crate::dep_graph::DepNodeKey; use crate::mir::mono::MonoItem; -use crate::ty::TyCtxt; - -/// This serves as an index into arrays built by `make_dep_kind_array`. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct DepKind { - variant: u16, -} +use crate::ty::{TyCtxt, tls}; +// `enum DepKind` is generated by `define_dep_nodes!` below. impl DepKind { #[inline] - pub const fn new(variant: u16) -> Self { - Self { variant } + pub(crate) fn from_u16(u: u16) -> Self { + if u > Self::MAX { + panic!("Invalid DepKind {u}"); + } + // SAFETY: See comment on DEP_KIND_NUM_VARIANTS + unsafe { std::mem::transmute(u) } } #[inline] - pub const fn as_inner(&self) -> u16 { - self.variant + pub(crate) const fn as_u16(&self) -> u16 { + *self as u16 } #[inline] pub const fn as_usize(&self) -> usize { - self.variant as usize - } -} - -pub fn default_dep_kind_debug(kind: DepKind, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("DepKind").field("variant", &kind.variant).finish() -} - -pub static DEP_KIND_DEBUG: AtomicRef) -> fmt::Result> = - AtomicRef::new(&(default_dep_kind_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); - -impl fmt::Debug for DepKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*DEP_KIND_DEBUG)(*self, f) + *self as usize } + + /// This is the highest value a `DepKind` can have. It's used during encoding to + /// pack information into the unused bits. + pub(crate) const MAX: u16 = DEP_KIND_NUM_VARIANTS - 1; } +/// Combination of a [`DepKind`] and a key fingerprint that uniquely identifies +/// a node in the dep graph. #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct DepNode { pub kind: DepKind, - pub hash: PackedFingerprint, + + /// This is _typically_ a hash of the query key, but sometimes not. + /// + /// For example, `anon` nodes have a fingerprint that is derived from their + /// dependencies instead of a key. + /// + /// In some cases the key value can be reconstructed from this fingerprint; + /// see [`KeyFingerprintStyle`]. + pub key_fingerprint: PackedFingerprint, } impl DepNode { /// Creates a new, parameterless DepNode. This method will assert /// that the DepNode corresponding to the given DepKind actually /// does not require any parameters. - pub fn new_no_params(tcx: Tcx, kind: DepKind) -> DepNode - where - Tcx: super::DepContext, - { - debug_assert_eq!(tcx.fingerprint_style(kind), FingerprintStyle::Unit); - DepNode { kind, hash: Fingerprint::ZERO.into() } + pub fn new_no_params<'tcx>(tcx: TyCtxt<'tcx>, kind: DepKind) -> DepNode { + debug_assert_eq!(tcx.key_fingerprint_style(kind), KeyFingerprintStyle::Unit); + DepNode { kind, key_fingerprint: Fingerprint::ZERO.into() } } - pub fn construct(tcx: Tcx, kind: DepKind, arg: &Key) -> DepNode + pub fn construct<'tcx, Key>(tcx: TyCtxt<'tcx>, kind: DepKind, key: &Key) -> DepNode where - Tcx: super::DepContext, - Key: DepNodeKey, + Key: DepNodeKey<'tcx>, { - let hash = arg.to_fingerprint(tcx); - let dep_node = DepNode { kind, hash: hash.into() }; + let dep_node = DepNode { kind, key_fingerprint: key.to_fingerprint(tcx).into() }; #[cfg(debug_assertions)] { - if !tcx.fingerprint_style(kind).reconstructible() - && (tcx.sess().opts.unstable_opts.incremental_info - || tcx.sess().opts.unstable_opts.query_dep_graph) + if !tcx.key_fingerprint_style(kind).is_maybe_recoverable() + && (tcx.sess.opts.unstable_opts.incremental_info + || tcx.sess.opts.unstable_opts.query_dep_graph) { - tcx.dep_graph().register_dep_node_debug_str(dep_node, || arg.to_debug_str(tcx)); + tcx.dep_graph.register_dep_node_debug_str(dep_node, || key.to_debug_str(tcx)); } } @@ -149,77 +135,36 @@ pub fn construct(tcx: Tcx, kind: DepKind, arg: &Key) -> DepNode /// Construct a DepNode from the given DepKind and DefPathHash. This /// method will assert that the given DepKind actually requires a /// single DefId/DefPathHash parameter. - pub fn from_def_path_hash(tcx: Tcx, def_path_hash: DefPathHash, kind: DepKind) -> Self - where - Tcx: super::DepContext, - { - debug_assert!(tcx.fingerprint_style(kind) == FingerprintStyle::DefPathHash); - DepNode { kind, hash: def_path_hash.0.into() } + pub fn from_def_path_hash<'tcx>( + tcx: TyCtxt<'tcx>, + def_path_hash: DefPathHash, + kind: DepKind, + ) -> Self { + debug_assert!(tcx.key_fingerprint_style(kind) == KeyFingerprintStyle::DefPathHash); + DepNode { kind, key_fingerprint: def_path_hash.0.into() } } } -pub fn default_dep_node_debug(node: DepNode, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("DepNode").field("kind", &node.kind).field("hash", &node.hash).finish() -} - -pub static DEP_NODE_DEBUG: AtomicRef) -> fmt::Result> = - AtomicRef::new(&(default_dep_node_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); - impl fmt::Debug for DepNode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*DEP_NODE_DEBUG)(*self, f) - } -} + write!(f, "{:?}(", self.kind)?; -/// Trait for query keys as seen by dependency-node tracking. -pub trait DepNodeKey: fmt::Debug + Sized { - fn fingerprint_style() -> FingerprintStyle; + tls::with_opt(|opt_tcx| { + if let Some(tcx) = opt_tcx { + if let Some(def_id) = self.extract_def_id(tcx) { + write!(f, "{}", tcx.def_path_debug_str(def_id))?; + } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(*self) { + write!(f, "{s}")?; + } else { + write!(f, "{}", self.key_fingerprint)?; + } + } else { + write!(f, "{}", self.key_fingerprint)?; + } + Ok(()) + })?; - /// This method turns a query key into an opaque `Fingerprint` to be used - /// in `DepNode`. - fn to_fingerprint(&self, _: Tcx) -> Fingerprint; - - fn to_debug_str(&self, tcx: Tcx) -> String; - - /// This method tries to recover the query key from the given `DepNode`, - /// something which is needed when forcing `DepNode`s during red-green - /// evaluation. The query system will only call this method if - /// `fingerprint_style()` is not `FingerprintStyle::Opaque`. - /// It is always valid to return `None` here, in which case incremental - /// compilation will treat the query as having changed instead of forcing it. - fn recover(tcx: Tcx, dep_node: &DepNode) -> Option; -} - -// Blanket impl of `DepNodeKey`, which is specialized by other impls elsewhere. -impl DepNodeKey for T -where - T: for<'a> HashStable> + fmt::Debug, -{ - #[inline(always)] - default fn fingerprint_style() -> FingerprintStyle { - FingerprintStyle::Opaque - } - - #[inline(always)] - default fn to_fingerprint(&self, tcx: Tcx) -> Fingerprint { - tcx.with_stable_hashing_context(|mut hcx| { - let mut hasher = StableHasher::new(); - self.hash_stable(&mut hcx, &mut hasher); - hasher.finish() - }) - } - - #[inline(always)] - default fn to_debug_str(&self, tcx: Tcx) -> String { - // Make sure to print dep node params with reduced queries since printing - // may themselves call queries, which may lead to (possibly untracked!) - // query cycles. - tcx.with_reduced_queries(|| format!("{self:?}")) - } - - #[inline(always)] - default fn recover(_: Tcx, _: &DepNode) -> Option { - None + write!(f, ")") } } @@ -228,21 +173,17 @@ impl DepNodeKey for T /// Information is retrieved by indexing the `DEP_KINDS` array using the integer value /// of the `DepKind`. Overall, this allows to implement `DepContext` using this manual /// jump table instead of large matches. -pub struct DepKindVTable { - /// Anonymous queries cannot be replayed from one compiler invocation to the next. - /// When their result is needed, it is recomputed. They are useful for fine-grained - /// dependency tracking, and caching within one compiler invocation. - pub is_anon: bool, - +pub struct DepKindVTable<'tcx> { /// Eval-always queries do not track their dependencies, and are always recomputed, even if /// their inputs have not changed since the last compiler invocation. The result is still /// cached within one compiler invocation. pub is_eval_always: bool, - /// Indicates whether and how the query key can be recovered from its hashed fingerprint. + /// Indicates whether and how a query key can be reconstructed from the + /// key fingerprint of a dep node with this [`DepKind`]. /// /// The [`DepNodeKey`] trait determines the fingerprint style for each key type. - pub fingerprint_style: FingerprintStyle, + pub key_fingerprint_style: KeyFingerprintStyle, /// The red/green evaluation system will try to mark a specific DepNode in the /// dependency graph as green by recursively trying to mark the dependencies of @@ -254,7 +195,7 @@ pub struct DepKindVTable { /// `force_from_dep_node()` implements. /// /// In the general case, a `DepNode` consists of a `DepKind` and an opaque - /// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint + /// "key fingerprint" that will uniquely identify the node. This key fingerprint /// is usually constructed by computing a stable hash of the query-key that the /// `DepNode` corresponds to. Consequently, it is not in general possible to go /// back from hash to query-key (since hash functions are not reversible). For @@ -268,7 +209,7 @@ pub struct DepKindVTable { /// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. /// Fortunately, we can use some contextual information that will allow us to /// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we - /// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a + /// enforce by construction that the key fingerprint of certain `DepNode`s is a /// valid `DefPathHash`. Since we also always build a huge table that maps every /// `DefPathHash` in the current codebase to the corresponding `DefId`, we have /// everything we need to re-run the query. @@ -276,17 +217,15 @@ pub struct DepKindVTable { /// Take the `mir_promoted` query as an example. Like many other queries, it /// just has a single parameter: the `DefId` of the item it will compute the /// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` - /// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` + /// with kind `mir_promoted`, we know that the key fingerprint of the `DepNode` /// is actually a `DefPathHash`, and can therefore just look up the corresponding /// `DefId` in `tcx.def_path_hash_to_def_id`. - pub force_from_dep_node: - Option bool>, + pub force_from_dep_node_fn: Option< + fn(tcx: TyCtxt<'tcx>, dep_node: DepNode, prev_index: SerializedDepNodeIndex) -> bool, + >, /// Invoke a query to put the on-disk cached value in memory. - pub try_load_from_on_disk_cache: Option, - - /// The name of this dep kind. - pub name: &'static &'static str, + pub promote_from_disk_fn: Option, DepNode)>, } /// A "work product" corresponds to a `.o` (or other) file that we @@ -294,7 +233,9 @@ pub struct DepKindVTable { /// some independent path or string that persists between runs without /// the need to be mapped or unmapped. (This ensures we can serialize /// them even in the absence of a tcx.) -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] +#[derive( + Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, HashStable +)] pub struct WorkProductId { hash: Fingerprint, } @@ -306,13 +247,6 @@ pub fn from_cgu_name(cgu_name: &str) -> WorkProductId { WorkProductId { hash: hasher.finish() } } } - -impl HashStable for WorkProductId { - #[inline] - fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { - self.hash.hash_stable(hcx, hasher) - } -} impl ToStableHashKey for WorkProductId { type KeyType = Fingerprint; #[inline] @@ -328,44 +262,45 @@ impl StableOrd for WorkProductId { const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } +// Note: `$K` and `$V` are unused but present so this can be called by `rustc_with_all_queries`. macro_rules! define_dep_nodes { ( - $( - $(#[$attr:meta])* - [$($modifiers:tt)*] fn $variant:ident($($K:tt)*) -> $V:ty, - )* - ) => { - - #[macro_export] - macro_rules! make_dep_kind_array { - ($mod:ident) => {[ $($mod::$variant()),* ]}; + queries { + $( + $(#[$q_attr:meta])* + fn $q_name:ident($K:ty) -> $V:ty + // Search for (QMODLIST) to find all occurrences of this query modifier list. + // Query modifiers are currently not used here, so skip the whole list. + { $($modifiers:tt)* } + )* } - - /// This enum serves as an index into arrays built by `make_dep_kind_array`. + non_queries { + $( + $(#[$nq_attr:meta])* + $nq_name:ident, + )* + } + ) => { // This enum has more than u8::MAX variants so we need some kind of multi-byte // encoding. The derived Encodable/Decodable uses leb128 encoding which is // dense when only considering this enum. But DepKind is encoded in a larger // struct, and there we can take advantage of the unused bits in the u16. + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[allow(non_camel_case_types)] - #[repr(u16)] // Must be kept in sync with the inner type of `DepKind`. - enum DepKindDefs { - $( $( #[$attr] )* $variant),* + #[repr(u16)] // Must be kept in sync with the rest of `DepKind`. + pub enum DepKind { + $( $(#[$nq_attr])* $nq_name, )* + $( $(#[$q_attr])* $q_name, )* } - #[allow(non_upper_case_globals)] - pub mod dep_kinds { - use super::*; - - $( - // The `as u16` cast must be kept in sync with the inner type of `DepKind`. - pub const $variant: DepKind = DepKind::new(DepKindDefs::$variant as u16); - )* - } - - // This checks that the discriminants of the variants have been assigned consecutively - // from 0 so that they can be used as a dense index. - pub(crate) const DEP_KIND_VARIANTS: u16 = { - let deps = &[$(dep_kinds::$variant,)*]; + // This computes the number of dep kind variants. Along the way, it sanity-checks that the + // discriminants of the variants have been assigned consecutively from 0 so that they can + // be used as a dense index, and that all discriminants fit in a `u16`. + pub(crate) const DEP_KIND_NUM_VARIANTS: u16 = { + let deps = &[ + $(DepKind::$nq_name,)* + $(DepKind::$q_name,)* + ]; let mut i = 0; while i < deps.len() { if i != deps[i].as_usize() { @@ -373,18 +308,14 @@ pub mod dep_kinds { } i += 1; } + assert!(deps.len() <= u16::MAX as usize); deps.len() as u16 }; - /// List containing the name of each dep kind as a static string, - /// indexable by `DepKind`. - pub(crate) const DEP_KIND_NAMES: &[&str] = &[ - $( self::label_strs::$variant, )* - ]; - pub(super) fn dep_kind_from_label_string(label: &str) -> Result { match label { - $( self::label_strs::$variant => Ok(self::dep_kinds::$variant), )* + $( stringify!($nq_name) => Ok(self::DepKind::$nq_name), )* + $( stringify!($q_name) => Ok(self::DepKind::$q_name), )* _ => Err(()), } } @@ -393,30 +324,19 @@ pub(super) fn dep_kind_from_label_string(label: &str) -> Result { /// DepNode groups for tests. #[expect(non_upper_case_globals)] pub mod label_strs { - $( pub const $variant: &str = stringify!($variant); )* + $( pub const $nq_name: &str = stringify!($nq_name); )* + $( pub const $q_name: &str = stringify!($q_name); )* } }; } -// Create various data structures for each query, and also for a few things -// that aren't queries. -rustc_with_all_queries!(define_dep_nodes![ - /// We use this for most things when incr. comp. is turned off. - [] fn Null() -> (), - /// We use this to create a forever-red node. - [] fn Red() -> (), - [] fn SideEffect() -> (), - [] fn AnonZeroDeps() -> (), - [] fn TraitSelect() -> (), - [] fn CompileCodegenUnit() -> (), - [] fn CompileMonoItem() -> (), - [] fn Metadata() -> (), -]); +// Create various data structures for each query, and also for a few things that aren't queries. +rustc_with_all_queries! { define_dep_nodes! } // WARNING: `construct` is generic and does not know that `CompileCodegenUnit` takes `Symbol`s as keys. // Be very careful changing this type signature! pub(crate) fn make_compile_codegen_unit(tcx: TyCtxt<'_>, name: Symbol) -> DepNode { - DepNode::construct(tcx, dep_kinds::CompileCodegenUnit, &name) + DepNode::construct(tcx, DepKind::CompileCodegenUnit, &name) } // WARNING: `construct` is generic and does not know that `CompileMonoItem` takes `MonoItem`s as keys. @@ -425,28 +345,16 @@ pub(crate) fn make_compile_mono_item<'tcx>( tcx: TyCtxt<'tcx>, mono_item: &MonoItem<'tcx>, ) -> DepNode { - DepNode::construct(tcx, dep_kinds::CompileMonoItem, mono_item) + DepNode::construct(tcx, DepKind::CompileMonoItem, mono_item) } // WARNING: `construct` is generic and does not know that `Metadata` takes `()`s as keys. // Be very careful changing this type signature! pub(crate) fn make_metadata(tcx: TyCtxt<'_>) -> DepNode { - DepNode::construct(tcx, dep_kinds::Metadata, &()) + DepNode::construct(tcx, DepKind::Metadata, &()) } -pub trait DepNodeExt: Sized { - fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option; - - fn from_label_string( - tcx: TyCtxt<'_>, - label: &str, - def_path_hash: DefPathHash, - ) -> Result; - - fn has_label_string(label: &str) -> bool; -} - -impl DepNodeExt for DepNode { +impl DepNode { /// Extracts the DefId corresponding to this DepNode. This will work /// if two conditions are met: /// @@ -457,33 +365,31 @@ impl DepNodeExt for DepNode { /// DepNode. Condition (2) might not be fulfilled if a DepNode /// refers to something from the previous compilation session that /// has been removed. - fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option { - if tcx.fingerprint_style(self.kind) == FingerprintStyle::DefPathHash { - tcx.def_path_hash_to_def_id(DefPathHash(self.hash.into())) + pub fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option { + if tcx.key_fingerprint_style(self.kind) == KeyFingerprintStyle::DefPathHash { + tcx.def_path_hash_to_def_id(DefPathHash(self.key_fingerprint.into())) } else { None } } - /// Used in testing - fn from_label_string( + pub fn from_label_string( tcx: TyCtxt<'_>, label: &str, def_path_hash: DefPathHash, ) -> Result { let kind = dep_kind_from_label_string(label)?; - match tcx.fingerprint_style(kind) { - FingerprintStyle::Opaque | FingerprintStyle::HirId => Err(()), - FingerprintStyle::Unit => Ok(DepNode::new_no_params(tcx, kind)), - FingerprintStyle::DefPathHash => { + match tcx.key_fingerprint_style(kind) { + KeyFingerprintStyle::Opaque | KeyFingerprintStyle::HirId => Err(()), + KeyFingerprintStyle::Unit => Ok(DepNode::new_no_params(tcx, kind)), + KeyFingerprintStyle::DefPathHash => { Ok(DepNode::from_def_path_hash(tcx, def_path_hash, kind)) } } } - /// Used in testing - fn has_label_string(label: &str) -> bool { + pub fn has_label_string(label: &str) -> bool { dep_kind_from_label_string(label).is_ok() } } diff --git a/compiler/rustc_middle/src/dep_graph/dep_node_key.rs b/compiler/rustc_middle/src/dep_graph/dep_node_key.rs index b1dfc15539bf..10e785ac6b87 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node_key.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node_key.rs @@ -1,15 +1,71 @@ +use std::fmt::Debug; + use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId, ModDefId}; use rustc_hir::definitions::DefPathHash; use rustc_hir::{HirId, ItemLocalId, OwnerId}; -use crate::dep_graph::{DepContext, DepNode, DepNodeExt, DepNodeKey, FingerprintStyle}; +use crate::dep_graph::{DepNode, KeyFingerprintStyle}; +use crate::ich::StableHashingContext; use crate::ty::TyCtxt; -impl<'tcx> DepNodeKey> for () { +/// Trait for query keys as seen by dependency-node tracking. +pub trait DepNodeKey<'tcx>: Debug + Sized { + fn key_fingerprint_style() -> KeyFingerprintStyle; + + /// This method turns a query key into an opaque `Fingerprint` to be used + /// in `DepNode`. + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint; + + fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String; + + /// This method tries to recover the query key from the given `DepNode`, + /// something which is needed when forcing `DepNode`s during red-green + /// evaluation. The query system will only call this method if + /// `fingerprint_style()` is not `FingerprintStyle::Opaque`. + /// It is always valid to return `None` here, in which case incremental + /// compilation will treat the query as having changed instead of forcing it. + fn try_recover_key(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option; +} + +// Blanket impl of `DepNodeKey`, which is specialized by other impls elsewhere. +impl<'tcx, T> DepNodeKey<'tcx> for T +where + T: for<'a> HashStable> + Debug, +{ #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - FingerprintStyle::Unit + default fn key_fingerprint_style() -> KeyFingerprintStyle { + KeyFingerprintStyle::Opaque + } + + #[inline(always)] + default fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + self.hash_stable(&mut hcx, &mut hasher); + hasher.finish() + }) + } + + #[inline(always)] + default fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { + // Make sure to print dep node params with reduced queries since printing + // may themselves call queries, which may lead to (possibly untracked!) + // query cycles. + tcx.with_reduced_queries(|| format!("{self:?}")) + } + + #[inline(always)] + default fn try_recover_key(_: TyCtxt<'tcx>, _: &DepNode) -> Option { + None + } +} + +impl<'tcx> DepNodeKey<'tcx> for () { + #[inline(always)] + fn key_fingerprint_style() -> KeyFingerprintStyle { + KeyFingerprintStyle::Unit } #[inline(always)] @@ -18,15 +74,15 @@ fn to_fingerprint(&self, _: TyCtxt<'tcx>) -> Fingerprint { } #[inline(always)] - fn recover(_: TyCtxt<'tcx>, _: &DepNode) -> Option { + fn try_recover_key(_: TyCtxt<'tcx>, _: &DepNode) -> Option { Some(()) } } -impl<'tcx> DepNodeKey> for DefId { +impl<'tcx> DepNodeKey<'tcx> for DefId { #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - FingerprintStyle::DefPathHash + fn key_fingerprint_style() -> KeyFingerprintStyle { + KeyFingerprintStyle::DefPathHash } #[inline(always)] @@ -40,15 +96,15 @@ fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { } #[inline(always)] - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + fn try_recover_key(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { dep_node.extract_def_id(tcx) } } -impl<'tcx> DepNodeKey> for LocalDefId { +impl<'tcx> DepNodeKey<'tcx> for LocalDefId { #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - FingerprintStyle::DefPathHash + fn key_fingerprint_style() -> KeyFingerprintStyle { + KeyFingerprintStyle::DefPathHash } #[inline(always)] @@ -62,15 +118,15 @@ fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { } #[inline(always)] - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + fn try_recover_key(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { dep_node.extract_def_id(tcx).map(|id| id.expect_local()) } } -impl<'tcx> DepNodeKey> for OwnerId { +impl<'tcx> DepNodeKey<'tcx> for OwnerId { #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - FingerprintStyle::DefPathHash + fn key_fingerprint_style() -> KeyFingerprintStyle { + KeyFingerprintStyle::DefPathHash } #[inline(always)] @@ -84,15 +140,15 @@ fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { } #[inline(always)] - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + fn try_recover_key(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { dep_node.extract_def_id(tcx).map(|id| OwnerId { def_id: id.expect_local() }) } } -impl<'tcx> DepNodeKey> for CrateNum { +impl<'tcx> DepNodeKey<'tcx> for CrateNum { #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - FingerprintStyle::DefPathHash + fn key_fingerprint_style() -> KeyFingerprintStyle { + KeyFingerprintStyle::DefPathHash } #[inline(always)] @@ -107,15 +163,15 @@ fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { } #[inline(always)] - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + fn try_recover_key(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { dep_node.extract_def_id(tcx).map(|id| id.krate) } } -impl<'tcx> DepNodeKey> for (DefId, DefId) { +impl<'tcx> DepNodeKey<'tcx> for (DefId, DefId) { #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - FingerprintStyle::Opaque + fn key_fingerprint_style() -> KeyFingerprintStyle { + KeyFingerprintStyle::Opaque } // We actually would not need to specialize the implementation of this @@ -139,10 +195,10 @@ fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { } } -impl<'tcx> DepNodeKey> for HirId { +impl<'tcx> DepNodeKey<'tcx> for HirId { #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - FingerprintStyle::HirId + fn key_fingerprint_style() -> KeyFingerprintStyle { + KeyFingerprintStyle::HirId } // We actually would not need to specialize the implementation of this @@ -166,9 +222,9 @@ fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { } #[inline(always)] - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - if tcx.fingerprint_style(dep_node.kind) == FingerprintStyle::HirId { - let (local_hash, local_id) = Fingerprint::from(dep_node.hash).split(); + fn try_recover_key(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + if tcx.key_fingerprint_style(dep_node.kind) == KeyFingerprintStyle::HirId { + let (local_hash, local_id) = Fingerprint::from(dep_node.key_fingerprint).split(); let def_path_hash = DefPathHash::new(tcx.stable_crate_id(LOCAL_CRATE), local_hash); let def_id = tcx.def_path_hash_to_def_id(def_path_hash)?.expect_local(); let local_id = local_id @@ -182,10 +238,10 @@ fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { } } -impl<'tcx> DepNodeKey> for ModDefId { +impl<'tcx> DepNodeKey<'tcx> for ModDefId { #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - FingerprintStyle::DefPathHash + fn key_fingerprint_style() -> KeyFingerprintStyle { + KeyFingerprintStyle::DefPathHash } #[inline(always)] @@ -199,15 +255,15 @@ fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { } #[inline(always)] - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - DefId::recover(tcx, dep_node).map(ModDefId::new_unchecked) + fn try_recover_key(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + DefId::try_recover_key(tcx, dep_node).map(ModDefId::new_unchecked) } } -impl<'tcx> DepNodeKey> for LocalModDefId { +impl<'tcx> DepNodeKey<'tcx> for LocalModDefId { #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - FingerprintStyle::DefPathHash + fn key_fingerprint_style() -> KeyFingerprintStyle { + KeyFingerprintStyle::DefPathHash } #[inline(always)] @@ -221,7 +277,7 @@ fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { } #[inline(always)] - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - LocalDefId::recover(tcx, dep_node).map(LocalModDefId::new_unchecked) + fn try_recover_key(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + LocalDefId::try_recover_key(tcx, dep_node).map(LocalModDefId::new_unchecked) } } diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index 3ef0511795b9..963d5f1a53bd 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -1,37 +1,60 @@ +use std::assert_matches; use std::fmt::Debug; use std::hash::Hash; -use std::marker::PhantomData; use std::sync::Arc; use std::sync::atomic::{AtomicU32, Ordering}; use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::outline; use rustc_data_structures::profiling::QueryInvocationId; use rustc_data_structures::sharded::{self, ShardedHashMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{AtomicU64, Lock}; +use rustc_data_structures::sync::{AtomicU64, Lock, is_dyn_thread_safe}; use rustc_data_structures::unord::UnordMap; -use rustc_data_structures::{assert_matches, outline}; use rustc_errors::DiagInner; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable}; -use rustc_query_system::ich::StableHashingContext; -use rustc_query_system::query::QuerySideEffect; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::Session; +use rustc_span::Symbol; use tracing::{debug, instrument}; #[cfg(debug_assertions)] use {super::debug::EdgeFilter, std::env}; -use super::query::DepGraphQuery; +use super::retained::RetainedDepGraph; use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex}; -use super::{DepContext, DepKind, DepNode, Deps, HasDepContext, WorkProductId}; +use super::{DepKind, DepNode, WorkProductId, read_deps, with_deps}; use crate::dep_graph::edges::EdgesVec; -use crate::query::QueryContext; +use crate::ich::StableHashingContext; +use crate::ty::TyCtxt; use crate::verify_ich::incremental_verify_ich; -pub struct DepGraph { - data: Option>>, +/// Tracks 'side effects' for a particular query. +/// This struct is saved to disk along with the query result, +/// and loaded from disk if we mark the query as green. +/// This allows us to 'replay' changes to global state +/// that would otherwise only occur if we actually +/// executed the query method. +/// +/// Each side effect gets an unique dep node index which is added +/// as a dependency of the query which had the effect. +#[derive(Debug, Encodable, Decodable)] +pub enum QuerySideEffect { + /// Stores a diagnostic emitted during query execution. + /// This diagnostic will be re-emitted if we mark + /// the query as green, as that query will have the side + /// effect dep node as a dependency. + Diagnostic(DiagInner), + /// Records the feature used during query execution. + /// This feature will be inserted into `sess.used_features` + /// if we mark the query as green, as that query will have + /// the side effect dep node as a dependency. + CheckFeature { symbol: Symbol }, +} +#[derive(Clone)] +pub struct DepGraph { + data: Option>, /// This field is used for assigning DepNodeIndices when running in /// non-incremental mode. Even in non-incremental mode we make sure that @@ -40,17 +63,6 @@ pub struct DepGraph { virtual_dep_node_index: Arc, } -/// Manual clone impl that does not require `D: Clone`. -impl Clone for DepGraph { - fn clone(&self) -> Self { - let Self { data, virtual_dep_node_index } = self; - Self { - data: Option::>::clone(data), - virtual_dep_node_index: Arc::clone(virtual_dep_node_index), - } - } -} - rustc_index::newtype_index! { pub struct DepNodeIndex {} } @@ -72,7 +84,7 @@ fn from(dep_node_index: DepNodeIndex) -> Self { } } -pub struct MarkFrame<'a> { +pub(crate) struct MarkFrame<'a> { index: SerializedDepNodeIndex, parent: Option<&'a MarkFrame<'a>>, } @@ -84,12 +96,12 @@ pub(super) enum DepNodeColor { Unknown, } -pub struct DepGraphData { +pub struct DepGraphData { /// The new encoding of the dependency graph, optimized for red/green /// tracking. The `current` field is the dependency graph of only the /// current compilation session: We don't merge the previous dep-graph into /// current one anymore, but we do reference shared data to save space. - current: CurrentDepGraph, + current: CurrentDepGraph, /// The dep-graph from the previous compilation session. It contains all /// nodes and edges as well as all fingerprints of nodes that have them. @@ -120,13 +132,13 @@ pub fn hash_result(hcx: &mut StableHashingContext<'_>, result: &R) -> Fingerp stable_hasher.finish() } -impl DepGraph { +impl DepGraph { pub fn new( session: &Session, prev_graph: Arc, prev_work_products: WorkProductMap, encoder: FileEncoder, - ) -> DepGraph { + ) -> DepGraph { let prev_graph_node_count = prev_graph.node_count(); let current = @@ -136,15 +148,17 @@ pub fn new( // Instantiate a node with zero dependencies only once for anonymous queries. let _green_node_index = current.alloc_new_node( - DepNode { kind: D::DEP_KIND_ANON_ZERO_DEPS, hash: current.anon_id_seed.into() }, + DepNode { kind: DepKind::AnonZeroDeps, key_fingerprint: current.anon_id_seed.into() }, EdgesVec::new(), Fingerprint::ZERO, ); assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_ZERO_DEPS_ANON_NODE); - // Instantiate a dependy-less red node only once for anonymous queries. + // Create a single always-red node, with no dependencies of its own. + // Other nodes can use the always-red node as a fake dependency, to + // ensure that their dependency list will never be all-green. let red_node_index = current.alloc_new_node( - DepNode { kind: D::DEP_KIND_RED, hash: Fingerprint::ZERO.into() }, + DepNode { kind: DepKind::Red, key_fingerprint: Fingerprint::ZERO.into() }, EdgesVec::new(), Fingerprint::ZERO, ); @@ -168,12 +182,12 @@ pub fn new( } } - pub fn new_disabled() -> DepGraph { + pub fn new_disabled() -> DepGraph { DepGraph { data: None, virtual_dep_node_index: Arc::new(AtomicU32::new(0)) } } #[inline] - pub fn data(&self) -> Option<&DepGraphData> { + pub fn data(&self) -> Option<&DepGraphData> { self.data.as_deref() } @@ -183,15 +197,15 @@ pub fn is_fully_enabled(&self) -> bool { self.data.is_some() } - pub fn with_query(&self, f: impl Fn(&DepGraphQuery)) { + pub fn with_retained_dep_graph(&self, f: impl Fn(&RetainedDepGraph)) { if let Some(data) = &self.data { - data.current.encoder.with_query(f) + data.current.encoder.with_retained_dep_graph(f) } } pub fn assert_ignored(&self) { if let Some(..) = self.data { - D::read_deps(|task_deps| { + read_deps(|task_deps| { assert_matches!( task_deps, TaskDepsRef::Ignore, @@ -205,7 +219,7 @@ pub fn with_ignore(&self, op: OP) -> R where OP: FnOnce() -> R, { - D::with_deps(TaskDepsRef::Ignore, op) + with_deps(TaskDepsRef::Ignore, op) } /// Used to wrap the deserialization of a query result from disk, @@ -258,27 +272,27 @@ pub fn with_query_deserialization(&self, op: OP) -> R where OP: FnOnce() -> R, { - D::with_deps(TaskDepsRef::Forbid, op) + with_deps(TaskDepsRef::Forbid, op) } #[inline(always)] - pub fn with_task, A: Debug, R>( + pub fn with_task<'tcx, A: Debug, R>( &self, - key: DepNode, - cx: Ctxt, - arg: A, - task: fn(Ctxt, A) -> R, + dep_node: DepNode, + tcx: TyCtxt<'tcx>, + task_arg: A, + task_fn: fn(tcx: TyCtxt<'tcx>, task_arg: A) -> R, hash_result: Option, &R) -> Fingerprint>, ) -> (R, DepNodeIndex) { match self.data() { - Some(data) => data.with_task(key, cx, arg, task, hash_result), - None => (task(cx, arg), self.next_virtual_depnode_index()), + Some(data) => data.with_task(dep_node, tcx, task_arg, task_fn, hash_result), + None => (task_fn(tcx, task_arg), self.next_virtual_depnode_index()), } } - pub fn with_anon_task, OP, R>( + pub fn with_anon_task<'tcx, OP, R>( &self, - cx: Tcx, + cx: TyCtxt<'tcx>, dep_kind: DepKind, op: OP, ) -> (R, DepNodeIndex) @@ -296,7 +310,7 @@ pub fn with_anon_task, OP, R>( } } -impl DepGraphData { +impl DepGraphData { /// Starts a new dep-graph task. Dep-graph tasks are specified /// using a free function (`task`) and **not** a closure -- this /// is intentional because we want to exercise tight control over @@ -304,33 +318,21 @@ impl DepGraphData { /// prevent implicit 'leaks' of tracked state into the task (which /// could then be read without generating correct edges in the /// dep-graph -- see the [rustc dev guide] for more details on - /// the dep-graph). To this end, the task function gets exactly two - /// pieces of state: the context `cx` and an argument `arg`. Both - /// of these bits of state must be of some type that implements - /// `DepGraphSafe` and hence does not leak. + /// the dep-graph). /// - /// The choice of two arguments is not fundamental. One argument - /// would work just as well, since multiple values can be - /// collected using tuples. However, using two arguments works out - /// to be quite convenient, since it is common to need a context - /// (`cx`) and some argument (e.g., a `DefId` identifying what - /// item to process). - /// - /// For cases where you need some other number of arguments: - /// - /// - If you only need one argument, just use `()` for the `arg` - /// parameter. - /// - If you need 3+ arguments, use a tuple for the - /// `arg` parameter. + /// Therefore, the task function takes a `TyCtxt`, plus exactly one + /// additional argument, `task_arg`. The additional argument type can be + /// `()` if no argument is needed, or a tuple if multiple arguments are + /// needed. /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html #[inline(always)] - pub fn with_task, A: Debug, R>( + pub fn with_task<'tcx, A: Debug, R>( &self, - key: DepNode, - cx: Ctxt, - arg: A, - task: fn(Ctxt, A) -> R, + dep_node: DepNode, + tcx: TyCtxt<'tcx>, + task_arg: A, + task_fn: fn(tcx: TyCtxt<'tcx>, task_arg: A) -> R, hash_result: Option, &R) -> Fingerprint>, ) -> (R, DepNodeIndex) { // If the following assertion triggers, it can have two reasons: @@ -338,32 +340,28 @@ pub fn with_task, A: Debug, R>( // in `DepGraph::try_mark_green()`. // 2. Two distinct query keys get mapped to the same `DepNode` // (see for example #48923). - self.assert_dep_node_not_yet_allocated_in_current_session( - cx.dep_context().sess(), - &key, - || { - format!( - "forcing query with already existing `DepNode`\n\ - - query-key: {arg:?}\n\ - - dep-node: {key:?}" - ) - }, - ); + self.assert_dep_node_not_yet_allocated_in_current_session(tcx.sess, &dep_node, || { + format!( + "forcing query with already existing `DepNode`\n\ + - query-key: {task_arg:?}\n\ + - dep-node: {dep_node:?}" + ) + }); - let with_deps = |task_deps| D::with_deps(task_deps, || task(cx, arg)); - let (result, edges) = if cx.dep_context().is_eval_always(key.kind) { + let with_deps = |task_deps| with_deps(task_deps, || task_fn(tcx, task_arg)); + let (result, edges) = if tcx.is_eval_always(dep_node.kind) { (with_deps(TaskDepsRef::EvalAlways), EdgesVec::new()) } else { let task_deps = Lock::new(TaskDeps::new( #[cfg(debug_assertions)] - Some(key), + Some(dep_node), 0, )); (with_deps(TaskDepsRef::Allow(&task_deps)), task_deps.into_inner().reads) }; - let dcx = cx.dep_context(); - let dep_node_index = self.hash_result_and_alloc_node(dcx, key, edges, &result, hash_result); + let dep_node_index = + self.hash_result_and_alloc_node(tcx, dep_node, edges, &result, hash_result); (result, dep_node_index) } @@ -379,9 +377,9 @@ pub fn with_task, A: Debug, R>( /// FIXME: This could perhaps return a `WithDepNode` to ensure that the /// user of this function actually performs the read; we'll have to see /// how to make that work with `anon` in `execute_job_incr`, though. - pub fn with_anon_task_inner, OP, R>( + pub fn with_anon_task_inner<'tcx, OP, R>( &self, - cx: Tcx, + cx: TyCtxt<'tcx>, dep_kind: DepKind, op: OP, ) -> (R, DepNodeIndex) @@ -397,7 +395,7 @@ pub fn with_anon_task_inner, OP, R>( None, 128, )); - let result = D::with_deps(TaskDepsRef::Allow(&task_deps), op); + let result = with_deps(TaskDepsRef::Allow(&task_deps), op); let task_deps = task_deps.into_inner(); let reads = task_deps.reads; @@ -428,7 +426,7 @@ pub fn with_anon_task_inner, OP, R>( // Fingerprint::combine() is faster than sending Fingerprint // through the StableHasher (at least as long as StableHasher // is so slow). - hash: self.current.anon_id_seed.combine(hasher.finish()).into(), + key_fingerprint: self.current.anon_id_seed.combine(hasher.finish()).into(), }; // The DepNodes generated by the process above are not unique. 2 queries could @@ -448,17 +446,17 @@ pub fn with_anon_task_inner, OP, R>( } /// Intern the new `DepNode` with the dependencies up-to-now. - fn hash_result_and_alloc_node, R>( + fn hash_result_and_alloc_node<'tcx, R>( &self, - cx: &Ctxt, + tcx: TyCtxt<'tcx>, node: DepNode, edges: EdgesVec, result: &R, hash_result: Option, &R) -> Fingerprint>, ) -> DepNodeIndex { - let hashing_timer = cx.profiler().incr_result_hashing(); + let hashing_timer = tcx.prof.incr_result_hashing(); let current_fingerprint = hash_result.map(|hash_result| { - cx.with_stable_hashing_context(|mut hcx| hash_result(&mut hcx, result)) + tcx.with_stable_hashing_context(|mut hcx| hash_result(&mut hcx, result)) }); let dep_node_index = self.alloc_and_color_node(node, edges, current_fingerprint); hashing_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -466,11 +464,11 @@ fn hash_result_and_alloc_node, R>( } } -impl DepGraph { +impl DepGraph { #[inline] pub fn read_index(&self, dep_node_index: DepNodeIndex) { if let Some(ref data) = self.data { - D::read_deps(|task_deps| { + read_deps(|task_deps| { let mut task_deps = match task_deps { TaskDepsRef::Allow(deps) => deps.lock(), TaskDepsRef::EvalAlways => { @@ -522,33 +520,40 @@ pub fn read_index(&self, dep_node_index: DepNodeIndex) { } } - /// This encodes a diagnostic by creating a node with an unique index and associating - /// `diagnostic` with it, for use in the next session. + /// This encodes a side effect by creating a node with an unique index and associating + /// it with the node, for use in the next session. #[inline] - pub fn record_diagnostic<'tcx, Qcx: QueryContext<'tcx>>( - &self, - qcx: Qcx, - diagnostic: &DiagInner, - ) { + pub fn record_diagnostic<'tcx>(&self, tcx: TyCtxt<'tcx>, diagnostic: &DiagInner) { if let Some(ref data) = self.data { - D::read_deps(|task_deps| match task_deps { + read_deps(|task_deps| match task_deps { TaskDepsRef::EvalAlways | TaskDepsRef::Ignore => return, TaskDepsRef::Forbid | TaskDepsRef::Allow(..) => { - self.read_index(data.encode_diagnostic(qcx, diagnostic)); + let dep_node_index = data + .encode_side_effect(tcx, QuerySideEffect::Diagnostic(diagnostic.clone())); + self.read_index(dep_node_index); } }) } } - /// This forces a diagnostic node green by running its side effect. `prev_index` would - /// refer to a node created used `encode_diagnostic` in the previous session. + /// This forces a side effect node green by running its side effect. `prev_index` would + /// refer to a node created used `encode_side_effect` in the previous session. #[inline] - pub fn force_diagnostic_node<'tcx, Qcx: QueryContext<'tcx>>( - &self, - qcx: Qcx, - prev_index: SerializedDepNodeIndex, - ) { + pub fn force_side_effect<'tcx>(&self, tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex) { if let Some(ref data) = self.data { - data.force_diagnostic_node(qcx, prev_index); + data.force_side_effect(tcx, prev_index); + } + } + + #[inline] + pub fn encode_side_effect<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + side_effect: QuerySideEffect, + ) -> DepNodeIndex { + if let Some(ref data) = self.data { + data.encode_side_effect(tcx, side_effect) + } else { + self.next_virtual_depnode_index() } } @@ -567,10 +572,10 @@ pub fn force_diagnostic_node<'tcx, Qcx: QueryContext<'tcx>>( /// FIXME: If the code is changed enough for this node to be marked before requiring the /// caller's node, we suppose that those changes will be enough to mark this node red and /// force a recomputation using the "normal" way. - pub fn with_feed_task, R>( + pub fn with_feed_task<'tcx, R>( &self, node: DepNode, - cx: Ctxt, + tcx: TyCtxt<'tcx>, result: &R, hash_result: Option, &R) -> Fingerprint>, format_value_fn: fn(&R) -> String, @@ -586,7 +591,7 @@ pub fn with_feed_task, R>( let dep_node_index = data.colors.current(prev_index); if let Some(dep_node_index) = dep_node_index { incremental_verify_ich( - cx, + tcx, data, result, prev_index, @@ -599,7 +604,7 @@ pub fn with_feed_task, R>( data.current.record_edge( dep_node_index, node, - data.prev_fingerprint_of(prev_index), + data.prev_value_fingerprint_of(prev_index), ); } @@ -608,7 +613,7 @@ pub fn with_feed_task, R>( } let mut edges = EdgesVec::new(); - D::read_deps(|task_deps| match task_deps { + read_deps(|task_deps| match task_deps { TaskDepsRef::Allow(deps) => edges.extend(deps.lock().reads.iter().copied()), TaskDepsRef::EvalAlways => { edges.push(DepNodeIndex::FOREVER_RED_NODE); @@ -619,7 +624,7 @@ pub fn with_feed_task, R>( } }); - data.hash_result_and_alloc_node(&cx, node, edges, result, hash_result) + data.hash_result_and_alloc_node(tcx, node, edges, result, hash_result) } else { // Incremental compilation is turned off. We just execute the task // without tracking. We still provide a dep-node index that uniquely @@ -630,7 +635,7 @@ pub fn with_feed_task, R>( } } -impl DepGraphData { +impl DepGraphData { fn assert_dep_node_not_yet_allocated_in_current_session( &self, sess: &Session, @@ -672,8 +677,8 @@ pub fn is_index_green(&self, prev_index: SerializedDepNodeIndex) -> bool { } #[inline] - pub fn prev_fingerprint_of(&self, prev_index: SerializedDepNodeIndex) -> Fingerprint { - self.previous.fingerprint_by_index(prev_index) + pub fn prev_value_fingerprint_of(&self, prev_index: SerializedDepNodeIndex) -> Fingerprint { + self.previous.value_fingerprint_for_index(prev_index) } #[inline] @@ -685,46 +690,35 @@ pub fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) { self.debug_loaded_from_disk.lock().insert(dep_node); } - /// This encodes a diagnostic by creating a node with an unique index and associating - /// `diagnostic` with it, for use in the next session. + /// This encodes a side effect by creating a node with an unique index and associating + /// it with the node, for use in the next session. #[inline] - fn encode_diagnostic<'tcx, Qcx: QueryContext<'tcx>>( + fn encode_side_effect<'tcx>( &self, - qcx: Qcx, - diagnostic: &DiagInner, + tcx: TyCtxt<'tcx>, + side_effect: QuerySideEffect, ) -> DepNodeIndex { // Use `send_new` so we get an unique index, even though the dep node is not. let dep_node_index = self.current.encoder.send_new( DepNode { - kind: D::DEP_KIND_SIDE_EFFECT, - hash: PackedFingerprint::from(Fingerprint::ZERO), + kind: DepKind::SideEffect, + key_fingerprint: PackedFingerprint::from(Fingerprint::ZERO), }, Fingerprint::ZERO, - // We want the side effect node to always be red so it will be forced and emit the - // diagnostic. + // We want the side effect node to always be red so it will be forced and run the + // side effect. std::iter::once(DepNodeIndex::FOREVER_RED_NODE).collect(), ); - let side_effect = QuerySideEffect::Diagnostic(diagnostic.clone()); - qcx.store_side_effect(dep_node_index, side_effect); + tcx.store_side_effect(dep_node_index, side_effect); dep_node_index } - /// This forces a diagnostic node green by running its side effect. `prev_index` would - /// refer to a node created used `encode_diagnostic` in the previous session. + /// This forces a side effect node green by running its side effect. `prev_index` would + /// refer to a node created used `encode_side_effect` in the previous session. #[inline] - fn force_diagnostic_node<'tcx, Qcx: QueryContext<'tcx>>( - &self, - qcx: Qcx, - prev_index: SerializedDepNodeIndex, - ) { - D::with_deps(TaskDepsRef::Ignore, || { - let side_effect = qcx.load_side_effect(prev_index).unwrap(); - - match &side_effect { - QuerySideEffect::Diagnostic(diagnostic) => { - qcx.dep_context().sess().dcx().emit_diagnostic(diagnostic.clone()); - } - } + fn force_side_effect<'tcx>(&self, tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex) { + with_deps(TaskDepsRef::Ignore, || { + let side_effect = tcx.load_side_effect(prev_index).unwrap(); // Use `send_and_color` as `promote_node_and_deps_to_current` expects all // green dependencies. `send_and_color` will also prevent multiple nodes @@ -733,15 +727,25 @@ fn force_diagnostic_node<'tcx, Qcx: QueryContext<'tcx>>( prev_index, &self.colors, DepNode { - kind: D::DEP_KIND_SIDE_EFFECT, - hash: PackedFingerprint::from(Fingerprint::ZERO), + kind: DepKind::SideEffect, + key_fingerprint: PackedFingerprint::from(Fingerprint::ZERO), }, Fingerprint::ZERO, std::iter::once(DepNodeIndex::FOREVER_RED_NODE).collect(), true, ); + + match &side_effect { + QuerySideEffect::Diagnostic(diagnostic) => { + tcx.dcx().emit_diagnostic(diagnostic.clone()); + } + QuerySideEffect::CheckFeature { symbol } => { + tcx.sess.used_features.lock().insert(*symbol, dep_node_index.as_u32()); + } + } + // This will just overwrite the same value for concurrent calls. - qcx.store_side_effect(dep_node_index, side_effect); + tcx.store_side_effect(dep_node_index, side_effect); }) } @@ -749,12 +753,12 @@ fn alloc_and_color_node( &self, key: DepNode, edges: EdgesVec, - fingerprint: Option, + value_fingerprint: Option, ) -> DepNodeIndex { if let Some(prev_index) = self.previous.node_to_index_opt(&key) { // Determine the color and index of the new `DepNode`. - let is_green = if let Some(fingerprint) = fingerprint { - if fingerprint == self.previous.fingerprint_by_index(prev_index) { + let is_green = if let Some(value_fingerprint) = value_fingerprint { + if value_fingerprint == self.previous.value_fingerprint_for_index(prev_index) { // This is a green node: it existed in the previous compilation, // its query was re-executed, and it has the same result as before. true @@ -771,22 +775,22 @@ fn alloc_and_color_node( false }; - let fingerprint = fingerprint.unwrap_or(Fingerprint::ZERO); + let value_fingerprint = value_fingerprint.unwrap_or(Fingerprint::ZERO); let dep_node_index = self.current.encoder.send_and_color( prev_index, &self.colors, key, - fingerprint, + value_fingerprint, edges, is_green, ); - self.current.record_node(dep_node_index, key, fingerprint); + self.current.record_node(dep_node_index, key, value_fingerprint); dep_node_index } else { - self.current.alloc_new_node(key, edges, fingerprint.unwrap_or(Fingerprint::ZERO)) + self.current.alloc_new_node(key, edges, value_fingerprint.unwrap_or(Fingerprint::ZERO)) } } @@ -803,7 +807,7 @@ fn promote_node_and_deps_to_current( self.current.record_edge( dep_node_index, *self.previous.index_to_node(prev_index), - self.previous.fingerprint_by_index(prev_index), + self.previous.value_fingerprint_for_index(prev_index), ); } @@ -811,7 +815,7 @@ fn promote_node_and_deps_to_current( } } -impl DepGraph { +impl DepGraph { /// Checks whether a previous work product exists for `v` and, if /// so, return the path that leads to it. Used to skip doing work. pub fn previous_work_product(&self, v: &WorkProductId) -> Option { @@ -846,7 +850,13 @@ pub(crate) fn register_dep_node_debug_str(&self, dep_node: DepNode, debug_str where F: FnOnce() -> String, { - let dep_node_debug = &self.data.as_ref().unwrap().dep_node_debug; + // Early queries (e.g., `-Z query-dep-graph` on empty crates) can reach here + // before the graph is initialized. Return early to prevent an ICE. + let data = match &self.data { + Some(d) => d, + None => return, + }; + let dep_node_debug = &data.dep_node_debug; if dep_node_debug.borrow().contains_key(&dep_node) { return; @@ -867,27 +877,27 @@ fn node_color(&self, dep_node: &DepNode) -> DepNodeColor { DepNodeColor::Unknown } - pub fn try_mark_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( + pub fn try_mark_green<'tcx>( &self, - qcx: Qcx, + tcx: TyCtxt<'tcx>, dep_node: &DepNode, ) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> { - self.data().and_then(|data| data.try_mark_green(qcx, dep_node)) + self.data()?.try_mark_green(tcx, dep_node) } } -impl DepGraphData { +impl DepGraphData { /// Try to mark a node index for the node dep_node. /// /// A node will have an index, when it's already been marked green, or when we can mark it /// green. This function will mark the current task as a reader of the specified node, when /// a node index can be found for that node. - pub fn try_mark_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( + pub fn try_mark_green<'tcx>( &self, - qcx: Qcx, + tcx: TyCtxt<'tcx>, dep_node: &DepNode, ) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> { - debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind)); + debug_assert!(!tcx.is_eval_always(dep_node.kind)); // Return None if the dep node didn't exist in the previous session let prev_index = self.previous.node_to_index_opt(dep_node)?; @@ -902,16 +912,16 @@ pub fn try_mark_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( // in the previous compilation session too, so we can try to // mark it as green by recursively marking all of its // dependencies green. - self.try_mark_previous_green(qcx, prev_index, None) + self.try_mark_previous_green(tcx, prev_index, None) .map(|dep_node_index| (prev_index, dep_node_index)) } } } - #[instrument(skip(self, qcx, parent_dep_node_index, frame), level = "debug")] - fn try_mark_parent_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( + #[instrument(skip(self, tcx, parent_dep_node_index, frame), level = "debug")] + fn try_mark_parent_green<'tcx>( &self, - qcx: Qcx, + tcx: TyCtxt<'tcx>, parent_dep_node_index: SerializedDepNodeIndex, frame: &MarkFrame<'_>, ) -> Option<()> { @@ -944,13 +954,13 @@ fn try_mark_parent_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( // We don't know the state of this dependency. If it isn't // an eval_always node, let's try to mark it green recursively. - if !qcx.dep_context().is_eval_always(dep_dep_node.kind) { + if !tcx.is_eval_always(dep_dep_node.kind) { debug!( "state of dependency {:?} ({}) is unknown, trying to mark it green", - dep_dep_node, dep_dep_node.hash, + dep_dep_node, dep_dep_node.key_fingerprint, ); - let node_index = self.try_mark_previous_green(qcx, parent_dep_node_index, Some(frame)); + let node_index = self.try_mark_previous_green(tcx, parent_dep_node_index, Some(frame)); if node_index.is_some() { debug!("managed to MARK dependency {dep_dep_node:?} as green"); @@ -960,7 +970,7 @@ fn try_mark_parent_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( // We failed to mark it green, so we try to force the query. debug!("trying to force dependency {dep_dep_node:?}"); - if !qcx.dep_context().try_force_from_dep_node(*dep_dep_node, parent_dep_node_index, frame) { + if !tcx.try_force_from_dep_node(*dep_dep_node, parent_dep_node_index, frame) { // The DepNode could not be forced. debug!("dependency {dep_dep_node:?} could not be forced"); return None; @@ -978,7 +988,7 @@ fn try_mark_parent_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( DepNodeColor::Unknown => {} } - if let None = qcx.dep_context().sess().dcx().has_errors_or_delayed_bugs() { + if let None = tcx.dcx().has_errors_or_delayed_bugs() { panic!("try_mark_previous_green() - Forcing the DepNode should have set its color") } @@ -997,25 +1007,22 @@ fn try_mark_parent_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( } /// Try to mark a dep-node which existed in the previous compilation session as green. - #[instrument(skip(self, qcx, prev_dep_node_index, frame), level = "debug")] - fn try_mark_previous_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( + #[instrument(skip(self, tcx, prev_dep_node_index, frame), level = "debug")] + fn try_mark_previous_green<'tcx>( &self, - qcx: Qcx, + tcx: TyCtxt<'tcx>, prev_dep_node_index: SerializedDepNodeIndex, frame: Option<&MarkFrame<'_>>, ) -> Option { let frame = MarkFrame { index: prev_dep_node_index, parent: frame }; // We never try to mark eval_always nodes as green - debug_assert!( - !qcx.dep_context() - .is_eval_always(self.previous.index_to_node(prev_dep_node_index).kind) - ); + debug_assert!(!tcx.is_eval_always(self.previous.index_to_node(prev_dep_node_index).kind)); let prev_deps = self.previous.edge_targets_from(prev_dep_node_index); for dep_dep_node_index in prev_deps { - self.try_mark_parent_green(qcx, dep_dep_node_index, &frame)?; + self.try_mark_parent_green(tcx, dep_dep_node_index, &frame)?; } // If we got here without hitting a `return` that means that all @@ -1041,7 +1048,7 @@ fn try_mark_previous_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( } } -impl DepGraph { +impl DepGraph { /// Returns true if the given node has been marked as red during the /// current compilation session. Used in various assertions pub fn is_red(&self, dep_node: &DepNode) -> bool { @@ -1073,15 +1080,19 @@ pub fn assert_dep_node_not_yet_allocated_in_current_session(&self, tcx: Tcx) { - let _prof_timer = tcx.profiler().generic_activity("incr_comp_query_cache_promotion"); + pub fn exec_cache_promotions<'tcx>(&self, tcx: TyCtxt<'tcx>) { + let _prof_timer = tcx.prof.generic_activity("incr_comp_query_cache_promotion"); let data = self.data.as_ref().unwrap(); for prev_index in data.colors.values.indices() { match data.colors.get(prev_index) { DepNodeColor::Green(_) => { let dep_node = data.previous.index_to_node(prev_index); - tcx.try_load_from_on_disk_cache(dep_node); + if let Some(promote_fn) = + tcx.dep_kind_vtable(dep_node.kind).promote_from_disk_fn + { + promote_fn(tcx, *dep_node) + }; } DepNodeColor::Unknown | DepNodeColor::Red => { // We can skip red nodes because a node can only be marked @@ -1175,14 +1186,14 @@ struct EdgeIndex {} /// `anon_node_to_index` and `data`, or `prev_index_to_index` and `data`. When /// manipulating both, we acquire `anon_node_to_index` or `prev_index_to_index` /// first, and `data` second. -pub(super) struct CurrentDepGraph { - encoder: GraphEncoder, +pub(super) struct CurrentDepGraph { + encoder: GraphEncoder, anon_node_to_index: ShardedHashMap, - /// This is used to verify that fingerprints do not change between the creation of a node - /// and its recomputation. + /// This is used to verify that value fingerprints do not change between the + /// creation of a node and its recomputation. #[cfg(debug_assertions)] - fingerprints: Lock>>, + value_fingerprints: Lock>>, /// Used to trap when a specific edge is added to the graph. /// This is used for debug purposes and is only active with `debug_assertions`. @@ -1214,7 +1225,7 @@ pub(super) struct CurrentDepGraph { pub(super) total_duplicate_read_count: AtomicU64, } -impl CurrentDepGraph { +impl CurrentDepGraph { fn new( session: &Session, prev_graph_node_count: usize, @@ -1249,7 +1260,7 @@ fn new( #[cfg(debug_assertions)] forbidden_edge, #[cfg(debug_assertions)] - fingerprints: Lock::new(IndexVec::from_elem_n(None, new_node_count_estimate)), + value_fingerprints: Lock::new(IndexVec::from_elem_n(None, new_node_count_estimate)), nodes_in_current_session: new_node_dbg.then(|| { Lock::new(FxHashMap::with_capacity_and_hasher( new_node_count_estimate, @@ -1262,12 +1273,20 @@ fn new( } #[cfg(debug_assertions)] - fn record_edge(&self, dep_node_index: DepNodeIndex, key: DepNode, fingerprint: Fingerprint) { + fn record_edge( + &self, + dep_node_index: DepNodeIndex, + key: DepNode, + value_fingerprint: Fingerprint, + ) { if let Some(forbidden_edge) = &self.forbidden_edge { forbidden_edge.index_to_node.lock().insert(dep_node_index, key); } - let previous = *self.fingerprints.lock().get_or_insert_with(dep_node_index, || fingerprint); - assert_eq!(previous, fingerprint, "Unstable fingerprints for {:?}", key); + let prior_value_fingerprint = *self + .value_fingerprints + .lock() + .get_or_insert_with(dep_node_index, || value_fingerprint); + assert_eq!(prior_value_fingerprint, value_fingerprint, "Unstable fingerprints for {key:?}"); } #[inline(always)] @@ -1275,10 +1294,10 @@ fn record_node( &self, dep_node_index: DepNodeIndex, key: DepNode, - _current_fingerprint: Fingerprint, + _value_fingerprint: Fingerprint, ) { #[cfg(debug_assertions)] - self.record_edge(dep_node_index, key, _current_fingerprint); + self.record_edge(dep_node_index, key, _value_fingerprint); if let Some(ref nodes_in_current_session) = self.nodes_in_current_session { outline(|| { @@ -1296,11 +1315,11 @@ fn alloc_new_node( &self, key: DepNode, edges: EdgesVec, - current_fingerprint: Fingerprint, + value_fingerprint: Fingerprint, ) -> DepNodeIndex { - let dep_node_index = self.encoder.send_new(key, current_fingerprint, edges); + let dep_node_index = self.encoder.send_new(key, value_fingerprint, edges); - self.record_node(dep_node_index, key, current_fingerprint); + self.record_node(dep_node_index, key, value_fingerprint); dep_node_index } @@ -1311,7 +1330,9 @@ fn debug_assert_not_in_new_nodes( prev_graph: &SerializedDepGraph, prev_index: SerializedDepNodeIndex, ) { - if let Some(ref nodes_in_current_session) = self.nodes_in_current_session { + if !is_dyn_thread_safe() + && let Some(ref nodes_in_current_session) = self.nodes_in_current_session + { debug_assert!( !nodes_in_current_session .lock() @@ -1356,8 +1377,6 @@ pub struct TaskDeps { /// scan. If the number is higher, a hashset has better perf. This field is that hashset. It's /// only used if the number of elements in `reads` exceeds `LINEAR_SCAN_MAX`. read_set: FxHashSet, - - phantom_data: PhantomData, } impl TaskDeps { @@ -1371,7 +1390,6 @@ fn new(#[cfg(debug_assertions)] node: Option, read_set_capacity: usize) node, reads: EdgesVec::new(), read_set: FxHashSet::with_capacity_and_hasher(read_set_capacity, Default::default()), - phantom_data: PhantomData, } } } @@ -1448,7 +1466,7 @@ pub(super) fn insert_red(&self, index: SerializedDepNodeIndex) { #[inline(never)] #[cold] -pub(crate) fn print_markframe_trace(graph: &DepGraph, frame: &MarkFrame<'_>) { +pub(crate) fn print_markframe_trace(graph: &DepGraph, frame: &MarkFrame<'_>) { let data = graph.data.as_ref().unwrap(); eprintln!("there was a panic while trying to force a dep node"); @@ -1468,7 +1486,7 @@ pub(crate) fn print_markframe_trace(graph: &DepGraph, frame: &MarkFr #[cold] #[inline(never)] -fn panic_on_forbidden_read(data: &DepGraphData, dep_node_index: DepNodeIndex) -> ! { +fn panic_on_forbidden_read(data: &DepGraphData, dep_node_index: DepNodeIndex) -> ! { // We have to do an expensive reverse-lookup of the DepNode that // corresponds to `dep_node_index`, but that's OK since we are about // to ICE anyway. @@ -1508,3 +1526,30 @@ fn panic_on_forbidden_read(data: &DepGraphData, dep_node_index: DepN See ." ) } + +impl<'tcx> TyCtxt<'tcx> { + /// Return whether this kind always require evaluation. + #[inline(always)] + fn is_eval_always(self, kind: DepKind) -> bool { + self.dep_kind_vtable(kind).is_eval_always + } + + // Interactions with on_disk_cache + fn load_side_effect( + self, + prev_dep_node_index: SerializedDepNodeIndex, + ) -> Option { + self.query_system + .on_disk_cache + .as_ref() + .and_then(|c| c.load_side_effect(self, prev_dep_node_index)) + } + + #[inline(never)] + #[cold] + fn store_side_effect(self, dep_node_index: DepNodeIndex, side_effect: QuerySideEffect) { + if let Some(c) = self.query_system.on_disk_cache.as_ref() { + c.store_side_effect(dep_node_index, side_effect) + } + } +} diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 723eed21f211..9787a998d637 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -1,59 +1,97 @@ use std::panic; -use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::sync::DynSync; -use rustc_query_system::ich::StableHashingContext; -use rustc_session::Session; use tracing::instrument; pub use self::dep_node::{ - DepKind, DepNode, DepNodeExt, DepNodeKey, WorkProductId, dep_kind_from_label, dep_kinds, - label_strs, + DepKind, DepKindVTable, DepNode, WorkProductId, dep_kind_from_label, label_strs, }; +pub use self::dep_node_key::DepNodeKey; pub use self::graph::{ - DepGraphData, DepNodeIndex, TaskDepsRef, WorkProduct, WorkProductMap, hash_result, + DepGraph, DepGraphData, DepNodeIndex, QuerySideEffect, TaskDepsRef, WorkProduct, + WorkProductMap, hash_result, }; use self::graph::{MarkFrame, print_markframe_trace}; -pub use self::query::DepGraphQuery; +pub use self::retained::RetainedDepGraph; pub use self::serialized::{SerializedDepGraph, SerializedDepNodeIndex}; pub use crate::dep_graph::debug::{DepNodeFilter, EdgeFilter}; use crate::ty::print::with_reduced_queries; use crate::ty::{self, TyCtxt}; mod debug; -pub mod dep_node; +pub(crate) mod dep_node; mod dep_node_key; mod edges; mod graph; -mod query; +mod retained; mod serialized; -pub trait DepContext: Copy { - type Deps: Deps; +/// Describes the contents of the fingerprint generated by a given query. +/// +/// This is mainly for determining whether and how we can reconstruct a key +/// from the fingerprint. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum KeyFingerprintStyle { + /// The fingerprint is actually a DefPathHash. + DefPathHash, + /// The fingerprint is actually a HirId. + HirId, + /// Query key was `()` or equivalent, so fingerprint is just zero. + Unit, + /// The fingerprint is an opaque hash, and a key cannot be reconstructed from it. + Opaque, +} - /// Create a hashing context for hashing new results. - fn with_stable_hashing_context(self, f: impl FnOnce(StableHashingContext<'_>) -> R) -> R; +impl KeyFingerprintStyle { + /// True if a key can _potentially_ be recovered from a key fingerprint + /// with this style. + /// + /// For some key types, recovery is possible but not guaranteed. + #[inline] + pub const fn is_maybe_recoverable(self) -> bool { + match self { + KeyFingerprintStyle::DefPathHash + | KeyFingerprintStyle::Unit + | KeyFingerprintStyle::HirId => true, + KeyFingerprintStyle::Opaque => false, + } + } +} - /// Access the DepGraph. - fn dep_graph(&self) -> &graph::DepGraph; +/// Execute the operation with provided dependencies. +fn with_deps(task_deps: TaskDepsRef<'_>, op: OP) -> R +where + OP: FnOnce() -> R, +{ + ty::tls::with_context(|icx| { + let icx = ty::tls::ImplicitCtxt { task_deps, ..*icx }; + ty::tls::enter_context(&icx, op) + }) +} - /// Access the profiler. - fn profiler(&self) -> &SelfProfilerRef; +/// Access dependencies from current implicit context. +fn read_deps(op: OP) +where + OP: for<'a> FnOnce(TaskDepsRef<'a>), +{ + ty::tls::with_context_opt(|icx| { + let Some(icx) = icx else { return }; + op(icx.task_deps) + }) +} - /// Access the compiler session. - fn sess(&self) -> &Session; +impl<'tcx> TyCtxt<'tcx> { + #[inline] + pub fn dep_kind_vtable(self, dk: DepKind) -> &'tcx DepKindVTable<'tcx> { + &self.dep_kind_vtables[dk.as_usize()] + } - fn dep_kind_vtable(&self, dep_node: DepKind) -> &dep_node::DepKindVTable; - - #[inline(always)] - fn fingerprint_style(self, kind: DepKind) -> FingerprintStyle { - self.dep_kind_vtable(kind).fingerprint_style + fn with_reduced_queries(self, f: impl FnOnce() -> T) -> T { + with_reduced_queries!(f()) } #[inline(always)] - /// Return whether this kind always require evaluation. - fn is_eval_always(self, kind: DepKind) -> bool { - self.dep_kind_vtable(kind).is_eval_always + pub fn key_fingerprint_style(self, kind: DepKind) -> KeyFingerprintStyle { + self.dep_kind_vtable(kind).key_fingerprint_style } /// Try to force a dep node to execute and see if it's green. @@ -69,13 +107,13 @@ fn try_force_from_dep_node( prev_index: SerializedDepNodeIndex, frame: &MarkFrame<'_>, ) -> bool { - if let Some(force_fn) = self.dep_kind_vtable(dep_node.kind).force_from_dep_node { + if let Some(force_fn) = self.dep_kind_vtable(dep_node.kind).force_from_dep_node_fn { match panic::catch_unwind(panic::AssertUnwindSafe(|| { force_fn(self, dep_node, prev_index) })) { Err(value) => { if !value.is::() { - print_markframe_trace(self.dep_graph(), frame); + print_markframe_trace(&self.dep_graph, frame); } panic::resume_unwind(value) } @@ -85,168 +123,4 @@ fn try_force_from_dep_node( false } } - - /// Load data from the on-disk cache. - fn try_load_from_on_disk_cache(self, dep_node: &DepNode) { - if let Some(try_load_fn) = self.dep_kind_vtable(dep_node.kind).try_load_from_on_disk_cache { - try_load_fn(self, *dep_node) - } - } - - fn with_reduced_queries(self, _: impl FnOnce() -> T) -> T; -} - -pub trait Deps: DynSync { - /// Execute the operation with provided dependencies. - fn with_deps(deps: TaskDepsRef<'_>, op: OP) -> R - where - OP: FnOnce() -> R; - - /// Access dependencies from current implicit context. - fn read_deps(op: OP) - where - OP: for<'a> FnOnce(TaskDepsRef<'a>); - - fn name(dep_kind: DepKind) -> &'static str; - - /// We use this for most things when incr. comp. is turned off. - const DEP_KIND_NULL: DepKind; - - /// We use this to create a forever-red node. - const DEP_KIND_RED: DepKind; - - /// We use this to create a side effect node. - const DEP_KIND_SIDE_EFFECT: DepKind; - - /// We use this to create the anon node with zero dependencies. - const DEP_KIND_ANON_ZERO_DEPS: DepKind; - - /// This is the highest value a `DepKind` can have. It's used during encoding to - /// pack information into the unused bits. - const DEP_KIND_MAX: u16; -} - -pub trait HasDepContext: Copy { - type Deps: self::Deps; - type DepContext: self::DepContext; - - fn dep_context(&self) -> &Self::DepContext; -} - -impl HasDepContext for T { - type Deps = T::Deps; - type DepContext = Self; - - fn dep_context(&self) -> &Self::DepContext { - self - } -} - -impl HasDepContext for (T, Q) { - type Deps = T::Deps; - type DepContext = T::DepContext; - - fn dep_context(&self) -> &Self::DepContext { - self.0.dep_context() - } -} - -/// Describes the contents of the fingerprint generated by a given query. -/// -/// This is mainly for determining whether and how we can reconstruct a key -/// from the fingerprint. -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum FingerprintStyle { - /// The fingerprint is actually a DefPathHash. - DefPathHash, - /// The fingerprint is actually a HirId. - HirId, - /// Query key was `()` or equivalent, so fingerprint is just zero. - Unit, - /// The fingerprint is an opaque hash, and a key cannot be reconstructed from it. - Opaque, -} - -impl FingerprintStyle { - #[inline] - pub const fn reconstructible(self) -> bool { - match self { - FingerprintStyle::DefPathHash | FingerprintStyle::Unit | FingerprintStyle::HirId => { - true - } - FingerprintStyle::Opaque => false, - } - } -} - -pub type DepGraph = graph::DepGraph; - -pub type DepKindVTable<'tcx> = dep_node::DepKindVTable>; - -pub struct DepsType; - -impl Deps for DepsType { - fn with_deps(task_deps: TaskDepsRef<'_>, op: OP) -> R - where - OP: FnOnce() -> R, - { - ty::tls::with_context(|icx| { - let icx = ty::tls::ImplicitCtxt { task_deps, ..icx.clone() }; - - ty::tls::enter_context(&icx, op) - }) - } - - fn read_deps(op: OP) - where - OP: for<'a> FnOnce(TaskDepsRef<'a>), - { - ty::tls::with_context_opt(|icx| { - let Some(icx) = icx else { return }; - op(icx.task_deps) - }) - } - - fn name(dep_kind: DepKind) -> &'static str { - dep_node::DEP_KIND_NAMES[dep_kind.as_usize()] - } - - const DEP_KIND_NULL: DepKind = dep_kinds::Null; - const DEP_KIND_RED: DepKind = dep_kinds::Red; - const DEP_KIND_SIDE_EFFECT: DepKind = dep_kinds::SideEffect; - const DEP_KIND_ANON_ZERO_DEPS: DepKind = dep_kinds::AnonZeroDeps; - const DEP_KIND_MAX: u16 = dep_node::DEP_KIND_VARIANTS - 1; -} - -impl<'tcx> DepContext for TyCtxt<'tcx> { - type Deps = DepsType; - - #[inline] - fn with_stable_hashing_context(self, f: impl FnOnce(StableHashingContext<'_>) -> R) -> R { - TyCtxt::with_stable_hashing_context(self, f) - } - - #[inline] - fn dep_graph(&self) -> &DepGraph { - &self.dep_graph - } - - #[inline(always)] - fn profiler(&self) -> &SelfProfilerRef { - &self.prof - } - - #[inline(always)] - fn sess(&self) -> &Session { - self.sess - } - - #[inline] - fn dep_kind_vtable(&self, dk: DepKind) -> &DepKindVTable<'tcx> { - &self.dep_kind_vtables[dk.as_usize()] - } - - fn with_reduced_queries(self, f: impl FnOnce() -> T) -> T { - with_reduced_queries!(f()) - } } diff --git a/compiler/rustc_middle/src/dep_graph/query.rs b/compiler/rustc_middle/src/dep_graph/retained.rs similarity index 62% rename from compiler/rustc_middle/src/dep_graph/query.rs rename to compiler/rustc_middle/src/dep_graph/retained.rs index f145e1cba2d7..4427982e3708 100644 --- a/compiler/rustc_middle/src/dep_graph/query.rs +++ b/compiler/rustc_middle/src/dep_graph/retained.rs @@ -4,26 +4,32 @@ use super::{DepNode, DepNodeIndex}; -pub struct DepGraphQuery { - pub graph: LinkedGraph, +/// An in-memory copy of the current session's query dependency graph, which +/// is only enabled when `-Zquery-dep-graph` is set (for debugging/testing). +/// +/// Normally, dependencies recorded during the current session are written to +/// disk and then forgotten, to avoid wasting memory on information that is +/// not needed when the compiler is working correctly. +pub struct RetainedDepGraph { + pub inner: LinkedGraph, pub indices: FxHashMap, pub dep_index_to_index: IndexVec>, } -impl DepGraphQuery { - pub fn new(prev_node_count: usize) -> DepGraphQuery { +impl RetainedDepGraph { + pub fn new(prev_node_count: usize) -> Self { let node_count = prev_node_count + prev_node_count / 4; let edge_count = 6 * node_count; - let graph = LinkedGraph::with_capacity(node_count, edge_count); + let inner = LinkedGraph::with_capacity(node_count, edge_count); let indices = FxHashMap::default(); let dep_index_to_index = IndexVec::new(); - DepGraphQuery { graph, indices, dep_index_to_index } + Self { inner, indices, dep_index_to_index } } pub fn push(&mut self, index: DepNodeIndex, node: DepNode, edges: &[DepNodeIndex]) { - let source = self.graph.add_node(node); + let source = self.inner.add_node(node); self.dep_index_to_index.insert(index, source); self.indices.insert(node, source); @@ -31,27 +37,27 @@ pub fn push(&mut self, index: DepNodeIndex, node: DepNode, edges: &[DepNodeIndex // We may miss the edges that are pushed while the `DepGraphQuery` is being accessed. // Skip them to issues. if let Some(&Some(target)) = self.dep_index_to_index.get(target) { - self.graph.add_edge(source, target, ()); + self.inner.add_edge(source, target, ()); } } } pub fn nodes(&self) -> Vec<&DepNode> { - self.graph.all_nodes().iter().map(|n| &n.data).collect() + self.inner.all_nodes().iter().map(|n| &n.data).collect() } pub fn edges(&self) -> Vec<(&DepNode, &DepNode)> { - self.graph + self.inner .all_edges() .iter() .map(|edge| (edge.source(), edge.target())) - .map(|(s, t)| (self.graph.node_data(s), self.graph.node_data(t))) + .map(|(s, t)| (self.inner.node_data(s), self.inner.node_data(t))) .collect() } fn reachable_nodes(&self, node: &DepNode, direction: Direction) -> Vec<&DepNode> { if let Some(&index) = self.indices.get(node) { - self.graph.depth_traverse(index, direction).map(|s| self.graph.node_data(s)).collect() + self.inner.depth_traverse(index, direction).map(|s| self.inner.node_data(s)).collect() } else { vec![] } diff --git a/compiler/rustc_middle/src/dep_graph/serialized.rs b/compiler/rustc_middle/src/dep_graph/serialized.rs index 3b5954fc8f94..8a4ac4b5e5ac 100644 --- a/compiler/rustc_middle/src/dep_graph/serialized.rs +++ b/compiler/rustc_middle/src/dep_graph/serialized.rs @@ -41,7 +41,6 @@ use std::cell::RefCell; use std::cmp::max; -use std::marker::PhantomData; use std::sync::Arc; use std::sync::atomic::Ordering; use std::{iter, mem, u64}; @@ -60,8 +59,8 @@ use tracing::{debug, instrument}; use super::graph::{CurrentDepGraph, DepNodeColorMap}; -use super::query::DepGraphQuery; -use super::{DepKind, DepNode, DepNodeIndex, Deps}; +use super::retained::RetainedDepGraph; +use super::{DepKind, DepNode, DepNodeIndex}; use crate::dep_graph::edges::EdgesVec; // The maximum value of `SerializedDepNodeIndex` leaves the upper two bits @@ -85,15 +84,19 @@ pub struct SerializedDepNodeIndex {} /// Data for use when recompiling the **current crate**. /// -/// There may be unused indices with DEP_KIND_NULL in this graph due to batch allocation of +/// There may be unused indices with DepKind::Null in this graph due to batch allocation of /// indices to threads. #[derive(Debug, Default)] pub struct SerializedDepGraph { /// The set of all DepNodes in the graph nodes: IndexVec, - /// The set of all Fingerprints in the graph. Each Fingerprint corresponds to - /// the DepNode at the same index in the nodes vector. - fingerprints: IndexVec, + /// A value fingerprint associated with each [`DepNode`] in [`Self::nodes`], + /// typically a hash of the value returned by the node's query in the + /// previous incremental-compilation session. + /// + /// Some nodes don't have a meaningful value hash (e.g. queries with `no_hash`), + /// so they store a dummy value here instead (e.g. [`Fingerprint::ZERO`]). + value_fingerprints: IndexVec, /// For each DepNode, stores the list of edges originating from that /// DepNode. Encoded as a [start, end) pair indexing into edge_list_data, /// which holds the actual DepNodeIndices of the target nodes. @@ -101,8 +104,8 @@ pub struct SerializedDepGraph { /// A flattened list of all edge targets in the graph, stored in the same /// varint encoding that we use on disk. Edge sources are implicit in edge_list_indices. edge_list_data: Vec, - /// Stores a map from fingerprints to nodes per dep node kind. - /// This is the reciprocal of `nodes`. + /// For each dep kind, stores a map from key fingerprints back to the index + /// of the corresponding node. This is the inverse of `nodes`. index: Vec>, /// The number of previous compilation sessions. This is used to generate /// unique anon dep nodes per session. @@ -139,12 +142,15 @@ pub fn index_to_node(&self, dep_node_index: SerializedDepNodeIndex) -> &DepNode #[inline] pub fn node_to_index_opt(&self, dep_node: &DepNode) -> Option { - self.index.get(dep_node.kind.as_usize())?.get(&dep_node.hash).cloned() + self.index.get(dep_node.kind.as_usize())?.get(&dep_node.key_fingerprint).copied() } #[inline] - pub fn fingerprint_by_index(&self, dep_node_index: SerializedDepNodeIndex) -> Fingerprint { - self.fingerprints[dep_node_index] + pub fn value_fingerprint_for_index( + &self, + dep_node_index: SerializedDepNodeIndex, + ) -> Fingerprint { + self.value_fingerprints[dep_node_index] } #[inline] @@ -192,7 +198,7 @@ fn mask(bits: usize) -> usize { impl SerializedDepGraph { #[instrument(level = "debug", skip(d))] - pub fn decode(d: &mut MemDecoder<'_>) -> Arc { + pub fn decode(d: &mut MemDecoder<'_>) -> Arc { // The last 16 bytes are the node count and edge count. debug!("position: {:?}", d.position()); @@ -213,10 +219,13 @@ pub fn decode(d: &mut MemDecoder<'_>) -> Arc { let graph_bytes = d.len() - (3 * IntEncodedWithFixedSize::ENCODED_SIZE) - d.position(); let mut nodes = IndexVec::from_elem_n( - DepNode { kind: D::DEP_KIND_NULL, hash: PackedFingerprint::from(Fingerprint::ZERO) }, + DepNode { + kind: DepKind::Null, + key_fingerprint: PackedFingerprint::from(Fingerprint::ZERO), + }, node_max, ); - let mut fingerprints = IndexVec::from_elem_n(Fingerprint::ZERO, node_max); + let mut value_fingerprints = IndexVec::from_elem_n(Fingerprint::ZERO, node_max); let mut edge_list_indices = IndexVec::from_elem_n(EdgeHeader { repr: 0, num_edges: 0 }, node_max); @@ -230,22 +239,21 @@ pub fn decode(d: &mut MemDecoder<'_>) -> Arc { // least (34 byte header + 1 byte len + 64 bytes edge data), which is ~1%. A 2-byte leb128 // length is about the same fractional overhead and it amortizes for yet greater lengths. let mut edge_list_data = - Vec::with_capacity(graph_bytes - node_count * size_of::>()); + Vec::with_capacity(graph_bytes - node_count * size_of::()); for _ in 0..node_count { // Decode the header for this edge; the header packs together as many of the fixed-size // fields as possible to limit the number of times we update decoder state. - let node_header = - SerializedNodeHeader:: { bytes: d.read_array(), _marker: PhantomData }; + let node_header = SerializedNodeHeader { bytes: d.read_array() }; let index = node_header.index(); let node = &mut nodes[index]; // Make sure there's no duplicate indices in the dep graph. - assert!(node_header.node().kind != D::DEP_KIND_NULL && node.kind == D::DEP_KIND_NULL); + assert!(node_header.node().kind != DepKind::Null && node.kind == DepKind::Null); *node = node_header.node(); - fingerprints[index] = node_header.fingerprint(); + value_fingerprints[index] = node_header.value_fingerprint(); // If the length of this node's edge list is small, the length is stored in the header. // If it is not, we fall back to another decoder call. @@ -270,30 +278,31 @@ pub fn decode(d: &mut MemDecoder<'_>) -> Arc { edge_list_data.extend(&[0u8; DEP_NODE_PAD]); // Read the number of each dep kind and use it to create an hash map with a suitable size. - let mut index: Vec<_> = (0..(D::DEP_KIND_MAX + 1)) + let mut index: Vec<_> = (0..(DepKind::MAX + 1)) .map(|_| UnhashMap::with_capacity_and_hasher(d.read_u32() as usize, Default::default())) .collect(); let session_count = d.read_u64(); for (idx, node) in nodes.iter_enumerated() { - if index[node.kind.as_usize()].insert(node.hash, idx).is_some() { + if index[node.kind.as_usize()].insert(node.key_fingerprint, idx).is_some() { // Empty nodes and side effect nodes can have duplicates - if node.kind != D::DEP_KIND_NULL && node.kind != D::DEP_KIND_SIDE_EFFECT { - let name = D::name(node.kind); + if node.kind != DepKind::Null && node.kind != DepKind::SideEffect { + let kind = node.kind; panic!( - "Error: A dep graph node ({name}) does not have an unique index. \ - Running a clean build on a nightly compiler with `-Z incremental-verify-ich` \ - can help narrow down the issue for reporting. A clean build may also work around the issue.\n - DepNode: {node:?}" - ) + "Error: A dep graph node ({kind:?}) does not have an unique index. \ + Running a clean build on a nightly compiler with \ + `-Z incremental-verify-ich` can help narrow down the issue for reporting. \ + A clean build may also work around the issue.\n + DepNode: {node:?}" + ) } } } Arc::new(SerializedDepGraph { nodes, - fingerprints, + value_fingerprints, edge_list_indices, edge_list_data, index, @@ -305,18 +314,17 @@ pub fn decode(d: &mut MemDecoder<'_>) -> Arc { /// A packed representation of all the fixed-size fields in a `NodeInfo`. /// /// This stores in one byte array: -/// * The `Fingerprint` in the `NodeInfo` -/// * The `Fingerprint` in `DepNode` that is in this `NodeInfo` +/// * The value `Fingerprint` in the `NodeInfo` +/// * The key `Fingerprint` in `DepNode` that is in this `NodeInfo` /// * The `DepKind`'s discriminant (a u16, but not all bits are used...) /// * The byte width of the encoded edges for this node /// * In whatever bits remain, the length of the edge list for this node, if it fits -struct SerializedNodeHeader { +struct SerializedNodeHeader { // 2 bytes for the DepNode // 4 bytes for the index // 16 for Fingerprint in DepNode // 16 for Fingerprint in NodeInfo bytes: [u8; 38], - _marker: PhantomData, } // The fields of a `SerializedNodeHeader`, this struct is an implementation detail and exists only @@ -326,8 +334,8 @@ struct Unpacked { bytes_per_index: usize, kind: DepKind, index: SerializedDepNodeIndex, - hash: PackedFingerprint, - fingerprint: Fingerprint, + key_fingerprint: PackedFingerprint, + value_fingerprint: Fingerprint, } // Bit fields, where @@ -337,24 +345,24 @@ struct Unpacked { // 0..M length of the edge // M..M+N bytes per index // M+N..16 kind -impl SerializedNodeHeader { +impl SerializedNodeHeader { const TOTAL_BITS: usize = size_of::() * 8; const LEN_BITS: usize = Self::TOTAL_BITS - Self::KIND_BITS - Self::WIDTH_BITS; const WIDTH_BITS: usize = DEP_NODE_WIDTH_BITS; - const KIND_BITS: usize = Self::TOTAL_BITS - D::DEP_KIND_MAX.leading_zeros() as usize; + const KIND_BITS: usize = Self::TOTAL_BITS - DepKind::MAX.leading_zeros() as usize; const MAX_INLINE_LEN: usize = (u16::MAX as usize >> (Self::TOTAL_BITS - Self::LEN_BITS)) - 1; #[inline] fn new( node: &DepNode, index: DepNodeIndex, - fingerprint: Fingerprint, + value_fingerprint: Fingerprint, edge_max_index: u32, edge_count: usize, ) -> Self { debug_assert_eq!(Self::TOTAL_BITS, Self::LEN_BITS + Self::WIDTH_BITS + Self::KIND_BITS); - let mut head = node.kind.as_inner(); + let mut head = node.kind.as_u16(); let free_bytes = edge_max_index.leading_zeros() as usize / 8; let bytes_per_index = (DEP_NODE_SIZE - free_bytes).saturating_sub(1); @@ -366,33 +374,33 @@ fn new( head |= (edge_count as u16 + 1) << (Self::KIND_BITS + Self::WIDTH_BITS); } - let hash: Fingerprint = node.hash.into(); + let hash: Fingerprint = node.key_fingerprint.into(); // Using half-open ranges ensures an unconditional panic if we get the magic numbers wrong. let mut bytes = [0u8; 38]; bytes[..2].copy_from_slice(&head.to_le_bytes()); bytes[2..6].copy_from_slice(&index.as_u32().to_le_bytes()); bytes[6..22].copy_from_slice(&hash.to_le_bytes()); - bytes[22..].copy_from_slice(&fingerprint.to_le_bytes()); + bytes[22..].copy_from_slice(&value_fingerprint.to_le_bytes()); #[cfg(debug_assertions)] { - let res = Self { bytes, _marker: PhantomData }; - assert_eq!(fingerprint, res.fingerprint()); + let res = Self { bytes }; + assert_eq!(value_fingerprint, res.value_fingerprint()); assert_eq!(*node, res.node()); if let Some(len) = res.len() { assert_eq!(edge_count, len as usize); } } - Self { bytes, _marker: PhantomData } + Self { bytes } } #[inline] fn unpack(&self) -> Unpacked { let head = u16::from_le_bytes(self.bytes[..2].try_into().unwrap()); let index = u32::from_le_bytes(self.bytes[2..6].try_into().unwrap()); - let hash = self.bytes[6..22].try_into().unwrap(); - let fingerprint = self.bytes[22..].try_into().unwrap(); + let key_fingerprint = self.bytes[6..22].try_into().unwrap(); + let value_fingerprint = self.bytes[22..].try_into().unwrap(); let kind = head & mask(Self::KIND_BITS) as u16; let bytes_per_index = (head >> Self::KIND_BITS) & mask(Self::WIDTH_BITS) as u16; @@ -401,10 +409,10 @@ fn unpack(&self) -> Unpacked { Unpacked { len: len.checked_sub(1), bytes_per_index: bytes_per_index as usize + 1, - kind: DepKind::new(kind), + kind: DepKind::from_u16(kind), index: SerializedDepNodeIndex::from_u32(index), - hash: Fingerprint::from_le_bytes(hash).into(), - fingerprint: Fingerprint::from_le_bytes(fingerprint), + key_fingerprint: Fingerprint::from_le_bytes(key_fingerprint).into(), + value_fingerprint: Fingerprint::from_le_bytes(value_fingerprint), } } @@ -424,14 +432,14 @@ fn index(&self) -> SerializedDepNodeIndex { } #[inline] - fn fingerprint(&self) -> Fingerprint { - self.unpack().fingerprint + fn value_fingerprint(&self) -> Fingerprint { + self.unpack().value_fingerprint } #[inline] fn node(&self) -> DepNode { - let Unpacked { kind, hash, .. } = self.unpack(); - DepNode { kind, hash } + let Unpacked { kind, key_fingerprint, .. } = self.unpack(); + DepNode { kind, key_fingerprint } } #[inline] @@ -446,17 +454,17 @@ fn edges_header(&self, edge_list_data: &[u8], num_edges: u32) -> EdgeHeader { #[derive(Debug)] struct NodeInfo { node: DepNode, - fingerprint: Fingerprint, + value_fingerprint: Fingerprint, edges: EdgesVec, } impl NodeInfo { - fn encode(&self, e: &mut MemEncoder, index: DepNodeIndex) { - let NodeInfo { ref node, fingerprint, ref edges } = *self; - let header = SerializedNodeHeader::::new( + fn encode(&self, e: &mut MemEncoder, index: DepNodeIndex) { + let NodeInfo { ref node, value_fingerprint, ref edges } = *self; + let header = SerializedNodeHeader::new( node, index, - fingerprint, + value_fingerprint, edges.max_index(), edges.len(), ); @@ -480,11 +488,11 @@ fn encode(&self, e: &mut MemEncoder, index: DepNodeIndex) { /// the previous dep graph and expects all edges to already have a new dep node index assigned. /// This avoids the overhead of constructing `EdgesVec`, which would be needed to call `encode`. #[inline] - fn encode_promoted( + fn encode_promoted( e: &mut MemEncoder, node: &DepNode, index: DepNodeIndex, - fingerprint: Fingerprint, + value_fingerprint: Fingerprint, prev_index: SerializedDepNodeIndex, colors: &DepNodeColorMap, previous: &SerializedDepGraph, @@ -496,7 +504,8 @@ fn encode_promoted( let edge_max = edges.clone().map(|i| colors.current(i).unwrap().as_u32()).max().unwrap_or(0); - let header = SerializedNodeHeader::::new(node, index, fingerprint, edge_max, edge_count); + let header = + SerializedNodeHeader::new(node, index, value_fingerprint, edge_max, edge_count); e.write_array(header.bytes); if header.len().is_none() { @@ -543,16 +552,15 @@ struct LocalEncoderResult { kind_stats: Vec, } -struct EncoderState { +struct EncoderState { next_node_index: AtomicU64, previous: Arc, file: Lock>, local: WorkerLocal>, stats: Option>>, - marker: PhantomData, } -impl EncoderState { +impl EncoderState { fn new(encoder: FileEncoder, record_stats: bool, previous: Arc) -> Self { Self { previous, @@ -566,10 +574,9 @@ fn new(encoder: FileEncoder, record_stats: bool, previous: Arc Vec, - record_graph: &Option>, + retained_graph: &Option>, local: &mut LocalEncoderState, ) { local.kind_stats[node.kind.as_usize()] += 1; local.edge_count += edge_count; - if let Some(record_graph) = &record_graph { + if let Some(retained_graph) = &retained_graph { // Call `edges` before the outlined code to allow the closure to be optimized out. let edges = edges(self); // Outline the build of the full dep graph as it's typically disabled and cold. outline(move || { // Do not ICE when a query is called from within `with_query`. - if let Some(record_graph) = &mut record_graph.try_lock() { - record_graph.push(index, *node, &edges); + if let Some(retained_graph) = &mut retained_graph.try_lock() { + retained_graph.push(index, *node, &edges); } }); } @@ -655,17 +662,17 @@ fn encode_node( &self, index: DepNodeIndex, node: &NodeInfo, - record_graph: &Option>, + retained_graph: &Option>, local: &mut LocalEncoderState, ) { - node.encode::(&mut local.encoder, index); + node.encode(&mut local.encoder, index); self.flush_mem_encoder(&mut *local); self.record( &node.node, index, node.edges.len(), |_| node.edges[..].to_vec(), - record_graph, + retained_graph, &mut *local, ); } @@ -681,17 +688,17 @@ fn encode_promoted_node( &self, index: DepNodeIndex, prev_index: SerializedDepNodeIndex, - record_graph: &Option>, + retained_graph: &Option>, colors: &DepNodeColorMap, local: &mut LocalEncoderState, ) { let node = self.previous.index_to_node(prev_index); - let fingerprint = self.previous.fingerprint_by_index(prev_index); - let edge_count = NodeInfo::encode_promoted::( + let value_fingerprint = self.previous.value_fingerprint_for_index(prev_index); + let edge_count = NodeInfo::encode_promoted( &mut local.encoder, node, index, - fingerprint, + value_fingerprint, prev_index, colors, &self.previous, @@ -707,12 +714,12 @@ fn encode_promoted_node( .map(|i| colors.current(i).unwrap()) .collect() }, - record_graph, + retained_graph, &mut *local, ); } - fn finish(&self, profiler: &SelfProfilerRef, current: &CurrentDepGraph) -> FileEncodeResult { + fn finish(&self, profiler: &SelfProfilerRef, current: &CurrentDepGraph) -> FileEncodeResult { // Prevent more indices from being allocated. self.next_node_index.store(u32::MAX as u64 + 1, Ordering::SeqCst); @@ -735,7 +742,7 @@ fn finish(&self, profiler: &SelfProfilerRef, current: &CurrentDepGraph) -> Fi let mut encoder = self.file.lock().take().unwrap(); - let mut kind_stats: Vec = iter::repeat_n(0, D::DEP_KIND_MAX as usize + 1).collect(); + let mut kind_stats: Vec = iter::repeat_n(0, DepKind::MAX as usize + 1).collect(); let mut node_max = 0; let mut node_count = 0; @@ -778,7 +785,7 @@ fn finish(&self, profiler: &SelfProfilerRef, current: &CurrentDepGraph) -> Fi fn print_incremental_info( &self, - current: &CurrentDepGraph, + current: &CurrentDepGraph, total_node_count: usize, total_edge_count: usize, ) { @@ -835,31 +842,32 @@ fn print_incremental_info( } } -pub(crate) struct GraphEncoder { +pub(crate) struct GraphEncoder { profiler: SelfProfilerRef, - status: EncoderState, - record_graph: Option>, + status: EncoderState, + /// In-memory copy of the dep graph; only present if `-Zquery-dep-graph` is set. + retained_graph: Option>, } -impl GraphEncoder { +impl GraphEncoder { pub(crate) fn new( sess: &Session, encoder: FileEncoder, prev_node_count: usize, previous: Arc, ) -> Self { - let record_graph = sess + let retained_graph = sess .opts .unstable_opts .query_dep_graph - .then(|| Lock::new(DepGraphQuery::new(prev_node_count))); + .then(|| Lock::new(RetainedDepGraph::new(prev_node_count))); let status = EncoderState::new(encoder, sess.opts.unstable_opts.incremental_info, previous); - GraphEncoder { status, record_graph, profiler: sess.prof.clone() } + GraphEncoder { status, retained_graph, profiler: sess.prof.clone() } } - pub(crate) fn with_query(&self, f: impl Fn(&DepGraphQuery)) { - if let Some(record_graph) = &self.record_graph { - f(&record_graph.lock()) + pub(crate) fn with_retained_dep_graph(&self, f: impl Fn(&RetainedDepGraph)) { + if let Some(retained_graph) = &self.retained_graph { + f(&retained_graph.lock()) } } @@ -867,15 +875,15 @@ pub(crate) fn with_query(&self, f: impl Fn(&DepGraphQuery)) { pub(crate) fn send_new( &self, node: DepNode, - fingerprint: Fingerprint, + value_fingerprint: Fingerprint, edges: EdgesVec, ) -> DepNodeIndex { let _prof_timer = self.profiler.generic_activity("incr_comp_encode_dep_graph"); - let node = NodeInfo { node, fingerprint, edges }; + let node = NodeInfo { node, value_fingerprint, edges }; let mut local = self.status.local.borrow_mut(); let index = self.status.next_index(&mut *local); self.status.bump_index(&mut *local); - self.status.encode_node(index, &node, &self.record_graph, &mut *local); + self.status.encode_node(index, &node, &self.retained_graph, &mut *local); index } @@ -887,12 +895,12 @@ pub(crate) fn send_and_color( prev_index: SerializedDepNodeIndex, colors: &DepNodeColorMap, node: DepNode, - fingerprint: Fingerprint, + value_fingerprint: Fingerprint, edges: EdgesVec, is_green: bool, ) -> DepNodeIndex { let _prof_timer = self.profiler.generic_activity("incr_comp_encode_dep_graph"); - let node = NodeInfo { node, fingerprint, edges }; + let node = NodeInfo { node, value_fingerprint, edges }; let mut local = self.status.local.borrow_mut(); @@ -907,7 +915,7 @@ pub(crate) fn send_and_color( } self.status.bump_index(&mut *local); - self.status.encode_node(index, &node, &self.record_graph, &mut *local); + self.status.encode_node(index, &node, &self.retained_graph, &mut *local); index } @@ -935,7 +943,7 @@ pub(crate) fn send_promoted( self.status.encode_promoted_node( index, prev_index, - &self.record_graph, + &self.retained_graph, colors, &mut *local, ); @@ -945,7 +953,7 @@ pub(crate) fn send_promoted( } } - pub(crate) fn finish(&self, current: &CurrentDepGraph) -> FileEncodeResult { + pub(crate) fn finish(&self, current: &CurrentDepGraph) -> FileEncodeResult { let _prof_timer = self.profiler.generic_activity("incr_comp_encode_dep_graph_finish"); self.status.finish(&self.profiler, current) diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index ca783311586f..cf1b863f754c 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -8,7 +8,6 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; 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::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; @@ -290,7 +289,7 @@ pub fn hir_body_param_idents(self, id: BodyId) -> impl Iterator) -> BodyOwnerKind { let def_id = def_id.into(); match self.def_kind(def_id) { - DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => { + DefKind::Const { .. } | DefKind::AssocConst { .. } | DefKind::AnonConst => { BodyOwnerKind::Const { inline: false } } DefKind::InlineConst => BodyOwnerKind::Const { inline: true }, @@ -369,7 +368,7 @@ pub fn hir_krate_attrs(self) -> &'tcx [Attribute] { } pub fn hir_rustc_coherence_is_core(self) -> bool { - find_attr!(self.hir_krate_attrs(), AttributeKind::RustcCoherenceIsCore(..)) + find_attr!(self.hir_krate_attrs(), RustcCoherenceIsCore(..)) } pub fn hir_get_module(self, module: LocalModDefId) -> (&'tcx Mod<'tcx>, Span, HirId) { diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index c926604a4195..2ab03adf6aaf 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -9,6 +9,7 @@ use rustc_span::{ExpnHash, ExpnId}; use crate::mir; +use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex}; use crate::ty::{Ty, TyCtxt}; macro_rules! declare_hooks { @@ -107,6 +108,11 @@ fn clone(&self) -> Self { *self } /// /// Creates the MIR for a given `DefId`, including unreachable code. hook build_mir_inner_impl(def: LocalDefId) -> mir::Body<'tcx>; + + hook encode_all_query_results( + encoder: &mut CacheEncoder<'_, 'tcx>, + query_result_index: &mut EncodedDepNodeIndex + ) -> (); } #[cold] diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_middle/src/ich/hcx.rs similarity index 100% rename from compiler/rustc_query_system/src/ich/hcx.rs rename to compiler/rustc_middle/src/ich/hcx.rs diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_middle/src/ich/impls_syntax.rs similarity index 98% rename from compiler/rustc_query_system/src/ich/impls_syntax.rs rename to compiler/rustc_middle/src/ich/impls_syntax.rs index 5592f6553971..1063b37be87e 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_middle/src/ich/impls_syntax.rs @@ -1,12 +1,13 @@ //! This module contains `HashStable` implementations for various data types //! from various crates in no particular order. +use rustc_ast as ast; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir as hir; use rustc_span::{SourceFile, Symbol, sym}; use smallvec::SmallVec; -use {rustc_ast as ast, rustc_hir as hir}; -use crate::ich::StableHashingContext; +use super::StableHashingContext; impl<'a> HashStable> for ast::NodeId { #[inline] diff --git a/compiler/rustc_query_system/src/ich/mod.rs b/compiler/rustc_middle/src/ich/mod.rs similarity index 100% rename from compiler/rustc_query_system/src/ich/mod.rs rename to compiler/rustc_middle/src/ich/mod.rs diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 1c4c987aee92..84fe59b3f711 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -27,7 +27,6 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::direct_use_of_rustc_type_ir)] -#![cfg_attr(bootstrap, feature(assert_matches))] #![cfg_attr(doc, feature(intra_doc_pointers))] #![feature(allocator_api)] #![feature(associated_type_defaults)] @@ -43,7 +42,6 @@ #![feature(extern_types)] #![feature(file_buffered)] #![feature(gen_blocks)] -#![feature(if_let_guard)] #![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] @@ -72,6 +70,7 @@ pub mod error; pub mod hir; pub mod hooks; +pub mod ich; pub mod infer; pub mod lint; pub mod metadata; diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index e4715f6e2c10..eb1628762ffe 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -2,14 +2,14 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; -use rustc_errors::{Diag, MultiSpan}; +use rustc_errors::{Diag, DiagLocation, Diagnostic, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; use rustc_lint_defs::EditionFcw; use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_session::Session; use rustc_session::lint::builtin::{self, FORBIDDEN_LINT_GROUPS}; use rustc_session::lint::{FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId}; -use rustc_span::{DUMMY_SP, Span, Symbol, kw}; +use rustc_span::{DUMMY_SP, ExpnKind, Span, Symbol, kw}; use tracing::instrument; use crate::ty::TyCtxt; @@ -299,7 +299,6 @@ fn explain_lint_level_source( /// for example: /// - [`TyCtxt::emit_node_span_lint`] /// - [`TyCtxt::node_span_lint`] -/// - [`TyCtxt::emit_node_lint`] /// - [`TyCtxt::node_lint`] /// - `LintContext::opt_span_lint` /// @@ -380,7 +379,17 @@ fn lint_level_impl( // allow individual lints to opt-out from being reported. let incompatible = future_incompatible.is_some_and(|f| f.reason.edition().is_none()); - if !incompatible && !lint.report_in_external_macro { + // In rustc, for the find_attr macro, we want to always emit this. + // This completely circumvents normal lint checking, which usually doesn't happen for macros from other crates. + // However, we kind of want that when using find_attr from another rustc crate. So we cheat a little. + let is_in_find_attr = sess.enable_internal_lints() + && err.span.primary_spans().iter().any(|s| { + s.source_callee().is_some_and( + |i| matches!(i.kind, ExpnKind::Macro(_, name) if name.as_str() == "find_attr") + ) + }); + + if !incompatible && !lint.report_in_external_macro && !is_in_find_attr { err.cancel(); // Don't continue further, since we don't want to have @@ -472,3 +481,219 @@ fn lint_level_impl( } lint_level_impl(sess, lint, level, span, Box::new(decorate)) } + +/// The innermost function for emitting lints implementing the [`trait@Diagnostic`] trait. +/// +/// If you are looking to implement a lint, look for higher level functions, +/// for example: +/// +/// - [`TyCtxt::emit_node_span_lint`] +/// - [`TyCtxt::node_span_lint`] +/// - [`TyCtxt::node_lint`] +/// - `LintContext::opt_span_lint` +/// +/// This function will replace `lint_level` once all its callers have been replaced +/// with `diag_lint_level`. +#[track_caller] +pub fn diag_lint_level<'a, D: Diagnostic<'a, ()> + 'a>( + sess: &'a Session, + lint: &'static Lint, + level: LevelAndSource, + span: Option, + decorate: D, +) { + // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to + // the "real" work. + #[track_caller] + fn diag_lint_level_impl<'a>( + sess: &'a Session, + lint: &'static Lint, + level: LevelAndSource, + span: Option, + decorate: Box< + dyn FnOnce(rustc_errors::DiagCtxtHandle<'a>, rustc_errors::Level) -> Diag<'a, ()> + 'a, + >, + ) { + let LevelAndSource { level, lint_id, src } = level; + + // Check for future incompatibility lints and issue a stronger warning. + let future_incompatible = lint.future_incompatible; + + let has_future_breakage = future_incompatible.map_or( + // Default allow lints trigger too often for testing. + sess.opts.unstable_opts.future_incompat_test && lint.default_level != Level::Allow, + |incompat| incompat.report_in_deps, + ); + + // Convert lint level to error level. + let err_level = match level { + Level::Allow => { + if has_future_breakage { + rustc_errors::Level::Allow + } else { + return; + } + } + Level::Expect => { + // This case is special as we actually allow the lint itself in this context, but + // 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 + } + Level::ForceWarn => rustc_errors::Level::ForceWarning, + Level::Warn => rustc_errors::Level::Warning, + Level::Deny | Level::Forbid => rustc_errors::Level::Error, + }; + + let disable_suggestions = if let Some(ref span) = span + // If this code originates in a foreign macro, aka something that this crate + // did not itself author, then it's likely that there's nothing this crate + // can do about it. We probably want to skip the lint entirely. + && span.primary_spans().iter().any(|s| s.in_external_macro(sess.source_map())) + { + true + } else { + false + }; + + if disable_suggestions { + // If this is a future incompatible that is not an edition fixing lint + // it'll become a hard error, so we have to emit *something*. Also, + // if this lint occurs in the expansion of a macro from an external crate, + // allow individual lints to opt-out from being reported. + let incompatible = future_incompatible.is_some_and(|f| f.reason.edition().is_none()); + + // In rustc, for the find_attr macro, we want to always emit this. + // This completely circumvents normal lint checking, which usually doesn't happen for macros from other crates. + // However, we kind of want that when using find_attr from another rustc crate. So we cheat a little. + let is_in_find_attr = sess.enable_internal_lints() + && span.as_ref().is_some_and(|span| { + span.primary_spans().iter().any(|s| { + s.source_callee().is_some_and(|i| { + matches!(i.kind, ExpnKind::Macro(_, name) if name.as_str() == "find_attr") + }) + }) + }); + + if !incompatible && !lint.report_in_external_macro && !is_in_find_attr { + // Don't continue further, since we don't want to have + // `diag_span_note_once` called for a diagnostic that isn't emitted. + return; + } + } + // Finally, run `decorate`. `decorate` can call `trimmed_path_str` (directly or indirectly), + // so we need to make sure when we do call `decorate` that the diagnostic is eventually + // 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. + // 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 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. + for primary in span.primary_spans() { + err.span.push_primary_span(*primary); + } + for (label_span, label) in span.span_labels_raw() { + 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 + // emit shouldn't be automatically fixed by rustfix. + err.disable_suggestions(); + } + + err.is_lint(lint.name_lower(), has_future_breakage); + // Lint diagnostics that are covered by the expect level will not be emitted outside + // the compiler. It is therefore not necessary to add any information for the user. + // This will therefore directly call the decorate function which will in turn emit + // the diagnostic. + if let Level::Expect = level { + err.emit(); + return; + } + + if let Some(future_incompatible) = future_incompatible { + let explanation = match future_incompatible.reason { + FutureIncompatibilityReason::FutureReleaseError(_) => { + "this was previously accepted by the compiler but is being phased out; \ + it will become a hard error in a future release!" + .to_owned() + } + FutureIncompatibilityReason::FutureReleaseSemanticsChange(_) => { + "this will change its meaning in a future release!".to_owned() + } + FutureIncompatibilityReason::EditionError(EditionFcw { edition, .. }) => { + let current_edition = sess.edition(); + format!( + "this is accepted in the current edition (Rust {current_edition}) but is a hard error in Rust {edition}!" + ) + } + FutureIncompatibilityReason::EditionSemanticsChange(EditionFcw { + edition, .. + }) => { + format!("this changes meaning in Rust {edition}") + } + FutureIncompatibilityReason::EditionAndFutureReleaseError(EditionFcw { + edition, + .. + }) => { + format!( + "this was previously accepted by the compiler but is being phased out; \ + it will become a hard error in Rust {edition} and in a future release in all editions!" + ) + } + FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange( + EditionFcw { edition, .. }, + ) => { + format!( + "this changes meaning in Rust {edition} and in a future release in all editions!" + ) + } + FutureIncompatibilityReason::Custom(reason, _) => reason.to_owned(), + FutureIncompatibilityReason::Unreachable => unreachable!(), + }; + + if future_incompatible.explain_reason { + err.warn(explanation); + } + + let citation = + format!("for more information, see {}", future_incompatible.reason.reference()); + err.note(citation); + } + + explain_lint_level_source(sess, lint, level, src, &mut err); + err.emit(); + } + diag_lint_level_impl( + sess, + lint, + level, + span, + Box::new(move |dcx, level| decorate.into_diag(dcx, level)), + ); +} diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index e3e04c9d1800..0be4c8243d63 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -8,9 +8,9 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::DefKind; use rustc_macros::HashStable; -use rustc_query_system::ich::StableHashingContext; use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId}; +use crate::ich::StableHashingContext; use crate::ty::{TyCtxt, Visibility}; /// Represents the levels of effective visibility an item can have. diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 4f0073b93eba..628d35814684 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -125,9 +125,14 @@ pub struct Deprecated { pub since_kind: DeprecatedSinceKind, } -impl<'a, G: EmissionGuarantee> rustc_errors::LintDiagnostic<'a, G> for Deprecated { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) { - diag.primary_message(match &self.since_kind { +impl<'a, G: EmissionGuarantee> rustc_errors::Diagnostic<'a, G> for Deprecated { + fn into_diag( + self, + dcx: rustc_errors::DiagCtxtHandle<'a>, + level: rustc_errors::Level, + ) -> Diag<'a, G> { + let Self { sub, kind, path, note, since_kind } = self; + let mut diag = Diag::new(dcx, level, match &since_kind { DeprecatedSinceKind::InEffect => msg!( "use of deprecated {$kind} `{$path}`{$has_note -> [true] : {$note} @@ -148,21 +153,22 @@ fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) { }" ) } - }); - diag.arg("kind", self.kind); - diag.arg("path", self.path); - if let DeprecatedSinceKind::InVersion(version) = self.since_kind { + }) + .with_arg("kind", kind) + .with_arg("path", path); + if let DeprecatedSinceKind::InVersion(version) = since_kind { diag.arg("version", version); } - if let Some(note) = self.note { + if let Some(note) = note { diag.arg("has_note", true); diag.arg("note", note); } else { diag.arg("has_note", false); } - if let Some(sub) = self.sub { + if let Some(sub) = sub { diag.subdiagnostic(sub); } + diag } } diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index afe39e4481ef..de3ef6deca1f 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -181,26 +181,6 @@ pub fn try_get_slice_bytes_for_diagnostics<'tcx>( Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end)) } - /// Check if a constant may contain provenance information. This is used by MIR opts. - /// Can return `true` even if there is no provenance. - pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Size) -> bool { - match *self { - ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false, - ConstValue::Scalar(Scalar::Ptr(..)) => return true, - // It's hard to find out the part of the allocation we point to; - // just conservatively check everything. - ConstValue::Slice { alloc_id, meta: _ } => { - !tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty() - } - ConstValue::Indirect { alloc_id, offset } => !tcx - .global_alloc(alloc_id) - .unwrap_memory() - .inner() - .provenance() - .range_empty(AllocRange::from(offset..offset + size), &tcx), - } - } - /// Check if a constant only contains uninitialized bytes. pub fn all_bytes_uninit(&self, tcx: TyCtxt<'_>) -> bool { let ConstValue::Indirect { alloc_id, .. } = self else { @@ -474,39 +454,6 @@ pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self { let val = ConstValue::Scalar(s); Self::Val(val, ty) } - - /// Return true if any evaluation of this constant always returns the same value, - /// taking into account even pointer identity tests. - pub fn is_deterministic(&self) -> bool { - // Some constants may generate fresh allocations for pointers they contain, - // so using the same constant twice can yield two different results. - // Notably, valtrees purposefully generate new allocations. - match self { - Const::Ty(_, c) => match c.kind() { - ty::ConstKind::Param(..) => true, - // A valtree may be a reference. Valtree references correspond to a - // different allocation each time they are evaluated. Valtrees for primitive - // types are fine though. - ty::ConstKind::Value(cv) => cv.ty.is_primitive(), - ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false, - // This can happen if evaluation of a constant failed. The result does not matter - // much since compilation is doomed. - ty::ConstKind::Error(..) => false, - // Should not appear in runtime MIR. - ty::ConstKind::Infer(..) - | ty::ConstKind::Bound(..) - | ty::ConstKind::Placeholder(..) => bug!(), - }, - Const::Unevaluated(..) => false, - Const::Val( - ConstValue::Slice { .. } - | ConstValue::ZeroSized - | ConstValue::Scalar(_) - | ConstValue::Indirect { .. }, - _, - ) => true, - } - } } /// An unevaluated (potentially generic) constant used in MIR. diff --git a/compiler/rustc_middle/src/mir/graphviz.rs b/compiler/rustc_middle/src/mir/graphviz.rs index cbfeae205177..30a781b025fd 100644 --- a/compiler/rustc_middle/src/mir/graphviz.rs +++ b/compiler/rustc_middle/src/mir/graphviz.rs @@ -16,12 +16,12 @@ pub fn write_mir_graphviz(tcx: TyCtxt<'_>, single: Option, w: &mut W) let mirs = def_ids .iter() - .filter(|def_id| !tcx.is_trivial_const(*def_id)) - .flat_map(|def_id| { - if tcx.is_const_fn(*def_id) { - vec![tcx.optimized_mir(*def_id), tcx.mir_for_ctfe(*def_id)] + .filter(|&&def_id| !tcx.is_trivial_const(def_id)) + .flat_map(|&def_id| { + if tcx.is_const_fn(def_id) { + vec![tcx.optimized_mir(def_id), tcx.mir_for_ctfe(def_id)] } else { - vec![tcx.instance_mir(ty::InstanceKind::Item(*def_id))] + vec![tcx.instance_mir(ty::InstanceKind::Item(def_id))] } }) .collect::>(); diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 1cfe5219997b..7fa818ba7d3a 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -3,6 +3,7 @@ mod init_mask; mod provenance_map; +use std::alloc::{self, Layout}; use std::borrow::Cow; use std::hash::Hash; use std::ops::{Deref, DerefMut, Range}; @@ -434,7 +435,7 @@ fn new_inner( // available to the compiler can change between runs. Normally queries are always // deterministic. However, we can be non-deterministic here because all uses of const // evaluation (including ConstProp!) will make compilation fail (via hard error - // or ICE) upon encountering a `MemoryExhausted` error. + // or OOM) upon encountering a `MemoryExhausted` error. let bytes = Bytes::zeroed(size, align, params).ok_or_else(fail)?; Ok(Allocation { @@ -468,7 +469,7 @@ pub fn try_new<'tcx>( .into() } - /// Try to create an Allocation of `size` bytes, panics if there is not enough memory + /// Try to create an Allocation of `size` bytes. Aborts if there is not enough memory /// available to the compiler to do so. /// /// Example use case: To obtain an Allocation filled with specific data, @@ -480,10 +481,15 @@ pub fn new( params: ::AllocParams, ) -> Self { match Self::new_inner(size, align, init, params, || { - panic!( - "interpreter ran out of memory: cannot create allocation of {} bytes", - size.bytes() - ); + // `size` may actually be bigger than isize::MAX since it is a *target* size. + // Clamp it to isize::MAX to still give a somewhat reasonable error message. + alloc::handle_alloc_error( + Layout::from_size_align( + size.bytes().min(isize::MAX as u64) as usize, + align.bytes_usize(), + ) + .unwrap(), + ) }) { Ok(x) => x, Err(x) => x, diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 66c928f518aa..035ffd362a6b 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -392,6 +392,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { DerefFunctionPointer(AllocId), /// Trying to access the data behind a vtable pointer. DerefVTablePointer(AllocId), + /// Trying to access the data behind a va_list pointer. + DerefVaListPointer(AllocId), /// Trying to access the actual type id. DerefTypeIdPointer(AllocId), /// Using a non-boolean `u8` as bool. @@ -402,6 +404,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { InvalidTag(Scalar), /// Using a pointer-not-to-a-function as function pointer. InvalidFunctionPointer(Pointer), + /// Using a pointer-not-to-a-va-list as variable argument list pointer. + InvalidVaListPointer(Pointer), /// Using a pointer-not-to-a-vtable as vtable pointer. InvalidVTablePointer(Pointer), /// Using a vtable for the wrong trait. @@ -434,6 +438,12 @@ pub enum UndefinedBehaviorInfo<'tcx> { }, /// ABI-incompatible return types. AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> }, + /// `va_arg` was called on an exhausted `VaList`. + VaArgOutOfBounds, + /// The caller and callee disagree on whether they are c-variadic or not. + CVariadicMismatch { caller_is_c_variadic: bool, callee_is_c_variadic: bool }, + /// The caller and callee disagree on the number of fixed (i.e. non-c-variadic) arguments. + CVariadicFixedCountMismatch { caller: u32, callee: u32 }, } #[derive(Debug, Clone, Copy)] diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 0abc3aec7e00..654404f9790c 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -309,7 +309,7 @@ pub fn address_space(&self, cx: &impl HasDataLayout) -> AddressSpace { pub fn mutability(&self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Mutability { // Let's see what kind of memory we are. - match self { + match *self { GlobalAlloc::Static(did) => { let DefKind::Static { safety: _, mutability, nested } = tcx.def_kind(did) else { bug!() @@ -351,7 +351,7 @@ pub fn size_and_align( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ) -> (Size, Align) { - match self { + match *self { GlobalAlloc::Static(def_id) => { let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!("GlobalAlloc::Static is not a static") @@ -396,7 +396,7 @@ pub fn size_and_align( // No data to be accessed here. But vtables are pointer-aligned. (Size::ZERO, tcx.data_layout.pointer_align().abi) } - // Fake allocation, there's nothing to access here + // Fake allocation, there's nothing to access here. GlobalAlloc::TypeId { .. } => (Size::ZERO, Align::ONE), } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 418cdea01660..8ac532ff4863 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -891,7 +891,7 @@ pub struct VarBindingForm<'tcx> { pub introductions: Vec, } -#[derive(Clone, Debug, TyEncodable, TyDecodable)] +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub enum BindingForm<'tcx> { /// This is a binding for a non-`self` binding, or a `self` that has an explicit type. Var(VarBindingForm<'tcx>), @@ -909,24 +909,6 @@ pub struct VarBindingIntroduction { pub is_shorthand: bool, } -mod binding_form_impl { - use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; - use rustc_query_system::ich::StableHashingContext; - - impl<'a, 'tcx> HashStable> for super::BindingForm<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - use super::BindingForm::*; - std::mem::discriminant(self).hash_stable(hcx, hasher); - - match self { - Var(binding) => binding.hash_stable(hcx, hasher), - ImplicitSelf(kind) => kind.hash_stable(hcx, hasher), - RefForGuard(local) => local.hash_stable(hcx, hasher), - } - } - } -} - /// `BlockTailInfo` is attached to the `LocalDecl` for temporaries /// created during evaluation of expressions in a block tail /// expression; that is, a block like `{ STMT_1; STMT_2; EXPR }`. diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 577d226fc9d7..acebf91b1cbf 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -12,7 +12,6 @@ use rustc_hir::attrs::{InlineAttr, Linkage}; use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, LOCAL_CRATE}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; -use rustc_query_system::ich::StableHashingContext; use rustc_session::config::OptLevel; use rustc_span::{Span, Symbol}; use rustc_target::spec::SymbolVisibility; @@ -20,6 +19,7 @@ use crate::dep_graph::dep_node::{make_compile_codegen_unit, make_compile_mono_item}; use crate::dep_graph::{DepNode, WorkProduct, WorkProductId}; +use crate::ich::StableHashingContext; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::ty::{self, GenericArgs, Instance, InstanceKind, SymbolName, Ty, TyCtxt}; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 2e6e96b4d8a8..bf3d595e9943 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -623,7 +623,7 @@ fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io: }; match (kind, body.source.promoted) { (_, Some(_)) => write!(w, "const ")?, // promoteds are the closest to consts - (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?, + (DefKind::Const { .. } | DefKind::AssocConst { .. }, _) => write!(w, "const ")?, (DefKind::Static { safety: _, mutability: hir::Mutability::Not, nested: false }, _) => { write!(w, "static ")? } @@ -802,10 +802,10 @@ fn write_basic_block( } } -impl Debug for Statement<'_> { +impl Debug for StatementKind<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { use self::StatementKind::*; - match self.kind { + match *self { Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"), FakeRead(box (ref cause, ref place)) => { write!(fmt, "FakeRead({cause:?}, {place:?})") @@ -844,6 +844,11 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { } } } +impl Debug for Statement<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + self.kind.fmt(fmt) + } +} impl Debug for StmtDebugInfo<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { @@ -915,6 +920,11 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { } } } +impl Debug for Terminator<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + self.kind.fmt(fmt) + } +} impl<'tcx> TerminatorKind<'tcx> { /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the @@ -1237,10 +1247,6 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { } } - ShallowInitBox(ref place, ref ty) => { - with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})")) - } - WrapUnsafeBinder(ref op, ty) => { with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})")) } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 973ceccc67f9..48dcef298d66 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -432,6 +432,22 @@ pub fn project_deeper(self, more_projections: &[PlaceElem<'tcx>], tcx: TyCtxt<'t self.as_ref().project_deeper(more_projections, tcx) } + /// Return a place that projects to a field of the current place. + /// + /// The type of the current place must be an ADT. + pub fn project_to_field( + self, + idx: FieldIdx, + local_decls: &impl HasLocalDecls<'tcx>, + tcx: TyCtxt<'tcx>, + ) -> Self { + let ty = self.ty(local_decls, tcx).ty; + let ty::Adt(adt, args) = ty.kind() else { panic!("projecting to field of non-ADT {ty}") }; + let field = &adt.non_enum_variant().fields[idx]; + let field_ty = field.ty(tcx, args); + self.project_deeper(&[ProjectionElem::Field(idx, field_ty)], tcx) + } + pub fn ty_from( local: Local, projection: &[PlaceElem<'tcx>], @@ -731,11 +747,6 @@ pub fn ty(&self) -> Ty<'tcx> { /////////////////////////////////////////////////////////////////////////// // Rvalues -pub enum RvalueInitializationState { - Shallow, - Deep, -} - impl<'tcx> Rvalue<'tcx> { /// Returns true if rvalue can be safely removed when the result is unused. #[inline] @@ -770,7 +781,6 @@ pub fn is_safe_to_remove(&self) -> bool { | Rvalue::UnaryOp(_, _) | Rvalue::Discriminant(_) | Rvalue::Aggregate(_, _) - | Rvalue::ShallowInitBox(_, _) | Rvalue::WrapUnsafeBinder(_, _) => true, } } @@ -817,21 +827,10 @@ pub fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> } AggregateKind::RawPtr(ty, mutability) => Ty::new_ptr(tcx, ty, mutability), }, - Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty), Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty, Rvalue::WrapUnsafeBinder(_, ty) => ty, } } - - #[inline] - /// Returns `true` if this rvalue is deeply initialized (most rvalues) or - /// whether its only shallowly initialized (`Rvalue::Box`). - pub fn initialization_state(&self) -> RvalueInitializationState { - match *self { - Rvalue::ShallowInitBox(_, _) => RvalueInitializationState::Shallow, - _ => RvalueInitializationState::Deep, - } - } } impl BorrowKind { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 6ec874fb15f7..c3da2f039485 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -306,7 +306,7 @@ pub enum FakeBorrowKind { /// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which /// ones you do not have to worry about. The MIR validator will generally enforce such restrictions, /// causing an ICE if they are violated. -#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] #[derive(TypeFoldable, TypeVisitable)] pub enum StatementKind<'tcx> { /// Assign statements roughly correspond to an assignment in Rust proper (`x = ...`) except @@ -1458,13 +1458,6 @@ pub enum Rvalue<'tcx> { /// coroutine lowering, `Coroutine` aggregate kinds are disallowed too. Aggregate(Box>, IndexVec>), - /// Transmutes a `*mut u8` into shallow-initialized `Box`. - /// - /// This is different from a normal transmute because dataflow analysis will treat the box as - /// initialized but its content as uninitialized. Like other pointer casts, this in general - /// affects alias analysis. - ShallowInitBox(Operand<'tcx>, Ty<'tcx>), - /// A CopyForDeref is equivalent to a read from a place at the /// codegen level, but is treated specially by drop elaboration. When such a read happens, it /// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator) diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 1e7729bd8d65..650c44aa41fb 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -444,7 +444,7 @@ macro_rules! add { } } -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct Terminator<'tcx> { pub source_info: SourceInfo, pub kind: TerminatorKind<'tcx>, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 07a36aef4320..16a8743a6d67 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -810,11 +810,6 @@ fn super_rvalue( } } - Rvalue::ShallowInitBox(operand, ty) => { - self.visit_operand(operand, location); - self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); - } - Rvalue::WrapUnsafeBinder(op, ty) => { self.visit_operand(op, location); self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 59171f2da4f2..2636fc7024ca 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -25,10 +25,13 @@ //! Query modifiers are special flags that alter the behavior of a query. They are parsed and processed by the `rustc_macros` //! The main modifiers are: //! -//! - `desc { ... }`: Sets the human-readable description for diagnostics and profiling. Required for every query. +//! - `desc { ... }`: Sets the human-readable description for diagnostics and profiling. Required +//! for every query. The block should contain a `format!`-style string literal followed by +//! optional arguments. The query key identifier is available for use within the block, as is +//! `tcx`. //! - `arena_cache`: Use an arena for in-memory caching of the query result. -//! - `cache_on_disk_if { ... }`: Cache the query result to disk if the provided block evaluates to true. -//! - `cycle_fatal`: If a dependency cycle is detected, abort compilation with a fatal error. +//! - `cache_on_disk_if { ... }`: Cache the query result to disk if the provided block evaluates to +//! true. The query key identifier is available for use within the block, as is `tcx`. //! - `cycle_delay_bug`: If a dependency cycle is detected, emit a delayed bug instead of aborting immediately. //! - `cycle_stash`: If a dependency cycle is detected, stash the error for later handling. //! - `no_hash`: Do not hash the query result for incremental compilation; just mark as dirty if recomputed. @@ -37,9 +40,6 @@ //! - `depth_limit`: Impose a recursion depth limit on the query to prevent stack overflows. //! - `separate_provide_extern`: Use separate provider functions for local and external crates. //! - `feedable`: Allow the query result to be set from another query ("fed" externally). -//! - `return_result_from_ensure_ok`: When called via `tcx.ensure_ok()`, return `Result<(), ErrorGuaranteed>` instead of `()`. -//! If the query needs to be executed and returns an error, the error is returned to the caller. -//! Only valid for queries returning `Result<_, ErrorGuaranteed>`. //! //! For the up-to-date list, see the `QueryModifiers` struct in //! [`rustc_macros/src/query.rs`](https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_macros/src/query.rs) @@ -66,8 +66,10 @@ use std::path::PathBuf; use std::sync::Arc; +use rustc_abi as abi; use rustc_abi::Align; use rustc_arena::TypedArena; +use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::tokenstream::TokenStream; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -76,6 +78,7 @@ use rustc_data_structures::svh::Svh; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::ErrorGuaranteed; +use rustc_hir as hir; use rustc_hir::attrs::{EiiDecl, EiiImpl, StrippedCfgItem}; use rustc_hir::def::{DefKind, DocLinkResMap}; use rustc_hir::def_id::{ @@ -96,7 +99,6 @@ use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, LocalExpnId, Span, Symbol}; use rustc_target::spec::PanicStrategy; -use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; @@ -145,11 +147,11 @@ // which memoizes and does dep-graph tracking, wrapping around the actual // `Providers` that the driver creates (using several `rustc_*` crates). // -// The result type of each query must implement `Clone`, and additionally -// `ty::query::values::Value`, which produces an appropriate placeholder -// (error) value if the query resulted in a query cycle. -// Queries marked with `cycle_fatal` do not need the latter implementation, -// as they will raise an fatal error on query cycles instead. +// The result type of each query must implement `Clone`. Additionally +// `ty::query::from_cycle_error::FromCycleError` can be implemented which produces an appropriate +// placeholder (error) value if the query resulted in a query cycle. +// Queries without a `FromCycleError` implementation will raise a fatal error on query +// cycles instead. rustc_queries! { /// Caches the expansion of a derive proc macro, e.g. `#[derive(Serialize)]`. /// The key is: @@ -196,7 +198,7 @@ desc { "getting the resolver outputs" } } - query resolver_for_lowering_raw(_: ()) -> (&'tcx Steal<(ty::ResolverAstLowering, Arc)>, &'tcx ty::ResolverGlobalCtxt) { + query resolver_for_lowering_raw(_: ()) -> (&'tcx Steal<(ty::ResolverAstLowering<'tcx>, Arc)>, &'tcx ty::ResolverGlobalCtxt) { eval_always no_hash desc { "getting the resolver for lowering" } @@ -239,13 +241,13 @@ /// Avoid calling this query directly. query hir_module_items(key: LocalModDefId) -> &'tcx rustc_middle::hir::ModuleItems { arena_cache - desc { |tcx| "getting HIR module items in `{}`", tcx.def_path_str(key) } + desc { "getting HIR module items in `{}`", tcx.def_path_str(key) } cache_on_disk_if { true } } /// Returns HIR ID for the given `LocalDefId`. query local_def_id_to_hir_id(key: LocalDefId) -> hir::HirId { - desc { |tcx| "getting HIR ID of `{}`", tcx.def_path_str(key) } + desc { "getting HIR ID of `{}`", tcx.def_path_str(key) } feedable } @@ -254,7 +256,7 @@ /// This can be conveniently accessed by `tcx.hir_*` methods. /// Avoid calling this query directly. query hir_owner_parent_q(key: hir::OwnerId) -> hir::HirId { - desc { |tcx| "getting HIR parent of `{}`", tcx.def_path_str(key) } + desc { "getting HIR parent of `{}`", tcx.def_path_str(key) } } /// Gives access to the HIR nodes and bodies inside `key` if it's a HIR owner. @@ -262,7 +264,7 @@ /// This can be conveniently accessed by `tcx.hir_*` methods. /// Avoid calling this query directly. query opt_hir_owner_nodes(key: LocalDefId) -> Option<&'tcx hir::OwnerNodes<'tcx>> { - desc { |tcx| "getting HIR owner items in `{}`", tcx.def_path_str(key) } + desc { "getting HIR owner items in `{}`", tcx.def_path_str(key) } feedable } @@ -271,7 +273,7 @@ /// This can be conveniently accessed by `tcx.hir_*` methods. /// Avoid calling this query directly. query hir_attr_map(key: hir::OwnerId) -> &'tcx hir::AttributeMap<'tcx> { - desc { |tcx| "getting HIR owner attributes in `{}`", tcx.def_path_str(key) } + desc { "getting HIR owner attributes in `{}`", tcx.def_path_str(key) } feedable } @@ -280,14 +282,14 @@ /// This can be conveniently accessed by `tcx.hir_*` methods. /// Avoid calling this query directly. query opt_ast_lowering_delayed_lints(key: hir::OwnerId) -> Option<&'tcx hir::lints::DelayedLints> { - desc { |tcx| "getting AST lowering delayed lints in `{}`", tcx.def_path_str(key) } + desc { "getting AST lowering delayed lints in `{}`", tcx.def_path_str(key) } } /// Returns the *default* of the const pararameter given by `DefId`. /// /// E.g., given `struct Ty;` this returns `3` for `N`. query const_param_default(param: DefId) -> ty::EarlyBinder<'tcx, ty::Const<'tcx>> { - desc { |tcx| "computing the default for const parameter `{}`", tcx.def_path_str(param) } + desc { "computing the default for const parameter `{}`", tcx.def_path_str(param) } cache_on_disk_if { param.is_local() } separate_provide_extern } @@ -300,7 +302,7 @@ /// /// This query will ICE if given a const that is not marked with `type const`. query const_of_item(def_id: DefId) -> ty::EarlyBinder<'tcx, ty::Const<'tcx>> { - desc { |tcx| "computing the type-level value for `{}`", tcx.def_path_str(def_id) } + desc { "computing the type-level value for `{}`", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern } @@ -324,7 +326,7 @@ /// /// [alias type]: rustc_middle::ty::AliasTy query type_of(key: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { - desc { |tcx| + desc { "{action} `{path}`", action = match tcx.def_kind(key) { DefKind::TyAlias => "expanding type alias", @@ -349,14 +351,14 @@ /// /// This query will panic if the given definition is not an opaque type. query type_of_opaque(key: DefId) -> Result>, CyclePlaceholder> { - desc { |tcx| + desc { "computing type of opaque `{path}`", path = tcx.def_path_str(key), } cycle_stash } query type_of_opaque_hir_typeck(key: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { - desc { |tcx| + desc { "computing type of opaque `{path}` via HIR typeck", path = tcx.def_path_str(key), } @@ -376,7 +378,7 @@ /// [free]: rustc_middle::ty::Free /// [alias type]: rustc_middle::ty::AliasTy query type_alias_is_lazy(key: DefId) -> bool { - desc { |tcx| + desc { "computing whether the type alias `{path}` is lazy", path = tcx.def_path_str(key), } @@ -400,7 +402,7 @@ query unsizing_params_for_adt(key: DefId) -> &'tcx rustc_index::bit_set::DenseBitSet { arena_cache - desc { |tcx| + desc { "determining what parameters of `{}` can participate in unsizing", tcx.def_path_str(key), } @@ -409,7 +411,7 @@ /// The root query triggering all analysis passes like typeck or borrowck. query analysis(key: ()) { eval_always - desc { |tcx| + desc { "running analysis passes on crate `{}`", tcx.crate_name(LOCAL_CRATE), } @@ -436,7 +438,7 @@ /// Returns the *generics* of the definition given by `DefId`. query generics_of(key: DefId) -> &'tcx ty::Generics { - desc { |tcx| "computing generics of `{}`", tcx.def_path_str(key) } + desc { "computing generics of `{}`", tcx.def_path_str(key) } arena_cache cache_on_disk_if { key.is_local() } separate_provide_extern @@ -451,15 +453,14 @@ /// **Tip**: You can use `#[rustc_dump_predicates]` on an item to basically print /// the result of this query for use in UI tests or for debugging purposes. query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } + desc { "computing predicates of `{}`", tcx.def_path_str(key) } } query opaque_types_defined_by( key: LocalDefId ) -> &'tcx ty::List { desc { - |tcx| "computing the opaque types defined by `{}`", + "computing the opaque types defined by `{}`", tcx.def_path_str(key.to_def_id()) } } @@ -470,7 +471,7 @@ key: LocalDefId ) -> &'tcx ty::List { desc { - |tcx| "computing the coroutines defined within `{}`", + "computing the coroutines defined within `{}`", tcx.def_path_str(key.to_def_id()) } } @@ -494,7 +495,7 @@ /// // ^^^^^^^^^^^^^^^ /// ``` query explicit_item_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } + desc { "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern feedable @@ -507,7 +508,7 @@ /// /// [explicit item bounds]: Self::explicit_item_bounds query explicit_item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } + desc { "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern feedable @@ -537,19 +538,19 @@ /// ] /// ``` query item_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { - desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } + desc { "elaborating item bounds for `{}`", tcx.def_path_str(key) } } query item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { - desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } + desc { "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } query item_non_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { - desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } + desc { "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } query impl_super_outlives(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { - desc { |tcx| "elaborating supertrait outlives for trait of `{}`", tcx.def_path_str(key) } + desc { "elaborating supertrait outlives for trait of `{}`", tcx.def_path_str(key) } } /// Look up all native libraries this crate depends on. @@ -564,7 +565,7 @@ query shallow_lint_levels_on(key: hir::OwnerId) -> &'tcx rustc_middle::lint::ShallowLintLevelMap { arena_cache - desc { |tcx| "looking up lint levels for `{}`", tcx.def_path_str(key) } + desc { "looking up lint levels for `{}`", tcx.def_path_str(key) } } query lint_expectations(_: ()) -> &'tcx Vec<(LintExpectationId, LintExpectation)> { @@ -578,29 +579,33 @@ } query expn_that_defined(key: DefId) -> rustc_span::ExpnId { - desc { |tcx| "getting the expansion that defined `{}`", tcx.def_path_str(key) } + desc { "getting the expansion that defined `{}`", tcx.def_path_str(key) } separate_provide_extern } query is_panic_runtime(_: CrateNum) -> bool { - cycle_fatal desc { "checking if the crate is_panic_runtime" } separate_provide_extern } /// Checks whether a type is representable or infinitely sized - query representability(_: LocalDefId) -> rustc_middle::ty::Representability { + query check_representability(key: LocalDefId) -> rustc_middle::ty::Representability { desc { "checking if `{}` is representable", tcx.def_path_str(key) } - // infinitely sized types will cause a cycle + // Infinitely sized types will cause a cycle. The custom `FromCycleError` impl for + // `Representability` will print a custom error about the infinite size and then abort + // compilation. (In the past we recovered and continued, but in practice that leads to + // confusing subsequent error messages about cycles that then abort.) cycle_delay_bug - // we don't want recursive representability calls to be forced with + // We don't want recursive representability calls to be forced with // incremental compilation because, if a cycle occurs, we need the - // entire cycle to be in memory for diagnostics + // entire cycle to be in memory for diagnostics. This means we can't + // use `ensure_ok()` with this query. anon } - /// An implementation detail for the `representability` query - query representability_adt_ty(_: Ty<'tcx>) -> rustc_middle::ty::Representability { + /// An implementation detail for the `check_representability` query. See that query for more + /// details, particularly on the modifiers. + query check_representability_adt_ty(key: Ty<'tcx>) -> rustc_middle::ty::Representability { desc { "checking if `{}` is representable", key } cycle_delay_bug anon @@ -619,7 +624,7 @@ query thir_body(key: LocalDefId) -> Result<(&'tcx Steal>, thir::ExprId), ErrorGuaranteed> { // Perf tests revealed that hashing THIR is inefficient (see #85729). no_hash - desc { |tcx| "building THIR for `{}`", tcx.def_path_str(key) } + desc { "building THIR for `{}`", tcx.def_path_str(key) } } /// Set of all the `DefId`s in this crate that have MIR associated with @@ -634,7 +639,7 @@ /// of the MIR const-checking pass. This is the set of qualifs in /// the final value of a `const`. query mir_const_qualif(key: DefId) -> mir::ConstQualifs { - desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } + desc { "const checking `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -645,7 +650,7 @@ /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/construction.html query mir_built(key: LocalDefId) -> &'tcx Steal> { - desc { |tcx| "building MIR for `{}`", tcx.def_path_str(key) } + desc { "building MIR for `{}`", tcx.def_path_str(key) } feedable } @@ -654,20 +659,20 @@ key: DefId ) -> Result>>, ErrorGuaranteed> { desc { - |tcx| "building an abstract representation for `{}`", tcx.def_path_str(key), + "building an abstract representation for `{}`", tcx.def_path_str(key), } separate_provide_extern } query mir_drops_elaborated_and_const_checked(key: LocalDefId) -> &'tcx Steal> { no_hash - desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key) } + desc { "elaborating drops for `{}`", tcx.def_path_str(key) } } query mir_for_ctfe( key: DefId ) -> &'tcx mir::Body<'tcx> { - desc { |tcx| "caching mir of `{}` for CTFE", tcx.def_path_str(key) } + desc { "caching mir of `{}` for CTFE", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -677,12 +682,12 @@ &'tcx Steal>> ) { no_hash - desc { |tcx| "promoting constants in MIR for `{}`", tcx.def_path_str(key) } + desc { "promoting constants in MIR for `{}`", tcx.def_path_str(key) } } query closure_typeinfo(key: LocalDefId) -> ty::ClosureTypeInfo<'tcx> { desc { - |tcx| "finding symbols for captures of closure `{}`", + "finding symbols for captures of closure `{}`", tcx.def_path_str(key) } } @@ -696,20 +701,19 @@ /// For coroutines this only contains upvars that are shared by all states. query closure_saved_names_of_captured_variables(def_id: DefId) -> &'tcx IndexVec { arena_cache - desc { |tcx| "computing debuginfo for closure `{}`", tcx.def_path_str(def_id) } + desc { "computing debuginfo for closure `{}`", tcx.def_path_str(def_id) } separate_provide_extern } query mir_coroutine_witnesses(key: DefId) -> Option<&'tcx mir::CoroutineLayout<'tcx>> { arena_cache - desc { |tcx| "coroutine witness types for `{}`", tcx.def_path_str(key) } + desc { "coroutine witness types for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } query check_coroutine_obligations(key: LocalDefId) -> Result<(), ErrorGuaranteed> { - desc { |tcx| "verify auto trait bounds for coroutine interior type `{}`", tcx.def_path_str(key) } - return_result_from_ensure_ok + desc { "verify auto trait bounds for coroutine interior type `{}`", tcx.def_path_str(key) } } /// Used in case `mir_borrowck` fails to prove an obligation. We generally assume that @@ -723,7 +727,7 @@ /// delay a bug if it does not. query check_potentially_region_dependent_goals(key: LocalDefId) -> Result<(), ErrorGuaranteed> { desc { - |tcx| "reproving potentially region dependent HIR typeck goals for `{}", + "reproving potentially region dependent HIR typeck goals for `{}", tcx.def_path_str(key) } } @@ -731,7 +735,7 @@ /// MIR after our optimization passes have run. This is MIR that is ready /// for codegen. This is also the only query that can fetch non-local MIR, at present. query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> { - desc { |tcx| "optimizing MIR for `{}`", tcx.def_path_str(key) } + desc { "optimizing MIR for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -742,7 +746,7 @@ /// Returns `false` if `#[coverage(off)]` was found, or `true` if /// either `#[coverage(on)]` or no coverage attribute was found. query coverage_attr_on(key: LocalDefId) -> bool { - desc { |tcx| "checking for `#[coverage(..)]` on `{}`", tcx.def_path_str(key) } + desc { "checking for `#[coverage(..)]` on `{}`", tcx.def_path_str(key) } feedable } @@ -759,7 +763,7 @@ /// /// Returns `None` for functions that were not instrumented. query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> Option<&'tcx mir::coverage::CoverageIdsInfo> { - desc { |tcx| "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) } + desc { "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) } arena_cache } @@ -769,7 +773,7 @@ /// after inlining a body may refer to promoteds from other bodies. In that case you still /// need to use the `DefId` of the original body. query promoted_mir(key: DefId) -> &'tcx IndexVec> { - desc { |tcx| "optimizing promoted MIR for `{}`", tcx.def_path_str(key) } + desc { "optimizing promoted MIR for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -815,16 +819,16 @@ /// /// This query will panic if the given definition is not a trait. query trait_explicit_predicates_and_bounds(key: LocalDefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing explicit predicates of trait `{}`", tcx.def_path_str(key) } + desc { "computing explicit predicates of trait `{}`", tcx.def_path_str(key) } } /// Returns the explicitly user-written *predicates* of the definition given by `DefId` /// that must be proven true at usage sites (and which can be assumed at definition site). /// - /// You should probably use [`Self::predicates_of`] unless you're looking for + /// You should probably use [`TyCtxt::predicates_of`] unless you're looking for /// predicates with explicit spans for diagnostics purposes. query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } + desc { "computing explicit predicates of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern feedable @@ -834,10 +838,10 @@ /// /// E.g., for `struct Foo<'a, T> { x: &'a T }`, this would return `[T: 'a]`. /// - /// **Tip**: You can use `#[rustc_outlives]` on an item to basically print the - /// result of this query for use in UI tests or for debugging purposes. + /// **Tip**: You can use `#[rustc_dump_inferred_outlives]` on an item to basically + /// print the result of this query for use in UI tests or for debugging purposes. query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Clause<'tcx>, Span)] { - desc { |tcx| "computing inferred outlives-predicates of `{}`", tcx.def_path_str(key) } + desc { "computing inferred outlives-predicates of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern feedable @@ -851,7 +855,7 @@ /// because we must evaluate them even during type conversion, often before the full /// predicates are available (note that super-predicates must not be cyclic). query explicit_super_predicates_of(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - desc { |tcx| "computing the super predicates of `{}`", tcx.def_path_str(key) } + desc { "computing the super predicates of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -863,7 +867,7 @@ /// associated type bounds. For trait aliases, currently, this includes all of the /// predicates of the trait alias. query explicit_implied_predicates_of(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - desc { |tcx| "computing the implied predicates of `{}`", tcx.def_path_str(key) } + desc { "computing the implied predicates of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -874,7 +878,7 @@ query explicit_supertraits_containing_assoc_item( key: (DefId, rustc_span::Ident) ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - desc { |tcx| "computing the super traits of `{}` with associated type name `{}`", + desc { "computing the super traits of `{}` with associated type name `{}`", tcx.def_path_str(key.0), key.1 } @@ -892,7 +896,7 @@ query const_conditions( key: DefId ) -> ty::ConstConditions<'tcx> { - desc { |tcx| "computing the conditions for `{}` to be considered const", + desc { "computing the conditions for `{}` to be considered const", tcx.def_path_str(key) } separate_provide_extern @@ -906,7 +910,7 @@ query explicit_implied_const_bounds( key: DefId ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::PolyTraitRef<'tcx>, Span)]> { - desc { |tcx| "computing the implied `[const]` bounds for `{}`", + desc { "computing the implied `[const]` bounds for `{}`", tcx.def_path_str(key) } separate_provide_extern @@ -917,40 +921,40 @@ query type_param_predicates( key: (LocalDefId, LocalDefId, rustc_span::Ident) ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - desc { |tcx| "computing the bounds for type parameter `{}`", tcx.hir_ty_param_name(key.1) } + desc { "computing the bounds for type parameter `{}`", tcx.hir_ty_param_name(key.1) } } query trait_def(key: DefId) -> &'tcx ty::TraitDef { - desc { |tcx| "computing trait definition for `{}`", tcx.def_path_str(key) } + desc { "computing trait definition for `{}`", tcx.def_path_str(key) } arena_cache cache_on_disk_if { key.is_local() } separate_provide_extern } query adt_def(key: DefId) -> ty::AdtDef<'tcx> { - desc { |tcx| "computing ADT definition for `{}`", tcx.def_path_str(key) } + desc { "computing ADT definition for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } query adt_destructor(key: DefId) -> Option { - desc { |tcx| "computing `Drop` impl for `{}`", tcx.def_path_str(key) } + desc { "computing `Drop` impl for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } query adt_async_destructor(key: DefId) -> Option { - desc { |tcx| "computing `AsyncDrop` impl for `{}`", tcx.def_path_str(key) } + desc { "computing `AsyncDrop` impl for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } query adt_sizedness_constraint( key: (DefId, SizedTraitKind) ) -> Option>> { - desc { |tcx| "computing the sizedness constraint for `{}`", tcx.def_path_str(key.0) } + desc { "computing the sizedness constraint for `{}`", tcx.def_path_str(key.0) } } query adt_dtorck_constraint( key: DefId ) -> &'tcx DropckConstraint<'tcx> { - desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) } + desc { "computing drop-check constraints for `{}`", tcx.def_path_str(key) } } /// Returns the constness of the function-like[^1] definition given by `DefId`. @@ -975,13 +979,13 @@ /// /// [^1]: Tuple struct/variant constructors, closures and free, associated and foreign functions. query constness(key: DefId) -> hir::Constness { - desc { |tcx| "checking if item is const: `{}`", tcx.def_path_str(key) } + desc { "checking if item is const: `{}`", tcx.def_path_str(key) } separate_provide_extern feedable } query asyncness(key: DefId) -> ty::Asyncness { - desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } + desc { "checking if the function is async: `{}`", tcx.def_path_str(key) } separate_provide_extern } @@ -993,7 +997,7 @@ /// function does not inspect the bits of any of its arguments (so is essentially just a /// constructor function). query is_promotable_const_fn(key: DefId) -> bool { - desc { |tcx| "checking if item is promotable: `{}`", tcx.def_path_str(key) } + desc { "checking if item is promotable: `{}`", tcx.def_path_str(key) } } /// The body of the coroutine, modified to take its upvars by move rather than by ref. @@ -1003,19 +1007,19 @@ /// is run right after building the initial MIR, and will only be populated for coroutines /// which come out of the async closure desugaring. query coroutine_by_move_body_def_id(def_id: DefId) -> DefId { - desc { |tcx| "looking up the coroutine by-move body for `{}`", tcx.def_path_str(def_id) } + desc { "looking up the coroutine by-move body for `{}`", tcx.def_path_str(def_id) } separate_provide_extern } /// Returns `Some(coroutine_kind)` if the node pointed to by `def_id` is a coroutine. query coroutine_kind(def_id: DefId) -> Option { - desc { |tcx| "looking up coroutine kind of `{}`", tcx.def_path_str(def_id) } + desc { "looking up coroutine kind of `{}`", tcx.def_path_str(def_id) } separate_provide_extern feedable } query coroutine_for_closure(def_id: DefId) -> DefId { - desc { |_tcx| "Given a coroutine-closure def id, return the def id of the coroutine returned by it" } + desc { "Given a coroutine-closure def id, return the def id of the coroutine returned by it" } separate_provide_extern } @@ -1042,10 +1046,10 @@ /// The list of variances corresponds to the list of (early-bound) generic /// parameters of the item (including its parents). /// - /// **Tip**: You can use `#[rustc_variance]` on an item to basically print the - /// result of this query for use in UI tests or for debugging purposes. + /// **Tip**: You can use `#[rustc_dump_variances]` on an item to basically print + /// the result of this query for use in UI tests or for debugging purposes. query variances_of(def_id: DefId) -> &'tcx [ty::Variance] { - desc { |tcx| "computing the variances of `{}`", tcx.def_path_str(def_id) } + desc { "computing the variances of `{}`", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern cycle_delay_bug @@ -1066,14 +1070,14 @@ /// Maps from an impl/trait or struct/variant `DefId` /// to a list of the `DefId`s of its associated items or fields. query associated_item_def_ids(key: DefId) -> &'tcx [DefId] { - desc { |tcx| "collecting associated items or fields of `{}`", tcx.def_path_str(key) } + desc { "collecting associated items or fields of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } /// Maps from a trait/impl item to the trait/impl item "descriptor". query associated_item(key: DefId) -> ty::AssocItem { - desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) } + desc { "computing associated item data for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern feedable @@ -1082,7 +1086,7 @@ /// Collects the associated items defined on a trait or impl. query associated_items(key: DefId) -> &'tcx ty::AssocItems { arena_cache - desc { |tcx| "collecting associated items of `{}`", tcx.def_path_str(key) } + desc { "collecting associated items of `{}`", tcx.def_path_str(key) } } /// Maps from associated items on a trait to the corresponding associated @@ -1108,20 +1112,20 @@ ///`{ trait_f: impl_f, trait_g: impl_g }` query impl_item_implementor_ids(impl_id: DefId) -> &'tcx DefIdMap { arena_cache - desc { |tcx| "comparing impl items against trait for `{}`", tcx.def_path_str(impl_id) } + desc { "comparing impl items against trait for `{}`", tcx.def_path_str(impl_id) } } /// Given the `item_def_id` of a trait or impl, return a mapping from associated fn def id /// to its associated type items that correspond to the RPITITs in its signature. query associated_types_for_impl_traits_in_trait_or_impl(item_def_id: DefId) -> &'tcx DefIdMap> { arena_cache - desc { |tcx| "synthesizing RPITIT items for the opaque types for methods in `{}`", tcx.def_path_str(item_def_id) } + desc { "synthesizing RPITIT items for the opaque types for methods in `{}`", tcx.def_path_str(item_def_id) } separate_provide_extern } /// Given an `impl_id`, return the trait it implements along with some header information. query impl_trait_header(impl_id: DefId) -> ty::ImplTraitHeader<'tcx> { - desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(impl_id) } + desc { "computing trait implemented by `{}`", tcx.def_path_str(impl_id) } cache_on_disk_if { impl_id.is_local() } separate_provide_extern } @@ -1130,36 +1134,35 @@ /// to either being one of the built-in unsized types (str/slice/dyn) or to be a struct /// whose tail is one of those types. query impl_self_is_guaranteed_unsized(impl_def_id: DefId) -> bool { - desc { |tcx| "computing whether `{}` has a guaranteed unsized self type", tcx.def_path_str(impl_def_id) } + desc { "computing whether `{}` has a guaranteed unsized self type", tcx.def_path_str(impl_def_id) } } /// Maps a `DefId` of a type to a list of its inherent impls. /// Contains implementations of methods that are inherent to a type. /// Methods in these implementations don't need to be exported. query inherent_impls(key: DefId) -> &'tcx [DefId] { - desc { |tcx| "collecting inherent impls for `{}`", tcx.def_path_str(key) } + desc { "collecting inherent impls for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } query incoherent_impls(key: SimplifiedType) -> &'tcx [DefId] { - desc { |tcx| "collecting all inherent impls for `{:?}`", key } + desc { "collecting all inherent impls for `{:?}`", key } } /// Unsafety-check this `LocalDefId`. query check_transmutes(key: LocalDefId) { - desc { |tcx| "check transmute calls inside `{}`", tcx.def_path_str(key) } + desc { "check transmute calls inside `{}`", tcx.def_path_str(key) } } /// Unsafety-check this `LocalDefId`. query check_unsafety(key: LocalDefId) { - desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } + desc { "unsafety-checking `{}`", tcx.def_path_str(key) } } /// Checks well-formedness of tail calls (`become f()`). - query check_tail_calls(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { - desc { |tcx| "tail-call-checking `{}`", tcx.def_path_str(key) } - return_result_from_ensure_ok + query check_tail_calls(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { "tail-call-checking `{}`", tcx.def_path_str(key) } } /// Returns the types assumed to be well formed while "inside" of the given item. @@ -1167,19 +1170,19 @@ /// Note that we've liberated the late bound regions of function signatures, so /// this can not be used to check whether these types are well formed. query assumed_wf_types(key: LocalDefId) -> &'tcx [(Ty<'tcx>, Span)] { - desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) } + desc { "computing the implied bounds of `{}`", tcx.def_path_str(key) } } /// We need to store the assumed_wf_types for an RPITIT so that impls of foreign /// traits with return-position impl trait in traits can inherit the right wf types. query assumed_wf_types_for_rpitit(key: DefId) -> &'tcx [(Ty<'tcx>, Span)] { - desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) } + desc { "computing the implied bounds of `{}`", tcx.def_path_str(key) } separate_provide_extern } /// Computes the signature of the function. query fn_sig(key: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { - desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) } + desc { "computing function signature of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern cycle_delay_bug @@ -1187,7 +1190,7 @@ /// Performs lint checking for the module. query lint_mod(key: LocalModDefId) { - desc { |tcx| "linting {}", describe_as_module(key, tcx) } + desc { "linting {}", describe_as_module(key, tcx) } } query check_unused_traits(_: ()) { @@ -1196,22 +1199,22 @@ /// Checks the attributes in the module. query check_mod_attrs(key: LocalModDefId) { - desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) } + desc { "checking attributes in {}", describe_as_module(key, tcx) } } /// Checks for uses of unstable APIs in the module. query check_mod_unstable_api_usage(key: LocalModDefId) { - desc { |tcx| "checking for unstable API usage in {}", describe_as_module(key, tcx) } + desc { "checking for unstable API usage in {}", describe_as_module(key, tcx) } } query check_mod_privacy(key: LocalModDefId) { - desc { |tcx| "checking privacy in {}", describe_as_module(key.to_local_def_id(), tcx) } + desc { "checking privacy in {}", describe_as_module(key.to_local_def_id(), tcx) } } query check_liveness(key: LocalDefId) -> &'tcx rustc_index::bit_set::DenseBitSet { arena_cache - desc { |tcx| "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) } + desc { "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { tcx.is_typeck_child(key.to_def_id()) } } /// Return the live symbols in the crate for dead code check. @@ -1226,35 +1229,32 @@ } query check_mod_deathness(key: LocalModDefId) { - desc { |tcx| "checking deathness of variables in {}", describe_as_module(key, tcx) } + desc { "checking deathness of variables in {}", describe_as_module(key, tcx) } } query check_type_wf(key: ()) -> Result<(), ErrorGuaranteed> { desc { "checking that types are well-formed" } - return_result_from_ensure_ok } /// Caches `CoerceUnsized` kinds for impls on custom types. query coerce_unsized_info(key: DefId) -> Result { - desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } + desc { "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern - return_result_from_ensure_ok } query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { - desc { |tcx| "type-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if(tcx) { !tcx.is_typeck_child(key.to_def_id()) } + desc { "type-checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if { !tcx.is_typeck_child(key.to_def_id()) } } query used_trait_imports(key: LocalDefId) -> &'tcx UnordSet { - desc { |tcx| "finding used_trait_imports `{}`", tcx.def_path_str(key) } + desc { "finding used_trait_imports `{}`", tcx.def_path_str(key) } cache_on_disk_if { true } } query coherent_trait(def_id: DefId) -> Result<(), ErrorGuaranteed> { - desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } - return_result_from_ensure_ok + desc { "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } } /// Borrow-checks the given typeck root, e.g. functions, const/static items, @@ -1263,7 +1263,7 @@ &'tcx FxIndexMap>, ErrorGuaranteed > { - desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key) } + desc { "borrow-checking `{}`", tcx.def_path_str(key) } } /// Gets a complete map from all types to their inherent impls. @@ -1286,7 +1286,6 @@ /// query crate_inherent_impls_validity_check(_: ()) -> Result<(), ErrorGuaranteed> { desc { "check for inherent impls that should not be defined in crate" } - return_result_from_ensure_ok } /// Checks all types in the crate for overlap in their inherent impls. Reports errors. @@ -1298,25 +1297,22 @@ /// query crate_inherent_impls_overlap_check(_: ()) -> Result<(), ErrorGuaranteed> { desc { "check for overlap between inherent impls defined in this crate" } - return_result_from_ensure_ok } /// Checks whether all impls in the crate pass the overlap check, returning /// which impls fail it. If all impls are correct, the returned slice is empty. query orphan_check_impl(key: LocalDefId) -> Result<(), ErrorGuaranteed> { - desc { |tcx| + desc { "checking whether impl `{}` follows the orphan rules", tcx.def_path_str(key), } - return_result_from_ensure_ok } /// Return the set of (transitive) callees that may result in a recursive call to `key`, /// if we were able to walk all callees. query mir_callgraph_cyclic(key: LocalDefId) -> &'tcx Option> { - cycle_fatal arena_cache - desc { |tcx| + desc { "computing (transitive) callees of `{}` that may recurse", tcx.def_path_str(key), } @@ -1325,8 +1321,7 @@ /// Obtain all the calls into other local functions query mir_inliner_callees(key: ty::InstanceKind<'tcx>) -> &'tcx [(DefId, GenericArgsRef<'tcx>)] { - cycle_fatal - desc { |tcx| + desc { "computing all local function calls in `{}`", tcx.def_path_str(key.def_id()), } @@ -1355,7 +1350,7 @@ /// query eval_to_allocation_raw(key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>) -> EvalToAllocationRawResult<'tcx> { - desc { |tcx| + desc { "const-evaluating + checking `{}`", key.value.display(tcx) } @@ -1364,7 +1359,7 @@ /// Evaluate a static's initializer, returning the allocation of the initializer's memory. query eval_static_initializer(key: DefId) -> EvalStaticInitializerRawResult<'tcx> { - desc { |tcx| + desc { "evaluating initializer of static `{}`", tcx.def_path_str(key) } @@ -1387,7 +1382,7 @@ /// [^1]: Such as enum variant explicit discriminants or array lengths. query eval_to_const_value_raw(key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>) -> EvalToConstValueResult<'tcx> { - desc { |tcx| + desc { "simplifying constant for the type system `{}`", key.value.display(tcx) } @@ -1415,9 +1410,8 @@ desc { "converting literal to const" } } - query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { - desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } - return_result_from_ensure_ok + query check_match(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { "match-checking `{}`", tcx.def_path_str(key) } } /// Performs part of the privacy check and computes effective visibilities. @@ -1426,7 +1420,7 @@ desc { "checking effective visibilities" } } query check_private_in_public(module_def_id: LocalModDefId) { - desc { |tcx| + desc { "checking for private elements in public interfaces for {}", describe_as_module(module_def_id, tcx) } @@ -1441,14 +1435,14 @@ /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; /// in the case of closures, this will be redirected to the enclosing function. query region_scope_tree(def_id: DefId) -> &'tcx crate::middle::region::ScopeTree { - desc { |tcx| "computing drop scopes for `{}`", tcx.def_path_str(def_id) } + desc { "computing drop scopes for `{}`", tcx.def_path_str(def_id) } } /// Generates a MIR body for the shim. query mir_shims(key: ty::InstanceKind<'tcx>) -> &'tcx mir::Body<'tcx> { arena_cache desc { - |tcx| "generating MIR shim for `{}`, instance={:?}", + "generating MIR shim for `{}`, instance={:?}", tcx.def_path_str(key.def_id()), key } @@ -1463,7 +1457,7 @@ } query def_kind(def_id: DefId) -> DefKind { - desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) } + desc { "looking up definition kind of `{}`", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern feedable @@ -1471,7 +1465,7 @@ /// Gets the span for the definition. query def_span(def_id: DefId) -> Span { - desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) } + desc { "looking up span for `{}`", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern feedable @@ -1479,7 +1473,7 @@ /// Gets the span for the identifier of the definition. query def_ident_span(def_id: DefId) -> Option { - desc { |tcx| "looking up span for `{}`'s identifier", tcx.def_path_str(def_id) } + desc { "looking up span for `{}`'s identifier", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern feedable @@ -1488,57 +1482,57 @@ /// Gets the span for the type of the definition. /// Panics if it is not a definition that has a single type. query ty_span(def_id: LocalDefId) -> Span { - desc { |tcx| "looking up span for `{}`'s type", tcx.def_path_str(def_id) } + desc { "looking up span for `{}`'s type", tcx.def_path_str(def_id) } cache_on_disk_if { true } } query lookup_stability(def_id: DefId) -> Option { - desc { |tcx| "looking up stability of `{}`", tcx.def_path_str(def_id) } + desc { "looking up stability of `{}`", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern } query lookup_const_stability(def_id: DefId) -> Option { - desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) } + desc { "looking up const stability of `{}`", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern } query lookup_default_body_stability(def_id: DefId) -> Option { - desc { |tcx| "looking up default body stability of `{}`", tcx.def_path_str(def_id) } + desc { "looking up default body stability of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } query should_inherit_track_caller(def_id: DefId) -> bool { - desc { |tcx| "computing should_inherit_track_caller of `{}`", tcx.def_path_str(def_id) } + desc { "computing should_inherit_track_caller of `{}`", tcx.def_path_str(def_id) } } query inherited_align(def_id: DefId) -> Option { - desc { |tcx| "computing inherited_align of `{}`", tcx.def_path_str(def_id) } + desc { "computing inherited_align of `{}`", tcx.def_path_str(def_id) } } query lookup_deprecation_entry(def_id: DefId) -> Option { - desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) } + desc { "checking whether `{}` is deprecated", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern } /// Determines whether an item is annotated with `#[doc(hidden)]`. query is_doc_hidden(def_id: DefId) -> bool { - desc { |tcx| "checking whether `{}` is `doc(hidden)`", tcx.def_path_str(def_id) } + desc { "checking whether `{}` is `doc(hidden)`", tcx.def_path_str(def_id) } separate_provide_extern } /// Determines whether an item is annotated with `#[doc(notable_trait)]`. query is_doc_notable_trait(def_id: DefId) -> bool { - desc { |tcx| "checking whether `{}` is `doc(notable_trait)`", tcx.def_path_str(def_id) } + desc { "checking whether `{}` is `doc(notable_trait)`", tcx.def_path_str(def_id) } } /// Returns the attributes on the item at `def_id`. /// /// Do not use this directly, use `tcx.get_attrs` instead. query attrs_for_def(def_id: DefId) -> &'tcx [hir::Attribute] { - desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) } + desc { "collecting attributes of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } @@ -1552,7 +1546,7 @@ /// Using this query would include the attribute regardless of the actual instance /// kind at the call site. query codegen_fn_attrs(def_id: DefId) -> &'tcx CodegenFnAttrs { - desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) } + desc { "computing codegen attributes of `{}`", tcx.def_path_str(def_id) } arena_cache cache_on_disk_if { def_id.is_local() } separate_provide_extern @@ -1560,11 +1554,11 @@ } query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet { - desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) } + desc { "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) } } query fn_arg_idents(def_id: DefId) -> &'tcx [Option] { - desc { |tcx| "looking up function parameter identifiers for `{}`", tcx.def_path_str(def_id) } + desc { "looking up function parameter identifiers for `{}`", tcx.def_path_str(def_id) } separate_provide_extern } @@ -1572,23 +1566,23 @@ /// Used by rustdoc. query rendered_const(def_id: DefId) -> &'tcx String { arena_cache - desc { |tcx| "rendering constant initializer of `{}`", tcx.def_path_str(def_id) } + desc { "rendering constant initializer of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } /// Gets the rendered precise capturing args for an opaque for use in rustdoc. query rendered_precise_capturing_args(def_id: DefId) -> Option<&'tcx [PreciseCapturingArgKind]> { - desc { |tcx| "rendering precise capturing args for `{}`", tcx.def_path_str(def_id) } + desc { "rendering precise capturing args for `{}`", tcx.def_path_str(def_id) } separate_provide_extern } query impl_parent(def_id: DefId) -> Option { - desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } + desc { "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } query is_mir_available(key: DefId) -> bool { - desc { |tcx| "checking if item has MIR available: `{}`", tcx.def_path_str(key) } + desc { "checking if item has MIR available: `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -1596,25 +1590,25 @@ query own_existential_vtable_entries( key: DefId ) -> &'tcx [DefId] { - desc { |tcx| "finding all existential vtable entries for trait `{}`", tcx.def_path_str(key) } + desc { "finding all existential vtable entries for trait `{}`", tcx.def_path_str(key) } } query vtable_entries(key: ty::TraitRef<'tcx>) -> &'tcx [ty::VtblEntry<'tcx>] { - desc { |tcx| "finding all vtable entries for trait `{}`", tcx.def_path_str(key.def_id) } + desc { "finding all vtable entries for trait `{}`", tcx.def_path_str(key.def_id) } } query first_method_vtable_slot(key: ty::TraitRef<'tcx>) -> usize { - desc { |tcx| "finding the slot within the vtable of `{}` for the implementation of `{}`", key.self_ty(), key.print_only_trait_name() } + desc { "finding the slot within the vtable of `{}` for the implementation of `{}`", key.self_ty(), key.print_only_trait_name() } } query supertrait_vtable_slot(key: (Ty<'tcx>, Ty<'tcx>)) -> Option { - desc { |tcx| "finding the slot within vtable for trait object `{}` vtable ptr during trait upcasting coercion from `{}` vtable", + desc { "finding the slot within vtable for trait object `{}` vtable ptr during trait upcasting coercion from `{}` vtable", key.1, key.0 } } query vtable_allocation(key: (Ty<'tcx>, Option>)) -> mir::interpret::AllocId { - desc { |tcx| "vtable const allocation for <{} as {}>", + desc { "vtable const allocation for <{} as {}>", key.0, key.1.map(|trait_ref| format!("{trait_ref}")).unwrap_or_else(|| "_".to_owned()) } @@ -1624,7 +1618,7 @@ key: PseudoCanonicalInput<'tcx, ty::TraitRef<'tcx>> ) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> { cache_on_disk_if { true } - desc { |tcx| "computing candidate for `{}`", key.value } + desc { "computing candidate for `{}`", key.value } } /// Return all `impl` blocks in the current crate. @@ -1640,19 +1634,18 @@ /// Given a trait `trait_id`, return all known `impl` blocks. query trait_impls_of(trait_id: DefId) -> &'tcx ty::trait_def::TraitImpls { arena_cache - desc { |tcx| "finding trait impls of `{}`", tcx.def_path_str(trait_id) } + desc { "finding trait impls of `{}`", tcx.def_path_str(trait_id) } } query specialization_graph_of(trait_id: DefId) -> Result<&'tcx specialization_graph::Graph, ErrorGuaranteed> { - desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(trait_id) } + desc { "building specialization graph of trait `{}`", tcx.def_path_str(trait_id) } cache_on_disk_if { true } - return_result_from_ensure_ok } query dyn_compatibility_violations(trait_id: DefId) -> &'tcx [DynCompatibilityViolation] { - desc { |tcx| "determining dyn-compatibility of trait `{}`", tcx.def_path_str(trait_id) } + desc { "determining dyn-compatibility of trait `{}`", tcx.def_path_str(trait_id) } } query is_dyn_compatible(trait_id: DefId) -> bool { - desc { |tcx| "checking if trait `{}` is dyn-compatible", tcx.def_path_str(trait_id) } + desc { "checking if trait `{}` is dyn-compatible", tcx.def_path_str(trait_id) } } /// Gets the ParameterEnvironment for a given item; this environment @@ -1664,7 +1657,7 @@ /// you should also probably have a `ParamEnv` from when it was built. If you don't, /// then you should take a `TypingEnv` to ensure that you handle opaque types correctly. query param_env(def_id: DefId) -> ty::ParamEnv<'tcx> { - desc { |tcx| "computing normalized predicates of `{}`", tcx.def_path_str(def_id) } + desc { "computing normalized predicates of `{}`", tcx.def_path_str(def_id) } feedable } @@ -1672,7 +1665,7 @@ /// replaced with their hidden type. This is used in the old trait solver /// when in `PostAnalysis` mode and should not be called directly. query typing_env_normalized_for_post_analysis(def_id: DefId) -> ty::TypingEnv<'tcx> { - desc { |tcx| "computing revealed normalized predicates of `{}`", tcx.def_path_str(def_id) } + desc { "computing revealed normalized predicates of `{}`", tcx.def_path_str(def_id) } } /// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`, @@ -1733,7 +1726,7 @@ /// those types require drop. If the ADT is known to always need drop /// then `Err(AlwaysRequiresDrop)` is returned. query adt_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { - desc { |tcx| "computing when `{}` needs drop", tcx.def_path_str(def_id) } + desc { "computing when `{}` needs drop", tcx.def_path_str(def_id) } cache_on_disk_if { true } } @@ -1741,7 +1734,7 @@ /// those types require async drop. If the ADT is known to always need async drop /// then `Err(AlwaysRequiresDrop)` is returned. query adt_async_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { - desc { |tcx| "computing when `{}` needs async drop", tcx.def_path_str(def_id) } + desc { "computing when `{}` needs async drop", tcx.def_path_str(def_id) } cache_on_disk_if { true } } @@ -1752,7 +1745,7 @@ /// freeing up memory). If the ADT is known to have a significant destructor then /// `Err(AlwaysRequiresDrop)` is returned. query adt_significant_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { - desc { |tcx| "computing when `{}` has a significant destructor", tcx.def_path_str(def_id) } + desc { "computing when `{}` has a significant destructor", tcx.def_path_str(def_id) } } /// Returns a list of types which (a) have a potentially significant destructor @@ -1773,7 +1766,7 @@ /// because this query partially depends on that query. /// Otherwise, there is a risk of query cycles. query list_significant_drop_tys(ty: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> &'tcx ty::List> { - desc { |tcx| "computing when `{}` has a significant destructor", ty.value } + desc { "computing when `{}` has a significant destructor", ty.value } } /// Computes the layout of a type. Note that this implicitly @@ -1797,12 +1790,38 @@ desc { "computing call ABI of `{}` function pointers", key.value.0 } } - /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for - /// direct calls to an `fn`. + /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for direct calls* + /// to an `fn`. Indirectly-passed parameters in the returned ABI might not include all possible + /// codegen optimization attributes (such as `ReadOnly` or `CapturesNone`), as deducing these + /// requires inspection of function bodies that can lead to cycles when performed during typeck. + /// Post typeck, you should prefer the optimized ABI returned by `TyCtxt::fn_abi_of_instance`. /// - /// NB: that includes virtual calls, which are represented by "direct calls" - /// to an `InstanceKind::Virtual` instance (of `::fn`). - query fn_abi_of_instance( + /// NB: the ABI returned by this query must not differ from that returned by + /// `fn_abi_of_instance_raw` in any other way. + /// + /// * that includes virtual calls, which are represented by "direct calls" to an + /// `InstanceKind::Virtual` instance (of `::fn`). + query fn_abi_of_instance_no_deduced_attrs( + key: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)> + ) -> Result<&'tcx rustc_target::callconv::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> { + desc { "computing unadjusted call ABI of `{}`", key.value.0 } + } + + /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for direct calls* + /// to an `fn`. Indirectly-passed parameters in the returned ABI will include applicable + /// codegen optimization attributes, including `ReadOnly` and `CapturesNone` -- deduction of + /// which requires inspection of function bodies that can lead to cycles when performed during + /// typeck. During typeck, you should therefore use instead the unoptimized ABI returned by + /// `fn_abi_of_instance_no_deduced_attrs`. + /// + /// For performance reasons, you should prefer to call the inherent `TyCtxt::fn_abi_of_instance` + /// method rather than invoke this query: it delegates to this query if necessary, but where + /// possible delegates instead to the `fn_abi_of_instance_no_deduced_attrs` query (thus avoiding + /// unnecessary query system overhead). + /// + /// * that includes virtual calls, which are represented by "direct calls" to an + /// `InstanceKind::Virtual` instance (of `::fn`). + query fn_abi_of_instance_raw( key: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)> ) -> Result<&'tcx rustc_target::callconv::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> { desc { "computing call ABI of `{}`", key.value.0 } @@ -1820,55 +1839,46 @@ } query is_compiler_builtins(_: CrateNum) -> bool { - cycle_fatal desc { "checking if the crate is_compiler_builtins" } separate_provide_extern } query has_global_allocator(_: CrateNum) -> bool { // This query depends on untracked global state in CStore eval_always - cycle_fatal desc { "checking if the crate has_global_allocator" } separate_provide_extern } query has_alloc_error_handler(_: CrateNum) -> bool { // This query depends on untracked global state in CStore eval_always - cycle_fatal desc { "checking if the crate has_alloc_error_handler" } separate_provide_extern } query has_panic_handler(_: CrateNum) -> bool { - cycle_fatal desc { "checking if the crate has_panic_handler" } separate_provide_extern } query is_profiler_runtime(_: CrateNum) -> bool { - cycle_fatal desc { "checking if a crate is `#![profiler_runtime]`" } separate_provide_extern } query has_ffi_unwind_calls(key: LocalDefId) -> bool { - desc { |tcx| "checking if `{}` contains FFI-unwind calls", tcx.def_path_str(key) } + desc { "checking if `{}` contains FFI-unwind calls", tcx.def_path_str(key) } cache_on_disk_if { true } } query required_panic_strategy(_: CrateNum) -> Option { - cycle_fatal desc { "getting a crate's required panic strategy" } separate_provide_extern } query panic_in_drop_strategy(_: CrateNum) -> PanicStrategy { - cycle_fatal desc { "getting a crate's configured panic-in-drop strategy" } separate_provide_extern } query is_no_builtins(_: CrateNum) -> bool { - cycle_fatal desc { "getting whether a crate has `#![no_builtins]`" } separate_provide_extern } query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { - cycle_fatal desc { "getting a crate's symbol mangling version" } separate_provide_extern } @@ -1888,32 +1898,30 @@ desc { "computing whether impls specialize one another" } } query in_scope_traits_map(_: hir::OwnerId) - -> Option<&'tcx ItemLocalMap>> { + -> Option<&'tcx ItemLocalMap<&'tcx [TraitCandidate<'tcx>]>> { desc { "getting traits in scope at a block" } } /// Returns whether the impl or associated function has the `default` keyword. /// Note: This will ICE on inherent impl items. Consider using `AssocItem::defaultness`. query defaultness(def_id: DefId) -> hir::Defaultness { - desc { |tcx| "looking up whether `{}` has `default`", tcx.def_path_str(def_id) } + desc { "looking up whether `{}` has `default`", tcx.def_path_str(def_id) } separate_provide_extern feedable } /// Returns whether the field corresponding to the `DefId` has a default field value. query default_field(def_id: DefId) -> Option { - desc { |tcx| "looking up the `const` corresponding to the default for `{}`", tcx.def_path_str(def_id) } + desc { "looking up the `const` corresponding to the default for `{}`", tcx.def_path_str(def_id) } separate_provide_extern } query check_well_formed(key: LocalDefId) -> Result<(), ErrorGuaranteed> { - desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) } - return_result_from_ensure_ok + desc { "checking that `{}` is well-formed", tcx.def_path_str(key) } } query enforce_impl_non_lifetime_params_are_constrained(key: LocalDefId) -> Result<(), ErrorGuaranteed> { - desc { |tcx| "checking that `{}`'s generics are constrained by the impl header", tcx.def_path_str(key) } - return_result_from_ensure_ok + desc { "checking that `{}`'s generics are constrained by the impl header", tcx.def_path_str(key) } } // The `DefId`s of all non-generic functions and statics in the given crate @@ -1935,12 +1943,12 @@ separate_provide_extern } query is_reachable_non_generic(def_id: DefId) -> bool { - desc { |tcx| "checking whether `{}` is an exported symbol", tcx.def_path_str(def_id) } + desc { "checking whether `{}` is an exported symbol", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern } query is_unreachable_local_definition(def_id: LocalDefId) -> bool { - desc { |tcx| + desc { "checking whether `{}` is reachable from outside the crate", tcx.def_path_str(def_id), } @@ -1968,7 +1976,7 @@ query upstream_monomorphizations_for(def_id: DefId) -> Option<&'tcx UnordMap, CrateNum>> { - desc { |tcx| + desc { "collecting available upstream monomorphizations for `{}`", tcx.def_path_str(def_id), } @@ -2086,13 +2094,13 @@ /// Do not call this directly, but instead use the `incoherent_impls` query. /// This query is only used to get the data necessary for that query. query crate_incoherent_impls(key: (CrateNum, SimplifiedType)) -> &'tcx [DefId] { - desc { |tcx| "collecting all impls for a type in a crate" } + desc { "collecting all impls for a type in a crate" } separate_provide_extern } /// Get the corresponding native library from the `native_libraries` query query native_library(def_id: DefId) -> Option<&'tcx NativeLib> { - desc { |tcx| "getting the native library for `{}`", tcx.def_path_str(def_id) } + desc { "getting the native library for `{}`", tcx.def_path_str(def_id) } } query inherit_sig_for_delegation_item(def_id: LocalDefId) -> &'tcx [Ty<'tcx>] { @@ -2104,18 +2112,18 @@ /// See `rustc_resolve::late::lifetimes` for details. query resolve_bound_vars(owner_id: hir::OwnerId) -> &'tcx ResolveBoundVars<'tcx> { arena_cache - desc { |tcx| "resolving lifetimes for `{}`", tcx.def_path_str(owner_id) } + desc { "resolving lifetimes for `{}`", tcx.def_path_str(owner_id) } } query named_variable_map(owner_id: hir::OwnerId) -> &'tcx SortedMap { - desc { |tcx| "looking up a named region inside `{}`", tcx.def_path_str(owner_id) } + desc { "looking up a named region inside `{}`", tcx.def_path_str(owner_id) } } query is_late_bound_map(owner_id: hir::OwnerId) -> Option<&'tcx FxIndexSet> { - desc { |tcx| "testing if a region is late bound inside `{}`", tcx.def_path_str(owner_id) } + desc { "testing if a region is late bound inside `{}`", tcx.def_path_str(owner_id) } } /// Returns the *default lifetime* to be used if a trait object type were to be passed for /// the type parameter given by `DefId`. /// - /// **Tip**: You can use `#[rustc_object_lifetime_default]` on an item to basically + /// **Tip**: You can use `#[rustc_dump_object_lifetime_defaults]` on an item to basically /// print the result of this query for use in UI tests or for debugging purposes. /// /// # Examples @@ -2132,7 +2140,7 @@ } query late_bound_vars_map(owner_id: hir::OwnerId) -> &'tcx SortedMap>> { - desc { |tcx| "looking up late bound vars inside `{}`", tcx.def_path_str(owner_id) } + desc { "looking up late bound vars inside `{}`", tcx.def_path_str(owner_id) } } /// For an opaque type, return the list of (captured lifetime, inner generic param). /// ```ignore (illustrative) @@ -2149,7 +2157,7 @@ /// ^^^^^^ captured lifetimes /// ``` query opaque_captured_lifetimes(def_id: LocalDefId) -> &'tcx [(ResolvedArg, LocalDefId)] { - desc { |tcx| "listing captured lifetimes for opaque `{}`", tcx.def_path_str(def_id) } + desc { "listing captured lifetimes for opaque `{}`", tcx.def_path_str(def_id) } } /// Computes the visibility of the provided `def_id`. @@ -2165,7 +2173,7 @@ /// /// In here, if you call `visibility` on `T`, it'll panic. query visibility(def_id: DefId) -> ty::Visibility { - desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } + desc { "computing visibility of `{}`", tcx.def_path_str(def_id) } separate_provide_extern feedable } @@ -2179,7 +2187,7 @@ desc { "computing the uninhabited predicate of `{}`", key } } - query dep_kind(_: CrateNum) -> CrateDepKind { + query crate_dep_kind(_: CrateNum) -> CrateDepKind { eval_always desc { "fetching what a dependency looks like" } separate_provide_extern @@ -2192,7 +2200,7 @@ separate_provide_extern } query module_children(def_id: DefId) -> &'tcx [ModChild] { - desc { |tcx| "collecting child items of module `{}`", tcx.def_path_str(def_id) } + desc { "collecting child items of module `{}`", tcx.def_path_str(def_id) } separate_provide_extern } @@ -2230,7 +2238,7 @@ } /// Whether the function is an intrinsic query intrinsic_raw(def_id: DefId) -> Option { - desc { |tcx| "fetch intrinsic name if `{}` is an intrinsic", tcx.def_path_str(def_id) } + desc { "fetch intrinsic name if `{}` is an intrinsic", tcx.def_path_str(def_id) } separate_provide_extern } /// Returns the lang items defined in another crate by loading it from metadata. @@ -2323,7 +2331,7 @@ } query upvars_mentioned(def_id: DefId) -> Option<&'tcx FxIndexMap> { - desc { |tcx| "collecting upvars mentioned in `{}`", tcx.def_path_str(def_id) } + desc { "collecting upvars mentioned in `{}`", tcx.def_path_str(def_id) } } /// All available crates in the graph, including those that should not be user-facing @@ -2383,7 +2391,7 @@ /// sets of different crates do not intersect. query exported_non_generic_symbols(cnum: CrateNum) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { desc { "collecting exported non-generic symbols for crate `{}`", cnum} - cache_on_disk_if { *cnum == LOCAL_CRATE } + cache_on_disk_if { cnum == LOCAL_CRATE } separate_provide_extern } @@ -2396,7 +2404,7 @@ /// sets of different crates do not intersect. query exported_generic_symbols(cnum: CrateNum) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { desc { "collecting exported generic symbols for crate `{}`", cnum} - cache_on_disk_if { *cnum == LOCAL_CRATE } + cache_on_disk_if { cnum == LOCAL_CRATE } separate_provide_extern } @@ -2406,7 +2414,7 @@ } query is_codegened_item(def_id: DefId) -> bool { - desc { |tcx| "determining whether `{}` needs codegen", tcx.def_path_str(def_id) } + desc { "determining whether `{}` needs codegen", tcx.def_path_str(def_id) } } query codegen_unit(sym: Symbol) -> &'tcx CodegenUnit<'tcx> { @@ -2565,14 +2573,14 @@ } query instantiate_and_check_impossible_predicates(key: (DefId, GenericArgsRef<'tcx>)) -> bool { - desc { |tcx| + desc { "checking impossible instantiated predicates: `{}`", tcx.def_path_str(key.0) } } query is_impossible_associated_item(key: (DefId, DefId)) -> bool { - desc { |tcx| + desc { "checking if `{}` is impossible to reference within `{}`", tcx.def_path_str(key.1), tcx.def_path_str(key.0), @@ -2674,12 +2682,11 @@ /// /// Any other def id will ICE. query compare_impl_item(key: LocalDefId) -> Result<(), ErrorGuaranteed> { - desc { |tcx| "checking assoc item `{}` is compatible with trait definition", tcx.def_path_str(key) } - return_result_from_ensure_ok + desc { "checking assoc item `{}` is compatible with trait definition", tcx.def_path_str(key) } } query deduced_param_attrs(def_id: DefId) -> &'tcx [DeducedParamAttrs] { - desc { |tcx| "deducing parameter attributes for {}", tcx.def_path_str(def_id) } + desc { "deducing parameter attributes for {}", tcx.def_path_str(def_id) } separate_provide_extern } @@ -2736,12 +2743,12 @@ } query anon_const_kind(def_id: DefId) -> ty::AnonConstKind { - desc { |tcx| "looking up anon const kind of `{}`", tcx.def_path_str(def_id) } + desc { "looking up anon const kind of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } query trivial_const(def_id: DefId) -> Option<(mir::ConstValue, Ty<'tcx>)> { - desc { |tcx| "checking if `{}` is a trivial const", tcx.def_path_str(def_id) } + desc { "checking if `{}` is a trivial const", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern } @@ -2752,7 +2759,7 @@ /// /// Returns the sanitizer settings for this def. query sanitizer_settings_for(key: LocalDefId) -> SanitizerFnAttrs { - desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) } + desc { "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) } feedable } @@ -2764,16 +2771,26 @@ query externally_implementable_items(cnum: CrateNum) -> &'tcx FxIndexMap)> { arena_cache desc { "looking up the externally implementable items of a crate" } - cache_on_disk_if { *cnum == LOCAL_CRATE } + cache_on_disk_if { cnum == LOCAL_CRATE } separate_provide_extern } - query is_rhs_type_const(def_id: DefId) -> bool { - desc { |tcx| "checking whether `{}` is a rhs type const", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - } + //----------------------------------------------------------------------------- + // "Non-queries" are special dep kinds that are not queries. + //----------------------------------------------------------------------------- + + /// We use this for most things when incr. comp. is turned off. + non_query Null + /// We use this to create a forever-red node. + non_query Red + /// We use this to create a side effect node. + non_query SideEffect + /// We use this to create the anon node with zero dependencies. + non_query AnonZeroDeps + non_query TraitSelect + non_query CompileCodegenUnit + non_query CompileMonoItem + non_query Metadata } rustc_with_all_queries! { define_callbacks! } -rustc_feedable_queries! { define_feedable! } diff --git a/compiler/rustc_middle/src/query/caches.rs b/compiler/rustc_middle/src/query/caches.rs index 7424492ddc1f..0c71a98b7fb2 100644 --- a/compiler/rustc_middle/src/query/caches.rs +++ b/compiler/rustc_middle/src/query/caches.rs @@ -1,19 +1,13 @@ -use std::fmt::Debug; -use std::hash::Hash; use std::sync::OnceLock; use rustc_data_structures::sharded::ShardedHashMap; -use rustc_data_structures::stable_hasher::HashStable; pub use rustc_data_structures::vec_cache::VecCache; use rustc_hir::def_id::LOCAL_CRATE; use rustc_index::Idx; -use rustc_query_system::ich::StableHashingContext; use rustc_span::def_id::{DefId, DefIndex}; use crate::dep_graph::DepNodeIndex; - -/// Traits that all query keys must satisfy. -pub trait QueryCacheKey = Hash + Eq + Copy + Debug + for<'a> HashStable>; +use crate::query::keys::QueryKey; /// Trait for types that serve as an in-memory cache for query results, /// for a given key (argument) type and value (return) type. @@ -21,7 +15,7 @@ /// Types implementing this trait are associated with actual key/value types /// by the `Cache` associated type of the `rustc_middle::query::Key` trait. pub trait QueryCache: Sized { - type Key: QueryCacheKey; + type Key: QueryKey; type Value: Copy; /// Returns the cached value (and other information) associated with the @@ -34,8 +28,13 @@ pub trait QueryCache: Sized { /// value by executing the query or loading a cached value from disk. fn complete(&self, key: Self::Key, value: Self::Value, index: DepNodeIndex); - fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)); + /// Calls a closure on each entry in this cache. + fn for_each(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)); + /// Returns the number of entries currently in this cache. + /// + /// Useful for reserving capacity in data structures that will hold the + /// output of a call to [`Self::for_each`]. fn len(&self) -> usize; } @@ -53,7 +52,7 @@ fn default() -> Self { impl QueryCache for DefaultCache where - K: QueryCacheKey, + K: QueryKey, V: Copy, { type Key = K; @@ -67,11 +66,11 @@ fn lookup(&self, key: &K) -> Option<(V, DepNodeIndex)> { #[inline] fn complete(&self, key: K, value: V, index: DepNodeIndex) { // We may be overwriting another value. This is all right, since the dep-graph - // will check that the fingerprint matches. + // will check that the value fingerprint matches. self.cache.insert(key, (value, index)); } - fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { + fn for_each(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { for shard in self.cache.lock_shards() { for (k, v) in shard.iter() { f(k, &v.0, v.1); @@ -113,7 +112,7 @@ fn complete(&self, _key: (), value: V, index: DepNodeIndex) { self.cache.set((value, index)).ok(); } - fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { + fn for_each(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { if let Some(value) = self.cache.get() { f(&(), &value.0, value.1) } @@ -166,11 +165,11 @@ fn complete(&self, key: DefId, value: V, index: DepNodeIndex) { } } - fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { - self.local.iter(&mut |key, value, index| { + fn for_each(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { + self.local.for_each(&mut |key, value, index| { f(&DefId { krate: LOCAL_CRATE, index: *key }, value, index); }); - self.foreign.iter(f); + self.foreign.for_each(f); } fn len(&self) -> usize { @@ -180,7 +179,7 @@ fn len(&self) -> usize { impl QueryCache for VecCache where - K: Idx + QueryCacheKey, + K: Idx + QueryKey, V: Copy, { type Key = K; @@ -196,8 +195,8 @@ fn complete(&self, key: K, value: V, index: DepNodeIndex) { self.complete(key, value, index) } - fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { - self.iter(f) + fn for_each(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { + self.for_each(f) } fn len(&self) -> usize { diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index b977172fcf9d..2c3959805d9d 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -7,7 +7,7 @@ use crate::dep_graph::{DepKind, DepNodeKey}; use crate::query::erase::{self, Erasable, Erased}; use crate::query::plumbing::QueryVTable; -use crate::query::{QueryCache, QueryMode}; +use crate::query::{EnsureMode, QueryCache, QueryMode}; use crate::ty::TyCtxt; /// Checks whether there is already a value for this key in the in-memory @@ -15,11 +15,11 @@ /// /// (Also performs some associated bookkeeping, if a value was found.) #[inline(always)] -fn try_get_cached<'tcx, C>(tcx: TyCtxt<'tcx>, cache: &C, key: &C::Key) -> Option +fn try_get_cached<'tcx, C>(tcx: TyCtxt<'tcx>, cache: &C, key: C::Key) -> Option where C: QueryCache, { - match cache.lookup(key) { + match cache.lookup(&key) { Some((value, index)) => { tcx.prof.query_cache_hit(index.into()); tcx.dep_graph.read_index(index); @@ -32,92 +32,94 @@ fn try_get_cached<'tcx, C>(tcx: TyCtxt<'tcx>, cache: &C, key: &C::Key) -> Option /// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)` /// for all queries. #[inline(always)] -pub(crate) fn query_get_at<'tcx, Cache>( +pub(crate) fn query_get_at<'tcx, C>( tcx: TyCtxt<'tcx>, - execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, - query_cache: &Cache, span: Span, - key: Cache::Key, -) -> Cache::Value + query: &'tcx QueryVTable<'tcx, C>, + key: C::Key, +) -> C::Value where - Cache: QueryCache, + C: QueryCache, { - match try_get_cached(tcx, query_cache, &key) { + match try_get_cached(tcx, &query.cache, key) { Some(value) => value, - None => execute_query(tcx, span, key, QueryMode::Get).unwrap(), + None => (query.execute_query_fn)(tcx, span, key, QueryMode::Get).unwrap(), } } -/// Shared implementation of `tcx.ensure_ok().$query(..)` for most queries, -/// and `tcx.ensure_done().$query(..)` for all queries. +/// Shared implementation of `tcx.ensure_ok().$query(..)` and +/// `tcx.ensure_done().$query(..)` for all queries. #[inline] -pub(crate) fn query_ensure<'tcx, Cache>( +pub(crate) fn query_ensure_ok_or_done<'tcx, C>( tcx: TyCtxt<'tcx>, - execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, - query_cache: &Cache, - key: Cache::Key, - check_cache: bool, + query: &'tcx QueryVTable<'tcx, C>, + key: C::Key, + ensure_mode: EnsureMode, ) where - Cache: QueryCache, + C: QueryCache, { - if try_get_cached(tcx, query_cache, &key).is_none() { - execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }); + match try_get_cached(tcx, &query.cache, key) { + Some(_value) => {} + None => { + (query.execute_query_fn)(tcx, DUMMY_SP, key, QueryMode::Ensure { ensure_mode }); + } } } -/// Shared implementation of `tcx.ensure_ok().$query(..)` for queries that -/// have the `return_result_from_ensure_ok` modifier. +/// Implementation of `tcx.ensure_result().$query(..)` for queries that +/// return `Result<_, ErrorGuaranteed>`. #[inline] -pub(crate) fn query_ensure_error_guaranteed<'tcx, Cache, T>( +pub(crate) fn query_ensure_result<'tcx, C, T>( tcx: TyCtxt<'tcx>, - execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, - query_cache: &Cache, - key: Cache::Key, - check_cache: bool, + query: &'tcx QueryVTable<'tcx, C>, + key: C::Key, ) -> Result<(), ErrorGuaranteed> where - Cache: QueryCache>>, + C: QueryCache>>, Result: Erasable, { - if let Some(res) = try_get_cached(tcx, query_cache, &key) { - erase::restore_val(res).map(drop) - } else { - execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }) - .map(erase::restore_val) - .map(|res| res.map(drop)) - // Either we actually executed the query, which means we got a full `Result`, - // or we can just assume the query succeeded, because it was green in the - // incremental cache. If it is green, that means that the previous compilation - // that wrote to the incremental cache compiles successfully. That is only - // possible if the cache entry was `Ok(())`, so we emit that here, without - // actually encoding the `Result` in the cache or loading it from there. - .unwrap_or(Ok(())) + match try_get_cached(tcx, &query.cache, key) { + Some(value) => erase::restore_val(value).map(drop), + None => (query.execute_query_fn)( + tcx, + DUMMY_SP, + key, + QueryMode::Ensure { ensure_mode: EnsureMode::Ok }, + ) + .map(erase::restore_val) + .map(|value| value.map(drop)) + // Either we actually executed the query, which means we got a full `Result`, + // or we can just assume the query succeeded, because it was green in the + // incremental cache. If it is green, that means that the previous compilation + // that wrote to the incremental cache compiles successfully. That is only + // possible if the cache entry was `Ok(())`, so we emit that here, without + // actually encoding the `Result` in the cache or loading it from there. + .unwrap_or(Ok(())), } } /// Common implementation of query feeding, used by `define_feedable!`. -pub(crate) fn query_feed<'tcx, Cache>( +pub(crate) fn query_feed<'tcx, C>( tcx: TyCtxt<'tcx>, dep_kind: DepKind, - query_vtable: &QueryVTable<'tcx, Cache>, - cache: &Cache, - key: Cache::Key, - value: Cache::Value, + query_vtable: &QueryVTable<'tcx, C>, + key: C::Key, + value: C::Value, ) where - Cache: QueryCache, - Cache::Key: DepNodeKey>, + C: QueryCache, + C::Key: DepNodeKey<'tcx>, { let format_value = query_vtable.format_value; // Check whether the in-memory cache already has a value for this key. - match try_get_cached(tcx, cache, &key) { + match try_get_cached(tcx, &query_vtable.cache, key) { Some(old) => { // The query already has a cached value for this key. // That's OK if both values are the same, i.e. they have the same hash, // so now we check their hashes. - if let Some(hasher_fn) = query_vtable.hash_result { + if let Some(hash_value_fn) = query_vtable.hash_value_fn { let (old_hash, value_hash) = tcx.with_stable_hashing_context(|ref mut hcx| { - (hasher_fn(hcx, &old), hasher_fn(hcx, &value)) + (hash_value_fn(hcx, &old), hash_value_fn(hcx, &value)) }); if old_hash != value_hash { // We have an inconsistency. This can happen if one of the two @@ -150,10 +152,10 @@ pub(crate) fn query_feed<'tcx, Cache>( dep_node, tcx, &value, - query_vtable.hash_result, + query_vtable.hash_value_fn, query_vtable.format_value, ); - cache.complete(key, value, dep_node_index); + query_vtable.cache.complete(key, value, dep_node_index); } } } diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 6652e6e78e76..54b72c5b6714 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -1,13 +1,17 @@ //! Defines the set of legal keys that can be used in queries. use std::ffi::OsStr; +use std::fmt::Debug; +use std::hash::Hash; use rustc_ast::tokenstream::TokenStream; +use rustc_data_structures::stable_hasher::HashStable; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::hir_id::OwnerId; use rustc_span::{DUMMY_SP, Ident, LocalExpnId, Span, Symbol}; use crate::dep_graph::DepNodeIndex; +use crate::ich::StableHashingContext; use crate::infer::canonical::CanonicalQueryInput; use crate::mir::mono::CollectionMode; use crate::query::{DefIdCache, DefaultCache, SingleCache, VecCache}; @@ -20,9 +24,10 @@ #[derive(Copy, Clone, Debug)] pub struct LocalCrate; -/// The `Key` trait controls what types can legally be used as the key -/// for a query. -pub trait Key: Sized { +pub trait QueryKeyBounds = Copy + Debug + Eq + Hash + for<'a> HashStable>; + +/// Controls what types can legally be used as the key for a query. +pub trait QueryKey: Sized + QueryKeyBounds { /// The type of in-memory cache to use for queries with this key type. /// /// In practice the cache type must implement [`QueryCache`], though that @@ -47,15 +52,15 @@ fn def_id_for_ty_in_cycle(&self) -> Option { } } -pub trait AsLocalKey: Key { - type LocalKey; +pub trait AsLocalQueryKey: QueryKey { + type LocalQueryKey; /// Given an instance of this key, what crate is it referring to? /// This is used to find the provider. - fn as_local_key(&self) -> Option; + fn as_local_key(&self) -> Option; } -impl Key for () { +impl QueryKey for () { type Cache = SingleCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { @@ -63,37 +68,37 @@ fn default_span(&self, _: TyCtxt<'_>) -> Span { } } -impl<'tcx> Key for ty::InstanceKind<'tcx> { +impl<'tcx> QueryKey for ty::InstanceKind<'tcx> { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.def_id()) } } -impl<'tcx> Key for ty::Instance<'tcx> { +impl<'tcx> QueryKey for ty::Instance<'tcx> { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.def_id()) } } -impl<'tcx> Key for mir::interpret::GlobalId<'tcx> { +impl<'tcx> QueryKey for mir::interpret::GlobalId<'tcx> { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.instance.default_span(tcx) } } -impl<'tcx> Key for (Ty<'tcx>, Option>) { +impl<'tcx> QueryKey for (Ty<'tcx>, Option>) { fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx> Key for ty::LitToConstInput<'tcx> { +impl<'tcx> QueryKey for ty::LitToConstInput<'tcx> { fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl Key for CrateNum { +impl QueryKey for CrateNum { type Cache = VecCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { @@ -101,16 +106,16 @@ fn default_span(&self, _: TyCtxt<'_>) -> Span { } } -impl AsLocalKey for CrateNum { - type LocalKey = LocalCrate; +impl AsLocalQueryKey for CrateNum { + type LocalQueryKey = LocalCrate; #[inline(always)] - fn as_local_key(&self) -> Option { + fn as_local_key(&self) -> Option { (*self == LOCAL_CRATE).then_some(LocalCrate) } } -impl Key for OwnerId { +impl QueryKey for OwnerId { type Cache = VecCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { @@ -122,7 +127,7 @@ fn key_as_def_id(&self) -> Option { } } -impl Key for LocalDefId { +impl QueryKey for LocalDefId { type Cache = VecCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { @@ -134,7 +139,7 @@ fn key_as_def_id(&self) -> Option { } } -impl Key for DefId { +impl QueryKey for DefId { type Cache = DefIdCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { @@ -147,16 +152,16 @@ fn key_as_def_id(&self) -> Option { } } -impl AsLocalKey for DefId { - type LocalKey = LocalDefId; +impl AsLocalQueryKey for DefId { + type LocalQueryKey = LocalDefId; #[inline(always)] - fn as_local_key(&self) -> Option { + fn as_local_key(&self) -> Option { self.as_local() } } -impl Key for LocalModDefId { +impl QueryKey for LocalModDefId { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(*self) } @@ -167,19 +172,19 @@ fn key_as_def_id(&self) -> Option { } } -impl Key for SimplifiedType { +impl QueryKey for SimplifiedType { fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl Key for (DefId, DefId) { +impl QueryKey for (DefId, DefId) { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) } } -impl Key for (DefId, Ident) { +impl QueryKey for (DefId, Ident) { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.0) } @@ -190,73 +195,73 @@ fn key_as_def_id(&self) -> Option { } } -impl Key for (LocalDefId, LocalDefId, Ident) { +impl QueryKey for (LocalDefId, LocalDefId, Ident) { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) } } -impl Key for (CrateNum, DefId) { +impl QueryKey for (CrateNum, DefId) { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) } } -impl AsLocalKey for (CrateNum, DefId) { - type LocalKey = DefId; +impl AsLocalQueryKey for (CrateNum, DefId) { + type LocalQueryKey = DefId; #[inline(always)] - fn as_local_key(&self) -> Option { + fn as_local_key(&self) -> Option { (self.0 == LOCAL_CRATE).then(|| self.1) } } -impl Key for (CrateNum, SimplifiedType) { +impl QueryKey for (CrateNum, SimplifiedType) { fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl AsLocalKey for (CrateNum, SimplifiedType) { - type LocalKey = SimplifiedType; +impl AsLocalQueryKey for (CrateNum, SimplifiedType) { + type LocalQueryKey = SimplifiedType; #[inline(always)] - fn as_local_key(&self) -> Option { + fn as_local_key(&self) -> Option { (self.0 == LOCAL_CRATE).then(|| self.1) } } -impl Key for (DefId, ty::SizedTraitKind) { +impl QueryKey for (DefId, ty::SizedTraitKind) { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) } } -impl<'tcx> Key for GenericArgsRef<'tcx> { +impl<'tcx> QueryKey for GenericArgsRef<'tcx> { fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx> Key for (DefId, GenericArgsRef<'tcx>) { +impl<'tcx> QueryKey for (DefId, GenericArgsRef<'tcx>) { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) } } -impl<'tcx> Key for ty::TraitRef<'tcx> { +impl<'tcx> QueryKey for ty::TraitRef<'tcx> { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.def_id) } } -impl<'tcx> Key for GenericArg<'tcx> { +impl<'tcx> QueryKey for GenericArg<'tcx> { fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx> Key for Ty<'tcx> { +impl<'tcx> QueryKey for Ty<'tcx> { fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } @@ -270,19 +275,19 @@ fn def_id_for_ty_in_cycle(&self) -> Option { } } -impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) { +impl<'tcx> QueryKey for (Ty<'tcx>, Ty<'tcx>) { fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx> Key for ty::Clauses<'tcx> { +impl<'tcx> QueryKey for ty::Clauses<'tcx> { fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx, T: Key> Key for ty::PseudoCanonicalInput<'tcx, T> { +impl<'tcx, T: QueryKey> QueryKey for ty::PseudoCanonicalInput<'tcx, T> { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.value.default_span(tcx) } @@ -292,19 +297,19 @@ fn def_id_for_ty_in_cycle(&self) -> Option { } } -impl Key for Symbol { +impl QueryKey for Symbol { fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl Key for Option { +impl QueryKey for Option { fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx> Key for &'tcx OsStr { +impl<'tcx> QueryKey for &'tcx OsStr { fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } @@ -312,55 +317,55 @@ fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { /// Canonical query goals correspond to abstract trait operations that /// are not tied to any crate in particular. -impl<'tcx, T: Clone> Key for CanonicalQueryInput<'tcx, T> { +impl<'tcx, T: QueryKeyBounds> QueryKey for CanonicalQueryInput<'tcx, T> { fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx, T: Clone> Key for (CanonicalQueryInput<'tcx, T>, bool) { +impl<'tcx, T: QueryKeyBounds> QueryKey for (CanonicalQueryInput<'tcx, T>, bool) { fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx> Key for (Ty<'tcx>, rustc_abi::VariantIdx) { +impl<'tcx> QueryKey for (Ty<'tcx>, rustc_abi::VariantIdx) { fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx> Key for (ty::Predicate<'tcx>, traits::WellFormedLoc) { +impl<'tcx> QueryKey for (ty::Predicate<'tcx>, traits::WellFormedLoc) { fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx> Key for (ty::PolyFnSig<'tcx>, &'tcx ty::List>) { +impl<'tcx> QueryKey for (ty::PolyFnSig<'tcx>, &'tcx ty::List>) { fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List>) { +impl<'tcx> QueryKey for (ty::Instance<'tcx>, &'tcx ty::List>) { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) } } -impl<'tcx> Key for ty::Value<'tcx> { +impl<'tcx> QueryKey for ty::Value<'tcx> { fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx> Key for (LocalExpnId, &'tcx TokenStream) { +impl<'tcx> QueryKey for (LocalExpnId, &'tcx TokenStream) { fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { self.0.expn_data().call_site } } -impl<'tcx> Key for (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) { +impl<'tcx> QueryKey for (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) { // Just forward to `Ty<'tcx>` fn default_span(&self, _: TyCtxt<'_>) -> Span { @@ -375,7 +380,7 @@ fn def_id_for_ty_in_cycle(&self) -> Option { } } -impl<'tcx> Key for (ty::Instance<'tcx>, CollectionMode) { +impl<'tcx> QueryKey for (ty::Instance<'tcx>, CollectionMode) { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d16ca3c401d2..ef96ebb83bff 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1,18 +1,13 @@ -use rustc_data_structures::jobserver::Proxy; use rustc_hir::def_id::LocalDefId; -use rustc_query_system::query::QuerySideEffect; -pub use self::caches::{ - DefIdCache, DefaultCache, QueryCache, QueryCacheKey, SingleCache, VecCache, -}; +pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache}; pub use self::job::{QueryInfo, QueryJob, QueryJobId, QueryLatch, QueryWaiter}; -pub use self::keys::{AsLocalKey, Key, LocalCrate}; +pub use self::keys::{AsLocalQueryKey, LocalCrate, QueryKey}; pub use self::plumbing::{ - ActiveKeyStatus, CycleError, CycleErrorHandling, IntoQueryParam, QueryMode, QueryState, - TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk, + ActiveKeyStatus, CycleError, CycleErrorHandling, EnsureMode, IntoQueryParam, QueryMode, + QueryState, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk, TyCtxtEnsureResult, }; pub use self::stack::{QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra}; -use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; pub use crate::queries::Providers; use crate::ty::TyCtxt; @@ -36,18 +31,3 @@ pub fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> Str format!("module `{}`", tcx.def_path_str(def_id)) } } - -pub trait QueryContext<'tcx>: HasDepContext { - /// Gets a jobserver reference which is used to release then acquire - /// a token while waiting on a query. - fn jobserver_proxy(&self) -> &Proxy; - - /// Load a side effect associated to the node in the previous session. - fn load_side_effect( - self, - prev_dep_node_index: SerializedDepNodeIndex, - ) -> Option; - - /// Register a side effect for the given node, for use in next session. - fn store_side_effect(self, dep_node_index: DepNodeIndex, side_effect: QuerySideEffect); -} diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index 81b9f0da6446..2bc70dd588df 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -1,77 +1,66 @@ //! 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 +//! all queries that use a particular modifier. #![allow(unused, non_camel_case_types)] // FIXME: Update and clarify documentation for these modifiers. -/// # `desc` query modifier +// tidy-alphabetical-start +// +/// # `anon` query modifier /// -/// The description of the query. This modifier is required on every query. -pub struct desc; +/// Generate a dep node based on the dependencies of the query +pub(crate) struct anon; /// # `arena_cache` query modifier /// /// Use this type for the in-memory cache. -pub struct arena_cache; +pub(crate) struct arena_cache; /// # `cache_on_disk_if` query modifier /// /// Cache the query to disk if the `Block` returns true. -pub struct cache_on_disk_if; - -/// # `cycle_fatal` query modifier -/// -/// A cycle error for this query aborting the compilation with a fatal error. -pub struct cycle_fatal; +pub(crate) struct cache_on_disk_if; /// # `cycle_delay_bug` query modifier /// /// A cycle error results in a delay_bug call -pub struct cycle_delay_bug; +pub(crate) struct cycle_delay_bug; /// # `cycle_stash` query modifier /// /// A cycle error results in a stashed cycle error that can be unstashed and canceled later -pub struct cycle_stash; - -/// # `no_hash` query modifier -/// -/// Don't hash the result, instead just mark a query red if it runs -pub struct no_hash; - -/// # `anon` query modifier -/// -/// Generate a dep node based on the dependencies of the query -pub struct anon; - -/// # `eval_always` query modifier -/// -/// Always evaluate the query, ignoring its dependencies -pub struct eval_always; +pub(crate) struct cycle_stash; /// # `depth_limit` query modifier /// /// Whether the query has a call depth limit -pub struct depth_limit; +pub(crate) struct depth_limit; -/// # `separate_provide_extern` query modifier +/// # `desc` query modifier /// -/// Use a separate query provider for local and extern crates -pub struct separate_provide_extern; +/// The description of the query. This modifier is required on every query. +pub(crate) struct desc; + +/// # `eval_always` query modifier +/// +/// Always evaluate the query, ignoring its dependencies +pub(crate) struct eval_always; /// # `feedable` query modifier /// /// Generate a `feed` method to set the query's value from another query. -pub struct feedable; +pub(crate) struct feedable; -/// # `return_result_from_ensure_ok` query modifier +/// # `no_hash` query modifier /// -/// When this query is called via `tcx.ensure_ok()`, it returns -/// `Result<(), ErrorGuaranteed>` instead of `()`. If the query needs to -/// be executed, and that execution returns an error, the error result is -/// returned to the caller. +/// Don't hash the result, instead just mark a query red if it runs +pub(crate) struct no_hash; + +/// # `separate_provide_extern` query modifier /// -/// If execution is skipped, a synthetic `Ok(())` is returned, on the -/// assumption that a query with all-green inputs must have succeeded. -/// -/// Can only be applied to queries with a return value of -/// `Result<_, ErrorGuaranteed>`. -pub struct return_result_from_ensure_ok; +/// Use a separate query provider for local and extern crates +pub(crate) struct separate_provide_extern; + +// tidy-alphabetical-end diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 5ef0c2500e7a..f2d418fea162 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -1,6 +1,6 @@ use std::collections::hash_map::Entry; -use std::mem; use std::sync::Arc; +use std::{fmt, mem}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; @@ -11,7 +11,6 @@ use rustc_hir::definitions::DefPathHash; use rustc_index::{Idx, IndexVec}; use rustc_macros::{Decodable, Encodable}; -use rustc_query_system::query::QuerySideEffect; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_session::Session; @@ -24,7 +23,7 @@ SourceFile, Span, SpanDecoder, SpanEncoder, StableSourceFileId, Symbol, }; -use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; +use crate::dep_graph::{DepNodeIndex, QuerySideEffect, SerializedDepNodeIndex}; use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use crate::mir::mono::MonoItem; use crate::mir::{self, interpret}; @@ -203,20 +202,9 @@ pub fn new_empty() -> Self { } } - /// Execute all cache promotions and release the serialized backing Mmap. - /// - /// Cache promotions require invoking queries, which needs to read the serialized data. - /// In order to serialize the new on-disk cache, the former on-disk cache file needs to be - /// deleted, hence we won't be able to refer to its memmapped data. - pub fn drop_serialized_data(&self, tcx: TyCtxt<'_>) { - // Load everything into memory so we can write it out to the on-disk - // cache. The vast majority of cacheable query results should already - // be in memory, so this should be a cheap operation. - // Do this *before* we clone 'latest_foreign_def_path_hashes', since - // loading existing queries may cause us to create new DepNodes, which - // may in turn end up invoking `store_foreign_def_id_hash` - tcx.dep_graph.exec_cache_promotions(tcx); - + /// Release the serialized backing `Mmap`. + pub fn close_serialized_data_mmap(&self) { + // Obtain a write lock, and replace the mmap with None to drop it. *self.serialized_data.write() = None; } @@ -262,7 +250,7 @@ pub fn serialize(&self, tcx: TyCtxt<'_>, encoder: FileEncoder) -> FileEncodeResu tcx.sess.time("encode_query_results", || { let enc = &mut encoder; let qri = &mut query_result_index; - (tcx.query_system.fns.encode_query_results)(tcx, enc, qri); + tcx.encode_all_query_results(enc, qri); }); // Encode side effects. @@ -509,7 +497,7 @@ fn decode_symbol_or_byte_symbol( // tag matches and the correct amount of bytes was read. fn decode_tagged(decoder: &mut D, expected_tag: T) -> V where - T: Decodable + Eq + std::fmt::Debug, + T: Decodable + Eq + fmt::Debug, V: Decodable, D: Decoder, { @@ -830,6 +818,13 @@ pub struct CacheEncoder<'a, 'tcx> { symbol_index_table: FxHashMap, } +impl<'a, 'tcx> fmt::Debug for CacheEncoder<'a, 'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Add more details here if/when necessary. + f.write_str("CacheEncoder") + } +} + impl<'a, 'tcx> CacheEncoder<'a, 'tcx> { #[inline] fn source_file_index(&mut self, source_file: Arc) -> SourceFileIndex { diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 727e93148251..ad0b6f7c335e 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -1,4 +1,4 @@ -use std::fmt::Debug; +use std::fmt; use std::ops::Deref; use rustc_data_structures::fingerprint::Fingerprint; @@ -8,16 +8,13 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::hir_id::OwnerId; use rustc_macros::HashStable; -use rustc_query_system::ich::StableHashingContext; use rustc_span::{ErrorGuaranteed, Span}; pub use sealed::IntoQueryParam; -use crate::dep_graph; use crate::dep_graph::{DepKind, DepNodeIndex, SerializedDepNodeIndex}; -use crate::queries::{ - ExternProviders, PerQueryVTables, Providers, QueryArenas, QueryCaches, QueryEngine, QueryStates, -}; -use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; +use crate::ich::StableHashingContext; +use crate::queries::{ExternProviders, Providers, QueryArenas, QueryVTables}; +use crate::query::on_disk_cache::OnDiskCache; use crate::query::stack::{QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra}; use crate::query::{QueryCache, QueryInfo, QueryJob}; use crate::ty::TyCtxt; @@ -60,25 +57,10 @@ pub enum ActiveKeyStatus<'tcx> { #[derive(Copy, Clone)] pub enum CycleErrorHandling { Error, - Fatal, DelayBug, Stash, } -pub type WillCacheOnDiskForKeyFn<'tcx, Key> = fn(tcx: TyCtxt<'tcx>, key: &Key) -> bool; - -pub type TryLoadFromDiskFn<'tcx, Key, Value> = fn( - tcx: TyCtxt<'tcx>, - key: &Key, - prev_index: SerializedDepNodeIndex, - index: DepNodeIndex, -) -> Option; - -pub type IsLoadableFromDiskFn<'tcx, Key> = - fn(tcx: TyCtxt<'tcx>, key: &Key, index: SerializedDepNodeIndex) -> bool; - -pub type HashResult = Option, &V) -> Fingerprint>; - #[derive(Clone, Debug)] pub struct CycleError { /// The query and related span that uses the cycle. @@ -97,32 +79,40 @@ pub fn lift(&self) -> CycleError { #[derive(Debug)] pub enum QueryMode { + /// This is a normal query call to `tcx.$query(..)` or `tcx.at(span).$query(..)`. Get, - Ensure { check_cache: bool }, + /// This is a call to `tcx.ensure_ok().$query(..)` or `tcx.ensure_done().$query(..)`. + Ensure { ensure_mode: EnsureMode }, } -/// Stores function pointers and other metadata for a particular query. -/// -/// Used indirectly by query plumbing in `rustc_query_system` via a trait, -/// and also used directly by query plumbing in `rustc_query_impl`. +/// Distinguishes between `tcx.ensure_ok()` and `tcx.ensure_done()` in shared +/// code paths that handle both modes. +#[derive(Debug)] +pub enum EnsureMode { + /// Corresponds to [`TyCtxt::ensure_ok`]. + Ok, + /// Corresponds to [`TyCtxt::ensure_done`]. + Done, +} + +/// Stores data and metadata (e.g. function pointers) for a particular query. pub struct QueryVTable<'tcx, C: QueryCache> { pub name: &'static str, + + /// True if this query has the `anon` modifier. + pub anon: bool, + /// True if this query has the `eval_always` modifier. pub eval_always: bool, + /// True if this query has the `depth_limit` modifier. + pub depth_limit: bool, + /// True if this query has the `feedable` modifier. + pub feedable: bool, + pub dep_kind: DepKind, /// How this query deals with query cycle errors. pub cycle_error_handling: CycleErrorHandling, - // Offset of this query's state field in the QueryStates struct - pub query_state: usize, - // Offset of this query's cache field in the QueryCaches struct - pub query_cache: usize, - pub will_cache_on_disk_for_key_fn: Option>, - - /// Function pointer that calls `tcx.$query(key)` for this query and - /// discards the returned value. - /// - /// This is a weird thing to be doing, and probably not what you want. - /// It is used for loading query results from disk-cache in some cases. - pub call_query_method_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key), + pub state: QueryState<'tcx, C::Key>, + pub cache: C, /// Function pointer that actually calls this query's provider. /// Also performs some associated secondary tasks; see the macro-defined @@ -131,11 +121,25 @@ pub struct QueryVTable<'tcx, C: QueryCache> { /// This should be the only code that calls the provider function. pub invoke_provider_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, - pub try_load_from_disk_fn: Option>, - pub is_loadable_from_disk_fn: Option>, - pub hash_result: HashResult, + pub will_cache_on_disk_for_key_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> bool, + + pub try_load_from_disk_fn: fn( + tcx: TyCtxt<'tcx>, + key: C::Key, + prev_index: SerializedDepNodeIndex, + index: DepNodeIndex, + ) -> Option, + + pub is_loadable_from_disk_fn: + fn(tcx: TyCtxt<'tcx>, key: C::Key, index: SerializedDepNodeIndex) -> bool, + + /// Function pointer that hashes this query's result values. + /// + /// For `no_hash` queries, this function pointer is None. + pub hash_value_fn: Option, &C::Value) -> Fingerprint>, + pub value_from_cycle_error: - fn(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) -> C::Value, + fn(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> C::Value, pub format_value: fn(&C::Value) -> String, /// Formats a human-readable description of this query and its key, as @@ -143,25 +147,34 @@ pub struct QueryVTable<'tcx, C: QueryCache> { /// /// Used when reporting query cycle errors and similar problems. pub description_fn: fn(TyCtxt<'tcx>, C::Key) -> String, + + /// Function pointer that is called by the query methods on [`TyCtxt`] and + /// friends[^1], after they have checked the in-memory cache and found no + /// existing value for this key. + /// + /// Transitive responsibilities include trying to load a disk-cached value + /// if possible (incremental only), invoking the query provider if necessary, + /// and putting the obtained value into the in-memory cache. + /// + /// [^1]: [`TyCtxt`], [`TyCtxtAt`], [`TyCtxtEnsureOk`], [`TyCtxtEnsureDone`] + pub execute_query_fn: fn(TyCtxt<'tcx>, Span, C::Key, QueryMode) -> Option, } -pub struct QuerySystemFns { - pub engine: QueryEngine, - pub local_providers: Providers, - pub extern_providers: ExternProviders, - pub encode_query_results: for<'tcx> fn( - tcx: TyCtxt<'tcx>, - encoder: &mut CacheEncoder<'_, 'tcx>, - query_result_index: &mut EncodedDepNodeIndex, - ), - pub try_mark_green: for<'tcx> fn(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool, +impl<'tcx, C: QueryCache> fmt::Debug for QueryVTable<'tcx, C> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // When debug-printing a query vtable (e.g. for ICE or tracing), + // just print the query name to know what query we're dealing with. + // The other fields and flags are probably just unhelpful noise. + // + // If there is need for a more detailed dump of all flags and fields, + // consider writing a separate dump method and calling it explicitly. + f.write_str(self.name) + } } pub struct QuerySystem<'tcx> { - pub states: QueryStates<'tcx>, pub arenas: WorkerLocal>, - pub caches: QueryCaches<'tcx>, - pub query_vtables: PerQueryVTables<'tcx>, + pub query_vtables: QueryVTables<'tcx>, /// This provides access to the incremental compilation on-disk cache for query results. /// Do not access this directly. It is only meant to be used by @@ -169,7 +182,8 @@ pub struct QuerySystem<'tcx> { /// This is `None` if we are not incremental compilation mode pub on_disk_cache: Option, - pub fns: QuerySystemFns, + pub local_providers: Providers, + pub extern_providers: ExternProviders, pub jobs: AtomicU64, } @@ -194,6 +208,12 @@ pub struct TyCtxtEnsureOk<'tcx> { pub tcx: TyCtxt<'tcx>, } +#[derive(Copy, Clone)] +#[must_use] +pub struct TyCtxtEnsureResult<'tcx> { + pub tcx: TyCtxt<'tcx>, +} + #[derive(Copy, Clone)] #[must_use] pub struct TyCtxtEnsureDone<'tcx> { @@ -201,6 +221,8 @@ pub struct TyCtxtEnsureDone<'tcx> { } impl<'tcx> TyCtxt<'tcx> { + /// FIXME: `ensure_ok`'s effects are subtle. Is this comment fully accurate? + /// /// Wrapper that calls queries in a special "ensure OK" mode, for callers /// that don't need the return value and just want to invoke a query for /// its potential side-effect of emitting fatal errors. @@ -221,17 +243,21 @@ impl<'tcx> TyCtxt<'tcx> { /// /// Therefore, this call mode is not appropriate for callers that want to /// ensure that the query is _never_ executed in the future. - /// - /// ## `return_result_from_ensure_ok` - /// If a query has the `return_result_from_ensure_ok` modifier, calls via - /// `ensure_ok` will instead return `Result<(), ErrorGuaranteed>`. If the - /// query needs to be executed, and execution returns an error, that error - /// is returned to the caller. #[inline(always)] pub fn ensure_ok(self) -> TyCtxtEnsureOk<'tcx> { TyCtxtEnsureOk { tcx: self } } + /// This is a variant of `ensure_ok` only usable with queries that return + /// `Result<_, ErrorGuaranteed>`. Queries calls through this function will + /// return `Result<(), ErrorGuaranteed>`. I.e. the error status is returned + /// but nothing else. As with `ensure_ok`, this can be more efficient than + /// a normal query call. + #[inline(always)] + pub fn ensure_result(self) -> TyCtxtEnsureResult<'tcx> { + TyCtxtEnsureResult { tcx: self } + } + /// Wrapper that calls queries in a special "ensure done" mode, for callers /// that don't need the return value and just want to guarantee that the /// query won't be executed in the future, by executing it now if necessary. @@ -259,24 +285,6 @@ pub fn ensure_done(self) -> TyCtxtEnsureDone<'tcx> { pub fn at(self, span: Span) -> TyCtxtAt<'tcx> { TyCtxtAt { tcx: self, span } } - - pub fn try_mark_green(self, dep_node: &dep_graph::DepNode) -> bool { - (self.query_system.fns.try_mark_green)(self, dep_node) - } -} - -/// Calls either `query_ensure` or `query_ensure_error_guaranteed`, depending -/// on whether the list of modifiers contains `return_result_from_ensure_ok`. -macro_rules! query_ensure_select { - ([]$($args:tt)*) => { - crate::query::inner::query_ensure($($args)*) - }; - ([(return_result_from_ensure_ok) $($rest:tt)*]$($args:tt)*) => { - crate::query::inner::query_ensure_error_guaranteed($($args)*) - }; - ([$other:tt $($modifiers:tt)*]$($args:tt)*) => { - query_ensure_select!([$($modifiers)*]$($args)*) - }; } macro_rules! query_helper_param_ty { @@ -285,246 +293,239 @@ macro_rules! query_helper_param_ty { ($K:ty) => { $K }; } -macro_rules! query_if_arena { - ([] $arena:tt $no_arena:tt) => { - $no_arena - }; - ([(arena_cache) $($rest:tt)*] $arena:tt $no_arena:tt) => { - $arena - }; - ([$other:tt $($modifiers:tt)*]$($args:tt)*) => { - query_if_arena!([$($modifiers)*]$($args)*) - }; -} - -/// If `separate_provide_extern`, then the key can be projected to its -/// local key via `<$K as AsLocalKey>::LocalKey`. -macro_rules! local_key_if_separate_extern { - ([] $($K:tt)*) => { - $($K)* - }; - ([(separate_provide_extern) $($rest:tt)*] $($K:tt)*) => { - <$($K)* as $crate::query::AsLocalKey>::LocalKey - }; - ([$other:tt $($modifiers:tt)*] $($K:tt)*) => { - local_key_if_separate_extern!([$($modifiers)*] $($K)*) - }; -} - -macro_rules! separate_provide_extern_decl { - ([][$name:ident]) => { - () - }; - ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => { - for<'tcx> fn( - TyCtxt<'tcx>, - $name::Key<'tcx>, - ) -> $name::ProvidedValue<'tcx> - }; - ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { - separate_provide_extern_decl!([$($modifiers)*][$($args)*]) - }; -} - -macro_rules! ensure_ok_result { - ( [] ) => { - () - }; - ( [(return_result_from_ensure_ok) $($rest:tt)*] ) => { - Result<(), ErrorGuaranteed> - }; - ( [$other:tt $($modifiers:tt)*] ) => { - ensure_ok_result!( [$($modifiers)*] ) - }; -} - -macro_rules! separate_provide_extern_default { - ([][$name:ident]) => { - () - }; - ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => { - |_, key| $crate::query::plumbing::default_extern_query(stringify!($name), &key) - }; - ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { - separate_provide_extern_default!([$($modifiers)*][$($args)*]) - }; -} - macro_rules! define_callbacks { ( - $( - $(#[$attr:meta])* - [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty, - )* + // You might expect the key to be `$K:ty`, but it needs to be `$($K:tt)*` so that + // `query_helper_param_ty!` can match on specific type names. + queries { + $( + $(#[$attr:meta])* + fn $name:ident($($K:tt)*) -> $V:ty + { + // Search for (QMODLIST) to find all occurrences of this query modifier list. + anon: $anon:literal, + arena_cache: $arena_cache:literal, + cache_on_disk: $cache_on_disk:literal, + cycle_error_handling: $cycle_error_handling:ident, + depth_limit: $depth_limit:literal, + eval_always: $eval_always:literal, + feedable: $feedable:literal, + no_hash: $no_hash:literal, + returns_error_guaranteed: $returns_error_guaranteed:literal, + separate_provide_extern: $separate_provide_extern:literal, + } + )* + } + // Non-queries are unused here. + non_queries { $($_:tt)* } ) => { - $(#[allow(unused_lifetimes)] pub mod $name { - use super::*; - use $crate::query::erase::{self, Erased}; + $( + #[allow(unused_lifetimes)] + pub mod $name { + use super::*; + use $crate::query::erase::{self, Erased}; - pub type Key<'tcx> = $($K)*; - pub type Value<'tcx> = $V; + pub type Key<'tcx> = $($K)*; + pub type Value<'tcx> = $V; - pub type LocalKey<'tcx> = local_key_if_separate_extern!([$($modifiers)*] $($K)*); + /// Key type used by provider functions in `local_providers`. + /// This query has the `separate_provide_extern` modifier. + #[cfg($separate_provide_extern)] + pub type LocalKey<'tcx> = + as $crate::query::AsLocalQueryKey>::LocalQueryKey; + /// Key type used by provider functions in `local_providers`. + #[cfg(not($separate_provide_extern))] + pub type LocalKey<'tcx> = Key<'tcx>; - /// This type alias specifies the type returned from query providers and the type - /// used for decoding. For regular queries this is the declared returned type `V`, - /// but `arena_cache` will use `::Provided` instead. - pub type ProvidedValue<'tcx> = query_if_arena!( - [$($modifiers)*] - (<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided) - ($V) - ); + /// Type returned from query providers and loaded from disk-cache. + #[cfg($arena_cache)] + pub type ProvidedValue<'tcx> = + as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided; + /// Type returned from query providers and loaded from disk-cache. + #[cfg(not($arena_cache))] + pub type ProvidedValue<'tcx> = Value<'tcx>; + + /// This helper function takes a value returned by the query provider + /// (or loaded from disk, or supplied by query feeding), allocates + /// it in an arena if requested by the `arena_cache` modifier, and + /// then returns an erased copy of it. + #[inline(always)] + pub fn provided_to_erased<'tcx>( + tcx: TyCtxt<'tcx>, + provided_value: ProvidedValue<'tcx>, + ) -> Erased> { + // For queries with the `arena_cache` modifier, store the + // provided value in an arena and get a reference to it. + #[cfg($arena_cache)] + let value: Value<'tcx> = { + use $crate::query::arena_cached::ArenaCached; + as ArenaCached>::alloc_in_arena( + tcx, + &tcx.query_system.arenas.$name, + provided_value, + ) + }; - /// This helper function takes a value returned by the query provider - /// (or loaded from disk, or supplied by query feeding), allocates - /// it in an arena if requested by the `arena_cache` modifier, and - /// then returns an erased copy of it. - #[inline(always)] - pub fn provided_to_erased<'tcx>( - tcx: TyCtxt<'tcx>, - provided_value: ProvidedValue<'tcx>, - ) -> Erased> { - // For queries with the `arena_cache` modifier, store the - // provided value in an arena and get a reference to it. - let value: Value<'tcx> = query_if_arena!([$($modifiers)*] { - <$V as $crate::query::arena_cached::ArenaCached>::alloc_in_arena( - tcx, - &tcx.query_system.arenas.$name, - provided_value, - ) - } { // Otherwise, the provided value is the value (and `tcx` is unused). - let _ = tcx; - provided_value - }); - erase::erase_val(value) + #[cfg(not($arena_cache))] + let value: Value<'tcx> = { + let _ = tcx; + provided_value + }; + + erase::erase_val(value) + } + + pub type Cache<'tcx> = + as $crate::query::QueryKey>::Cache>>; + + // Ensure that keys grow no larger than 88 bytes by accident. + // Increase this limit if necessary, but do try to keep the size low if possible + #[cfg(target_pointer_width = "64")] + const _: () = { + if size_of::>() > 88 { + panic!("{}", concat!( + "the query `", + stringify!($name), + "` has a key type `", + stringify!($($K)*), + "` that is too large" + )); + } + }; + + // Ensure that values grow no larger than 64 bytes by accident. + // Increase this limit if necessary, but do try to keep the size low if possible + #[cfg(target_pointer_width = "64")] + #[cfg(not(feature = "rustc_randomized_layouts"))] + const _: () = { + if size_of::>() > 64 { + panic!("{}", concat!( + "the query `", + stringify!($name), + "` has a value type `", + stringify!($V), + "` that is too large" + )); + } + }; } - - pub type Storage<'tcx> = <$($K)* as $crate::query::Key>::Cache>; - - // Ensure that keys grow no larger than 88 bytes by accident. - // Increase this limit if necessary, but do try to keep the size low if possible - #[cfg(target_pointer_width = "64")] - const _: () = { - if size_of::>() > 88 { - panic!("{}", concat!( - "the query `", - stringify!($name), - "` has a key type `", - stringify!($($K)*), - "` that is too large" - )); - } - }; - - // Ensure that values grow no larger than 64 bytes by accident. - // Increase this limit if necessary, but do try to keep the size low if possible - #[cfg(target_pointer_width = "64")] - #[cfg(not(feature = "rustc_randomized_layouts"))] - const _: () = { - if size_of::>() > 64 { - panic!("{}", concat!( - "the query `", - stringify!($name), - "` has a value type `", - stringify!($V), - "` that is too large" - )); - } - }; - })* + )* /// Holds per-query arenas for queries with the `arena_cache` modifier. #[derive(Default)] pub struct QueryArenas<'tcx> { $( - $(#[$attr])* - pub $name: query_if_arena!([$($modifiers)*] - // Use the `ArenaCached` helper trait to determine the arena's value type. - (TypedArena<<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Allocated>) - // No arena for this query, so the field type is `()`. - () - ), + // Use the `ArenaCached` helper trait to determine the arena's value type. + #[cfg($arena_cache)] + pub $name: TypedArena< + <$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Allocated, + >, )* } - #[derive(Default)] - pub struct QueryCaches<'tcx> { - $($(#[$attr])* pub $name: $name::Storage<'tcx>,)* - } - impl<'tcx> $crate::query::TyCtxtEnsureOk<'tcx> { - $($(#[$attr])* - #[inline(always)] - pub fn $name( - self, - key: query_helper_param_ty!($($K)*), - ) -> ensure_ok_result!([$($modifiers)*]) { - query_ensure_select!( - [$($modifiers)*] - self.tcx, - self.tcx.query_system.fns.engine.$name, - &self.tcx.query_system.caches.$name, - $crate::query::IntoQueryParam::into_query_param(key), - false, - ) - })* - } - - impl<'tcx> $crate::query::TyCtxtEnsureDone<'tcx> { - $($(#[$attr])* - #[inline(always)] - pub fn $name(self, key: query_helper_param_ty!($($K)*)) { - crate::query::inner::query_ensure( - self.tcx, - self.tcx.query_system.fns.engine.$name, - &self.tcx.query_system.caches.$name, - $crate::query::IntoQueryParam::into_query_param(key), - true, - ); - })* - } - - impl<'tcx> TyCtxt<'tcx> { - $($(#[$attr])* - #[inline(always)] - #[must_use] - pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V - { - self.at(DUMMY_SP).$name(key) - })* - } - - impl<'tcx> $crate::query::TyCtxtAt<'tcx> { - $($(#[$attr])* - #[inline(always)] - pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V - { - use $crate::query::{erase, inner}; - - erase::restore_val::<$V>(inner::query_get_at( - self.tcx, - self.tcx.query_system.fns.engine.$name, - &self.tcx.query_system.caches.$name, - self.span, - $crate::query::IntoQueryParam::into_query_param(key), - )) - })* - } - - /// Holds a `QueryVTable` for each query. - /// - /// ("Per" just makes this pluralized name more visually distinct.) - pub struct PerQueryVTables<'tcx> { $( - pub $name: ::rustc_middle::query::plumbing::QueryVTable<'tcx, $name::Storage<'tcx>>, + $(#[$attr])* + #[inline(always)] + pub fn $name(self, key: query_helper_param_ty!($($K)*)) { + crate::query::inner::query_ensure_ok_or_done( + self.tcx, + &self.tcx.query_system.query_vtables.$name, + $crate::query::IntoQueryParam::into_query_param(key), + $crate::query::EnsureMode::Ok, + ) + } )* } - #[derive(Default)] - pub struct QueryStates<'tcx> { + // Only defined when the `ensure_result` modifier is present. + impl<'tcx> $crate::query::TyCtxtEnsureResult<'tcx> { $( - pub $name: $crate::query::QueryState<'tcx, $($K)*>, + #[cfg($returns_error_guaranteed)] + $(#[$attr])* + #[inline(always)] + pub fn $name( + self, + key: query_helper_param_ty!($($K)*), + ) -> Result<(), rustc_errors::ErrorGuaranteed> { + crate::query::inner::query_ensure_result( + self.tcx, + &self.tcx.query_system.query_vtables.$name, + $crate::query::IntoQueryParam::into_query_param(key), + ) + } + )* + } + + impl<'tcx> $crate::query::TyCtxtEnsureDone<'tcx> { + $( + $(#[$attr])* + #[inline(always)] + pub fn $name(self, key: query_helper_param_ty!($($K)*)) { + crate::query::inner::query_ensure_ok_or_done( + self.tcx, + &self.tcx.query_system.query_vtables.$name, + $crate::query::IntoQueryParam::into_query_param(key), + $crate::query::EnsureMode::Done, + ); + } + )* + } + + impl<'tcx> TyCtxt<'tcx> { + $( + $(#[$attr])* + #[inline(always)] + #[must_use] + pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V { + self.at(DUMMY_SP).$name(key) + } + )* + } + + impl<'tcx> $crate::query::TyCtxtAt<'tcx> { + $( + $(#[$attr])* + #[inline(always)] + pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V { + use $crate::query::{erase, inner}; + + erase::restore_val::<$V>(inner::query_get_at( + self.tcx, + self.span, + &self.tcx.query_system.query_vtables.$name, + $crate::query::IntoQueryParam::into_query_param(key), + )) + } + )* + } + + $( + #[cfg($feedable)] + impl<'tcx, K: $crate::query::IntoQueryParam<$name::Key<'tcx>> + Copy> + TyCtxtFeed<'tcx, K> + { + $(#[$attr])* + #[inline(always)] + pub fn $name(self, value: $name::ProvidedValue<'tcx>) { + let key = self.key().into_query_param(); + let erased_value = $name::provided_to_erased(self.tcx, value); + $crate::query::inner::query_feed( + self.tcx, + dep_graph::DepKind::$name, + &self.tcx.query_system.query_vtables.$name, + key, + erased_value, + ); + } + } + )* + + /// Holds a `QueryVTable` for each query. + pub struct QueryVTables<'tcx> { + $( + pub $name: ::rustc_middle::query::plumbing::QueryVTable<'tcx, $name::Cache<'tcx>>, )* } @@ -540,13 +541,23 @@ pub struct Providers { } pub struct ExternProviders { - $(pub $name: separate_provide_extern_decl!([$($modifiers)*][$name]),)* + $( + #[cfg($separate_provide_extern)] + pub $name: for<'tcx> fn( + TyCtxt<'tcx>, + $name::Key<'tcx>, + ) -> $name::ProvidedValue<'tcx>, + )* } impl Default for Providers { fn default() -> Self { Providers { - $($name: |_, key| $crate::query::plumbing::default_query(stringify!($name), &key)),* + $( + $name: |_, key| { + $crate::query::plumbing::default_query(stringify!($name), &key) + }, + )* } } } @@ -554,7 +565,13 @@ fn default() -> Self { impl Default for ExternProviders { fn default() -> Self { ExternProviders { - $($name: separate_provide_extern_default!([$($modifiers)*][$name]),)* + $( + #[cfg($separate_provide_extern)] + $name: |_, key| $crate::query::plumbing::default_extern_query( + stringify!($name), + &key, + ), + )* } } } @@ -568,56 +585,9 @@ impl Copy for ExternProviders {} impl Clone for ExternProviders { fn clone(&self) -> Self { *self } } - - pub struct QueryEngine { - $(pub $name: for<'tcx> fn( - TyCtxt<'tcx>, - Span, - $name::Key<'tcx>, - $crate::query::QueryMode, - ) -> Option<$crate::query::erase::Erased<$V>>,)* - } }; } -macro_rules! define_feedable { - ($($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { - $(impl<'tcx, K: $crate::query::IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> { - $(#[$attr])* - #[inline(always)] - pub fn $name(self, value: $name::ProvidedValue<'tcx>) { - let key = self.key().into_query_param(); - - let tcx = self.tcx; - let erased_value = $name::provided_to_erased(tcx, value); - - let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name; - - $crate::query::inner::query_feed( - tcx, - dep_kind, - &tcx.query_system.query_vtables.$name, - &tcx.query_system.caches.$name, - key, - erased_value, - ); - } - })* - } -} - -// Each of these queries corresponds to a function pointer field in the -// `Providers` struct for requesting a value of that type, and a method -// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way -// which memoizes and does dep-graph tracking, wrapping around the actual -// `Providers` that the driver creates (using several `rustc_*` crates). -// -// The result type of each query must implement `Clone`, and additionally -// `ty::query::values::Value`, which produces an appropriate placeholder -// (error) value if the query resulted in a query cycle. -// Queries marked with `cycle_fatal` do not need the latter implementation, -// as they will raise an fatal error on query cycles instead. - mod sealed { use rustc_hir::def_id::{LocalModDefId, ModDefId}; @@ -638,13 +608,6 @@ fn into_query_param(self) -> P { } } - impl<'a, P: Copy> IntoQueryParam

for &'a P { - #[inline(always)] - fn into_query_param(self) -> P { - *self - } - } - impl IntoQueryParam for OwnerId { #[inline(always)] fn into_query_param(self) -> LocalDefId { diff --git a/compiler/rustc_middle/src/query/stack.rs b/compiler/rustc_middle/src/query/stack.rs index b2b01517b7ee..fd80c7edd602 100644 --- a/compiler/rustc_middle/src/query/stack.rs +++ b/compiler/rustc_middle/src/query/stack.rs @@ -4,7 +4,6 @@ use std::sync::Arc; use rustc_data_structures::sync::{DynSend, DynSync}; -use rustc_hashes::Hash64; use rustc_hir::def::DefKind; use rustc_span::Span; use rustc_span::def_id::DefId; @@ -23,9 +22,6 @@ pub struct QueryStackFrame { pub info: I, pub dep_kind: DepKind, - /// This hash is used to deterministically pick - /// a query to remove cycles in the parallel compiler. - pub hash: Hash64, pub def_id: Option, /// A def-id that is extracted from a `Ty` in a query key pub def_id_for_ty_in_cycle: Option, @@ -36,18 +32,16 @@ impl<'tcx> QueryStackFrame> { pub fn new( info: QueryStackDeferred<'tcx>, dep_kind: DepKind, - hash: Hash64, def_id: Option, def_id_for_ty_in_cycle: Option, ) -> Self { - Self { info, def_id, dep_kind, hash, def_id_for_ty_in_cycle } + Self { info, def_id, dep_kind, def_id_for_ty_in_cycle } } pub fn lift(&self) -> QueryStackFrame { QueryStackFrame { info: self.info.extract(), dep_kind: self.dep_kind, - hash: self.hash, def_id: self.def_id, def_id_for_ty_in_cycle: self.def_id_for_ty_in_cycle, } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 5e8031403565..18456e763078 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -268,10 +268,6 @@ pub enum ExprKind<'tcx> { hir_id: HirId, value: ExprId, }, - /// A `box ` expression. - Box { - value: ExprId, - }, /// An `if` expression. If { if_then_scope: region::Scope, diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 8e0e3aa294b8..c17e15513cdf 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -48,7 +48,6 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( let Expr { kind, ty: _, temp_scope_id: _, span: _ } = expr; match *kind { Scope { value, region_scope: _, hir_id: _ } => visitor.visit_expr(&visitor.thir()[value]), - Box { value } => visitor.visit_expr(&visitor.thir()[value]), If { cond, then, else_opt, if_then_scope: _ } => { visitor.visit_expr(&visitor.thir()[cond]); visitor.visit_expr(&visitor.thir()[then]); diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 84415a592a2e..c50168277552 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -1,6 +1,5 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_hir::find_attr; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; @@ -62,7 +61,7 @@ pub enum OverlapMode { impl OverlapMode { pub fn get(tcx: TyCtxt<'_>, trait_id: DefId) -> OverlapMode { let with_negative_coherence = tcx.features().with_negative_coherence(); - let strict_coherence = find_attr!(tcx.get_all_attrs(trait_id), AttributeKind::RustcStrictCoherence(span) => *span); + let strict_coherence = find_attr!(tcx, trait_id, RustcStrictCoherence(span) => *span); if with_negative_coherence { if strict_coherence.is_some() { OverlapMode::Strict } else { OverlapMode::WithNegative } @@ -176,7 +175,7 @@ pub struct LeafDef { /// The node in the specialization graph containing the definition of `item`. pub defining_node: Node, - /// The "top-most" (ie. least specialized) specialization graph node that finalized the + /// The "top-most" (i.e. least specialized) specialization graph node that finalized the /// definition of `item`. /// /// Example: @@ -211,7 +210,7 @@ pub fn is_final(&self) -> bool { } impl<'tcx> Ancestors<'tcx> { - /// Finds the bottom-most (ie. most specialized) definition of an associated + /// Finds the bottom-most (i.e. most specialized) definition of an associated /// item. pub fn leaf_def(mut self, tcx: TyCtxt<'tcx>, trait_item_def_id: DefId) -> Option { let mut finalizing_node = None; diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 510c546f82a4..ac51e5bd2289 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -3,29 +3,30 @@ use std::ops::Range; use std::str; -use rustc_abi::{FIRST_VARIANT, ReprOptions, VariantIdx}; +use rustc_abi::{FIRST_VARIANT, FieldIdx, ReprOptions, VariantIdx}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher}; use rustc_errors::ErrorGuaranteed; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_index::{IndexSlice, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; -use rustc_query_system::ich::StableHashingContext; use rustc_session::DataTypeKind; +use rustc_span::sym; +use rustc_type_ir::FieldInfo; use rustc_type_ir::solve::AdtDestructorKind; use tracing::{debug, info, trace}; use super::{ AsyncDestructor, Destructor, FieldDef, GenericPredicates, Ty, TyCtxt, VariantDef, VariantDiscr, }; +use crate::ich::StableHashingContext; use crate::mir::interpret::ErrorHandled; -use crate::ty; use crate::ty::util::{Discr, IntTypeExt}; +use crate::ty::{self, ConstKind}; #[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] pub struct AdtFlags(u16); @@ -59,6 +60,10 @@ impl AdtFlags: u16 { const IS_PIN = 1 << 11; /// Indicates whether the type is `#[pin_project]`. const IS_PIN_PROJECT = 1 << 12; + /// Indicates whether the type is `FieldRepresentingType`. + const IS_FIELD_REPRESENTING_TYPE = 1 << 13; + /// Indicates whether the type is `MaybeDangling<_>`. + const IS_MAYBE_DANGLING = 1 << 14; } } rustc_data_structures::external_bitflags_debug! { AdtFlags } @@ -201,6 +206,51 @@ pub fn flags(self) -> AdtFlags { pub fn repr(self) -> ReprOptions { self.0.0.repr } + + pub fn field_representing_type_info( + self, + tcx: TyCtxt<'tcx>, + args: ty::GenericArgsRef<'tcx>, + ) -> Option>> { + if !self.is_field_representing_type() { + return None; + } + let base = args.type_at(0); + let variant_idx = match args.const_at(1).kind() { + ConstKind::Value(v) => VariantIdx::from_u32(v.to_leaf().to_u32()), + _ => return None, + }; + let field_idx = match args.const_at(2).kind() { + ConstKind::Value(v) => FieldIdx::from_u32(v.to_leaf().to_u32()), + _ => return None, + }; + let (ty, variant, name) = match base.kind() { + ty::Adt(base_def, base_args) => { + let variant = base_def.variant(variant_idx); + let field = &variant.fields[field_idx]; + (field.ty(tcx, base_args), base_def.is_enum().then_some(variant.name), field.name) + } + ty::Tuple(tys) => { + if variant_idx != FIRST_VARIANT { + bug!("expected variant of tuple to be FIRST_VARIANT, but found {variant_idx:?}") + } + ( + if let Some(ty) = tys.get(field_idx.index()) { + *ty + } else { + bug!( + "expected valid tuple index, but got {field_idx:?}, tuple length: {}", + tys.len() + ) + }, + None, + sym::integer(field_idx.index()), + ) + } + _ => panic!(), + }; + Some(FieldInfo { base, ty, variant, variant_idx, name, field_idx }) + } } impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { @@ -212,6 +262,10 @@ fn is_struct(self) -> bool { self.is_struct() } + fn is_packed(self) -> bool { + self.repr().packed() + } + fn struct_tail_ty(self, interner: TyCtxt<'tcx>) -> Option>> { Some(interner.type_of(self.non_enum_variant().tail_opt()?.did)) } @@ -224,6 +278,14 @@ fn is_manually_drop(self) -> bool { self.is_manually_drop() } + fn field_representing_type_info( + self, + tcx: TyCtxt<'tcx>, + args: ty::GenericArgsRef<'tcx>, + ) -> Option>> { + self.field_representing_type_info(tcx, args) + } + fn all_field_tys( self, tcx: TyCtxt<'tcx>, @@ -282,13 +344,11 @@ pub(super) fn new( debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr); let mut flags = AdtFlags::NO_ADT_FLAGS; - if kind == AdtKind::Enum - && find_attr!(tcx.get_all_attrs(did), AttributeKind::NonExhaustive(..)) - { + if kind == AdtKind::Enum && find_attr!(tcx, did, NonExhaustive(..)) { debug!("found non-exhaustive variant list for {:?}", did); flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; } - if find_attr!(tcx.get_all_attrs(did), AttributeKind::PinV2(..)) { + if find_attr!(tcx, did, PinV2(..)) { debug!("found pin-project type {:?}", did); flags |= AdtFlags::IS_PIN_PROJECT; } @@ -303,7 +363,7 @@ pub(super) fn new( flags |= AdtFlags::HAS_CTOR; } - if find_attr!(tcx.get_all_attrs(did), AttributeKind::Fundamental) { + if find_attr!(tcx, did, Fundamental) { flags |= AdtFlags::IS_FUNDAMENTAL; } if tcx.is_lang_item(did, LangItem::PhantomData) { @@ -315,6 +375,9 @@ pub(super) fn new( if tcx.is_lang_item(did, LangItem::ManuallyDrop) { flags |= AdtFlags::IS_MANUALLY_DROP; } + if tcx.is_lang_item(did, LangItem::MaybeDangling) { + flags |= AdtFlags::IS_MAYBE_DANGLING; + } if tcx.is_lang_item(did, LangItem::UnsafeCell) { flags |= AdtFlags::IS_UNSAFE_CELL; } @@ -324,6 +387,9 @@ pub(super) fn new( if tcx.is_lang_item(did, LangItem::Pin) { flags |= AdtFlags::IS_PIN; } + if tcx.is_lang_item(did, LangItem::FieldRepresentingType) { + flags |= AdtFlags::IS_FIELD_REPRESENTING_TYPE; + } AdtDefData { did, variants, flags, repr } } @@ -439,6 +505,12 @@ pub fn is_manually_drop(self) -> bool { self.flags().contains(AdtFlags::IS_MANUALLY_DROP) } + /// Returns `true` if this is `MaybeDangling`. + #[inline] + pub fn is_maybe_dangling(self) -> bool { + self.flags().contains(AdtFlags::IS_MAYBE_DANGLING) + } + /// Returns `true` if this is `Pin`. #[inline] pub fn is_pin(self) -> bool { @@ -452,6 +524,10 @@ pub fn is_pin_project(self) -> bool { self.flags().contains(AdtFlags::IS_PIN_PROJECT) } + pub fn is_field_representing_type(self) -> bool { + self.flags().contains(AdtFlags::IS_FIELD_REPRESENTING_TYPE) + } + /// Returns `true` if this type has a destructor. pub fn has_dtor(self, tcx: TyCtxt<'tcx>) -> bool { self.destructor(tcx).is_some() @@ -676,8 +752,7 @@ pub fn sizedness_constraint( } } +/// This type exists just so a `FromCycleError` impl can be made for the `check_representability` +/// query. #[derive(Clone, Copy, Debug, HashStable)] -pub enum Representability { - Representable, - Infinite(ErrorGuaranteed), -} +pub struct Representability; diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 8c560df4e162..4cc255cba635 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -30,7 +30,7 @@ pub fn opt_name(&self) -> Option { match self.kind { ty::AssocKind::Type { data: AssocTypeData::Normal(name) } => Some(name), ty::AssocKind::Type { data: AssocTypeData::Rpitit(_) } => None, - ty::AssocKind::Const { name } => Some(name), + ty::AssocKind::Const { name, .. } => Some(name), ty::AssocKind::Fn { name, .. } => Some(name), } } @@ -119,7 +119,7 @@ pub fn signature(&self, tcx: TyCtxt<'_>) -> String { tcx.fn_sig(self.def_id).instantiate_identity().skip_binder().to_string() } ty::AssocKind::Type { .. } => format!("type {};", self.name()), - ty::AssocKind::Const { name } => { + ty::AssocKind::Const { name, .. } => { format!("const {}: {:?};", name, tcx.type_of(self.def_id).instantiate_identity()) } } @@ -173,7 +173,7 @@ pub enum AssocTypeData { #[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)] pub enum AssocKind { - Const { name: Symbol }, + Const { name: Symbol, is_type_const: bool }, Fn { name: Symbol, has_self: bool }, Type { data: AssocTypeData }, } @@ -196,7 +196,9 @@ pub fn tag(&self) -> AssocTag { pub fn as_def_kind(&self) -> DefKind { match self { - Self::Const { .. } => DefKind::AssocConst, + Self::Const { is_type_const, .. } => { + DefKind::AssocConst { is_type_const: *is_type_const } + } Self::Fn { .. } => DefKind::AssocFn, Self::Type { .. } => DefKind::AssocTy, } diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 4856df3a6222..d1987f51f6e0 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -13,6 +13,7 @@ use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; +use rustc_middle::ty::Const; use rustc_serialize::{Decodable, Encodable}; use rustc_span::source_map::Spanned; use rustc_span::{Span, SpanDecoder, SpanEncoder}; @@ -21,9 +22,8 @@ use crate::infer::canonical::{CanonicalVarKind, CanonicalVarKinds}; use crate::mir::interpret::{AllocId, ConstAllocation, CtfeProvenance}; use crate::mir::mono::MonoItem; -use crate::mir::{self}; -use crate::traits; use crate::ty::{self, AdtDef, GenericArgsRef, Ty, TyCtxt}; +use crate::{mir, traits}; /// The shorthand encoding uses an enum's variant index `usize` /// and is offset by this value so it never matches a real variant. @@ -498,6 +498,7 @@ fn decode(d: &mut D) -> Self { &'tcx ty::List>, &'tcx ty::List>, &'tcx ty::ListWithCachedTypeInfo>, + &'tcx ty::List>, } #[macro_export] diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index c9ccb9bd0b3e..fab000c9b1be 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -1,4 +1,5 @@ -use rustc_data_structures::assert_matches; +use std::assert_matches; + use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use super::Const; diff --git a/compiler/rustc_middle/src/ty/consts/lit.rs b/compiler/rustc_middle/src/ty/consts/lit.rs index 3d41925131b4..be6dfb20e043 100644 --- a/compiler/rustc_middle/src/ty/consts/lit.rs +++ b/compiler/rustc_middle/src/ty/consts/lit.rs @@ -1,4 +1,4 @@ -use rustc_ast::LitKind; +use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_hir; use rustc_macros::HashStable; @@ -44,10 +44,17 @@ pub fn const_lit_matches_ty<'tcx>( { true } - (LitKind::Int(..), ty::Uint(_)) if !neg => true, - (LitKind::Int(..), ty::Int(_)) => true, + (LitKind::Int(_, LitIntType::Unsigned(lit_ty)), ty::Uint(expect_ty)) if !neg => { + lit_ty == *expect_ty + } + (LitKind::Int(_, LitIntType::Signed(lit_ty)), ty::Int(expect_ty)) => lit_ty == *expect_ty, + (LitKind::Int(_, LitIntType::Unsuffixed), ty::Uint(_)) if !neg => true, + (LitKind::Int(_, LitIntType::Unsuffixed), ty::Int(_)) => true, (LitKind::Bool(..), ty::Bool) => true, - (LitKind::Float(..), ty::Float(_)) => true, + (LitKind::Float(_, LitFloatType::Suffixed(lit_ty)), ty::Float(expect_ty)) => { + lit_ty == *expect_ty + } + (LitKind::Float(_, LitFloatType::Unsuffixed), ty::Float(_)) => true, (LitKind::Char(..), ty::Char) => true, (LitKind::Err(..), _) => true, _ => false, diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index e3fc9bfee49c..50242613b3e7 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -31,12 +31,6 @@ fn try_to_scalar(&self) -> Option { // recurses through pub struct ValTree<'tcx>(pub(crate) Interned<'tcx, ty::ValTreeKind>>); -impl<'tcx> rustc_type_ir::inherent::ValTree> for ValTree<'tcx> { - fn kind(&self) -> &ty::ValTreeKind> { - &self - } -} - impl<'tcx> ValTree<'tcx> { /// Returns the zero-sized valtree: `Branch([])`. pub fn zst(tcx: TyCtxt<'tcx>) -> Self { @@ -44,7 +38,7 @@ pub fn zst(tcx: TyCtxt<'tcx>) -> Self { } pub fn is_zst(self) -> bool { - matches!(*self, ty::ValTreeKind::Branch(box [])) + matches!(*self, ty::ValTreeKind::Branch(consts) if consts.is_empty()) } pub fn from_raw_bytes(tcx: TyCtxt<'tcx>, bytes: &[u8]) -> Self { @@ -58,7 +52,9 @@ pub fn from_branches( tcx: TyCtxt<'tcx>, branches: impl IntoIterator>, ) -> Self { - tcx.intern_valtree(ty::ValTreeKind::Branch(branches.into_iter().collect())) + tcx.intern_valtree(ty::ValTreeKind::Branch( + tcx.mk_const_list_from_iter(branches.into_iter()), + )) } pub fn from_scalar_int(tcx: TyCtxt<'tcx>, i: ScalarInt) -> Self { @@ -81,6 +77,14 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } +impl<'tcx> rustc_type_ir::inherent::IntoKind for ty::ValTree<'tcx> { + type Kind = ty::ValTreeKind>; + + fn kind(self) -> Self::Kind { + *self.0 + } +} + /// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed /// because the value contains something of type `ty` that is not valtree-compatible. /// The caller can then show an appropriate error; the query does not have the @@ -136,19 +140,24 @@ pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> { ty::Ref(_, inner_ty, _) => match inner_ty.kind() { // `&str` can be interpreted as raw bytes ty::Str => {} - // `&[u8]` can be interpreted as raw bytes - ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {} + // `&[T]` can be interpreted as raw bytes if elements are `u8` + ty::Slice(_) => {} // other `&_` can't be interpreted as raw bytes _ => return None, }, - // `[u8; N]` can be interpreted as raw bytes - ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {} + // `[T; N]` can be interpreted as raw bytes if elements are `u8` + ty::Array(_, _) => {} // Otherwise, type cannot be interpreted as raw bytes _ => return None, } // We create an iterator that yields `Option` - let iterator = self.to_branch().into_iter().map(|ct| Some(ct.try_to_leaf()?.to_u8())); + let iterator = self.to_branch().into_iter().map(|ct| { + (*ct) + .try_to_value() + .and_then(|value| (value.ty == tcx.types.u8).then_some(value)) + .and_then(|value| value.try_to_leaf().map(|leaf| leaf.to_u8())) + }); // If there is `None` in the iterator, then the array is not a valid array of u8s and we return `None` let bytes: Vec = iterator.collect::>>()?; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 430890d5a42d..64af9e7f0998 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -29,18 +29,15 @@ use rustc_data_structures::sync::{ self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal, }; -use rustc_errors::{Applicability, Diag, DiagCtxtHandle, LintDiagnostic, MultiSpan}; -use rustc_hir::attrs::AttributeKind; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, MultiSpan}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::{DefPathData, Definitions, DisambiguatorState}; use rustc_hir::intravisit::VisitorExt; use rustc_hir::lang_items::LangItem; use rustc_hir::limit::Limit; -use rustc_hir::{self as hir, HirId, Node, TraitCandidate, find_attr}; +use rustc_hir::{self as hir, CRATE_HIR_ID, HirId, Node, TraitCandidate, find_attr}; use rustc_index::IndexVec; -use rustc_query_system::ich::StableHashingContext; -use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::Session; use rustc_session::config::CrateType; use rustc_session::cstore::{CrateStoreDyn, Untracked}; @@ -55,8 +52,9 @@ use crate::arena::Arena; use crate::dep_graph::dep_node::make_metadata; use crate::dep_graph::{DepGraph, DepKindVTable, DepNodeIndex}; +use crate::ich::StableHashingContext; use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind}; -use crate::lint::lint_level; +use crate::lint::{diag_lint_level, lint_level}; use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature}; use crate::middle::resolve_bound_vars; @@ -565,7 +563,7 @@ fn new( )) }; - let valtree_zst = mk_valtree(ty::ValTreeKind::Branch(Box::default())); + let valtree_zst = mk_valtree(ty::ValTreeKind::Branch(List::empty())); let valtree_true = mk_valtree(ty::ValTreeKind::Leaf(ty::ScalarInt::TRUE)); let valtree_false = mk_valtree(ty::ValTreeKind::Leaf(ty::ScalarInt::FALSE)); @@ -915,8 +913,8 @@ pub fn body_codegen_attrs(self, def_id: DefId) -> &'tcx CodegenFnAttrs { } else if matches!( def_kind, DefKind::AnonConst - | DefKind::AssocConst - | DefKind::Const + | DefKind::AssocConst { .. } + | DefKind::Const { .. } | DefKind::InlineConst | DefKind::GlobalAsm ) { @@ -994,8 +992,10 @@ pub fn is_sizedness_trait(self, def_id: DefId) -> bool { /// `rustc_layout_scalar_valid_range` attribute. // FIXME(eddyb) this is an awkward spot for this method, maybe move it? pub fn layout_scalar_valid_range(self, def_id: DefId) -> (Bound, Bound) { - let start = find_attr!(self.get_all_attrs(def_id), AttributeKind::RustcLayoutScalarValidRangeStart(n, _) => Bound::Included(**n)).unwrap_or(Bound::Unbounded); - let end = find_attr!(self.get_all_attrs(def_id), AttributeKind::RustcLayoutScalarValidRangeEnd(n, _) => Bound::Included(**n)).unwrap_or(Bound::Unbounded); + let start = find_attr!(self, def_id, RustcLayoutScalarValidRangeStart(n, _) => Bound::Included(**n)).unwrap_or(Bound::Unbounded); + let end = + find_attr!(self, def_id, RustcLayoutScalarValidRangeEnd(n, _) => Bound::Included(**n)) + .unwrap_or(Bound::Unbounded); (start, end) } @@ -1113,13 +1113,13 @@ pub fn type_const_span(self, def_id: DefId) -> Option { /// Check if the given `def_id` is a `type const` (mgca) pub fn is_type_const>(self, def_id: I) -> bool { // No need to call the query directly in this case always false. - if !(matches!( - self.def_kind(def_id.into_query_param()), - DefKind::Const | DefKind::AssocConst - )) { - return false; + let def_kind = self.def_kind(def_id.into_query_param()); + match def_kind { + DefKind::Const { is_type_const } | DefKind::AssocConst { is_type_const } => { + is_type_const + } + _ => false, } - self.is_rhs_type_const(def_id) } /// Returns the movability of the coroutine of `def_id`, or panics @@ -1220,7 +1220,7 @@ pub fn needs_metadata(self) -> bool { pub fn needs_crate_hash(self) -> bool { // Why is the crate hash needed for these configurations? // - debug_assertions: for the "fingerprint the result" check in - // `rustc_query_system::query::plumbing::execute_job`. + // `rustc_query_impl::execution::execute_job`. // - incremental: for query lookups. // - needs_metadata: for putting into crate metadata. // - instrument_coverage: for putting into coverage data (see @@ -1330,8 +1330,8 @@ pub fn adjust_target_feature_sig( caller: DefId, ) -> Option>> { let fun_features = &self.codegen_fn_attrs(fun_def).target_features; - let callee_features = &self.codegen_fn_attrs(caller).target_features; - if self.is_target_feature_call_safe(&fun_features, &callee_features) { + let caller_features = &self.body_codegen_attrs(caller).target_features; + if self.is_target_feature_call_safe(&fun_features, &caller_features) { return Some(fun_sig.map_bound(|sig| ty::FnSig { safety: hir::Safety::Safe, ..sig })); } None @@ -1494,10 +1494,6 @@ pub fn with_stable_hashing_context( f(StableHashingContext::new(self.sess, &self.untracked)) } - pub fn serialize_query_result_cache(self, encoder: FileEncoder) -> FileEncodeResult { - self.query_system.on_disk_cache.as_ref().map_or(Ok(0), |c| c.serialize(self, encoder)) - } - #[inline] pub fn local_crate_exports_generics(self) -> bool { // compiler-builtins has some special treatment in codegen, which can result in confusing @@ -1687,6 +1683,36 @@ pub fn finish(self) { self.sess.dcx().emit_fatal(crate::error::FailedWritingFile { path: &path, error }); } } + + pub fn report_unused_features(self) { + // Collect first to avoid holding the lock while linting. + let used_features = self.sess.used_features.lock(); + let unused_features = self + .features() + .enabled_features_iter_stable_order() + .filter(|(f, _)| { + !used_features.contains_key(f) + // FIXME: `restricted_std` is used to tell a standard library built + // for a platform that it doesn't know how to support. But it + // could only gate a private mod (see `__restricted_std_workaround`) + // with `cfg(not(restricted_std))`, so it cannot be recorded as used + // in downstream crates. It should never be linted, but should we + // hack this in the linter to ignore it? + && f.as_str() != "restricted_std" + }) + .collect::>(); + + for (feature, span) in unused_features { + self.node_span_lint( + rustc_session::lint::builtin::UNUSED_FEATURES, + CRATE_HIR_ID, + span, + |lint| { + lint.primary_message(format!("feature `{}` is declared but not used", feature)); + }, + ); + } + } } macro_rules! nop_lift { @@ -2085,7 +2111,7 @@ pub fn trait_may_define_assoc_item(self, trait_def_id: DefId, assoc_name: Ident) /// Given a `ty`, return whether it's an `impl Future<...>`. pub fn ty_is_opaque_future(self, ty: Ty<'_>) -> bool { - let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind() else { return false }; + let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *ty.kind() else { return false }; let future_trait = self.require_lang_item(LangItem::Future, DUMMY_SP); self.explicit_item_self_bounds(def_id).skip_binder().iter().any(|&(predicate, _)| { @@ -2150,9 +2176,9 @@ fn check_args_compatible_inner( // ATPITs) do not. let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy) && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); - let is_inherent_assoc_type_const = matches!(self.def_kind(def_id), DefKind::AssocConst) - && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }) - && self.is_type_const(def_id); + let is_inherent_assoc_type_const = + matches!(self.def_kind(def_id), DefKind::AssocConst { is_type_const: true }) + && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); let own_args = if !nested && (is_inherent_assoc_ty || is_inherent_assoc_type_const) { if generics.own_params.len() + 1 != args.len() { return false; @@ -2197,9 +2223,12 @@ pub fn debug_assert_args_compatible(self, def_id: DefId, args: &'tcx [ty::Generi if cfg!(debug_assertions) && !self.check_args_compatible(def_id, args) { let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy) && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); - let is_inherent_assoc_type_const = matches!(self.def_kind(def_id), DefKind::AssocConst) - && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }) - && self.is_type_const(def_id); + let is_inherent_assoc_type_const = + matches!(self.def_kind(def_id), DefKind::AssocConst { is_type_const: true }) + && matches!( + self.def_kind(self.parent(def_id)), + DefKind::Impl { of_trait: false } + ); if is_inherent_assoc_ty || is_inherent_assoc_type_const { bug!( "args not compatible with generics for {}: args={:#?}, generics={:#?}", @@ -2494,20 +2523,18 @@ pub fn mk_outlives_from_iter(self, iter: I) -> T::Output T::collect_and_apply(iter, |xs| self.mk_outlives(xs)) } - /// Emit a lint at `span` from a lint struct (some type that implements `LintDiagnostic`, - /// typically generated by `#[derive(LintDiagnostic)]`). + /// Emit a lint at `span` from a lint struct (some type that implements `Diagnostic`, + /// typically generated by `#[derive(Diagnostic)]`). #[track_caller] pub fn emit_node_span_lint( self, lint: &'static Lint, hir_id: HirId, span: impl Into, - decorator: impl for<'a> LintDiagnostic<'a, ()>, + decorator: impl for<'a> Diagnostic<'a, ()>, ) { let level = self.lint_level_at_node(lint, hir_id); - lint_level(self.sess, lint, level, Some(span.into()), |lint| { - decorator.decorate_lint(lint); - }) + diag_lint_level(self.sess, lint, level, Some(span.into()), decorator) } /// Emit a lint at the appropriate level for a hir node, with an associated span. @@ -2555,18 +2582,17 @@ pub fn disabled_nightly_features( } } - /// Emit a lint from a lint struct (some type that implements `LintDiagnostic`, typically - /// generated by `#[derive(LintDiagnostic)]`). + /// Emit a lint from a lint struct (some type that implements `Diagnostic`, typically generated + /// by `#[derive(Diagnostic)]`). #[track_caller] pub fn emit_node_lint( self, lint: &'static Lint, id: HirId, - decorator: impl for<'a> LintDiagnostic<'a, ()>, + decorator: impl for<'a> Diagnostic<'a, ()>, ) { - self.node_lint(lint, id, |lint| { - decorator.decorate_lint(lint); - }) + let level = self.lint_level_at_node(lint, id); + diag_lint_level(self.sess, lint, level, None, decorator); } /// Emit a lint at the appropriate level for a hir node. @@ -2583,7 +2609,7 @@ pub fn node_lint( lint_level(self.sess, lint, level, None, decorate); } - pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate]> { + pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate<'tcx>]> { let map = self.in_scope_traits_map(id.owner)?; let candidates = map.get(&id.local_id)?; Some(candidates) @@ -2746,7 +2772,9 @@ pub fn extern_mod_stmt_cnum(self, def_id: LocalDefId) -> Option { self.resolutions(()).extern_crate_map.get(&def_id).copied() } - pub fn resolver_for_lowering(self) -> &'tcx Steal<(ty::ResolverAstLowering, Arc)> { + pub fn resolver_for_lowering( + self, + ) -> &'tcx Steal<(ty::ResolverAstLowering<'tcx>, Arc)> { self.resolver_for_lowering_raw(()).0 } @@ -2768,7 +2796,7 @@ pub fn needs_coroutine_by_move_body_def_id(self, def_id: DefId) -> bool { /// Whether this is a trait implementation that has `#[diagnostic::do_not_recommend]` pub fn do_not_recommend_impl(self, def_id: DefId) -> bool { - find_attr!(self.get_all_attrs(def_id), AttributeKind::DoNotRecommend { .. }) + find_attr!(self, def_id, DoNotRecommend { .. }) } pub fn is_trivial_const

(self, def_id: P) -> bool @@ -2794,10 +2822,8 @@ pub fn is_entrypoint(self, def_id: DefId) -> bool { } pub fn provide(providers: &mut Providers) { - providers.is_panic_runtime = - |tcx, LocalCrate| find_attr!(tcx.hir_krate_attrs(), AttributeKind::PanicRuntime); - providers.is_compiler_builtins = - |tcx, LocalCrate| find_attr!(tcx.hir_krate_attrs(), AttributeKind::CompilerBuiltins); + providers.is_panic_runtime = |tcx, LocalCrate| find_attr!(tcx, crate, PanicRuntime); + providers.is_compiler_builtins = |tcx, LocalCrate| find_attr!(tcx, crate, CompilerBuiltins); providers.has_panic_handler = |tcx, LocalCrate| { // We want to check if the panic handler was defined in this crate tcx.lang_items().panic_impl().is_some_and(|did| did.is_local()) diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 7580cc65d530..e59573976af5 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -1,19 +1,18 @@ //! Implementation of [`rustc_type_ir::Interner`] for [`TyCtxt`]. -use std::fmt; +use std::{debug_assert_matches, fmt}; use rustc_abi::ExternAbi; -use rustc_data_structures::debug_assert_matches; use rustc_errors::ErrorGuaranteed; +use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; -use rustc_hir::{self as hir}; use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use rustc_type_ir::{CollectAndApply, Interner, TypeFoldable, search_graph}; -use crate::dep_graph::DepNodeIndex; +use crate::dep_graph::{DepKind, DepNodeIndex}; use crate::infer::canonical::CanonicalVarKinds; use crate::query::IntoQueryParam; use crate::traits::cache::WithDepNode; @@ -77,7 +76,7 @@ fn mk_external_constraints( } type DepNodeIndex = DepNodeIndex; fn with_cached_task(self, task: impl FnOnce() -> T) -> (T, DepNodeIndex) { - self.dep_graph.with_anon_task(self, crate::dep_graph::dep_kinds::TraitSelect, task) + self.dep_graph.with_anon_task(self, DepKind::TraitSelect, task) } type Ty = Ty<'tcx>; type Tys = &'tcx List>; @@ -95,6 +94,7 @@ fn with_cached_task(self, task: impl FnOnce() -> T) -> (T, DepNodeIndex) { type Safety = hir::Safety; type Abi = ExternAbi; type Const = ty::Const<'tcx>; + type Consts = &'tcx List; type ParamConst = ty::ParamConst; type ValueConst = ty::Value<'tcx>; @@ -214,7 +214,7 @@ fn alias_term_kind(self, alias: ty::AliasTerm<'tcx>) -> ty::AliasTermKind { ty::AliasTermKind::ProjectionTy } } - DefKind::AssocConst => { + DefKind::AssocConst { .. } => { if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(alias.def_id)) { ty::AliasTermKind::InherentConst @@ -224,7 +224,7 @@ fn alias_term_kind(self, alias: ty::AliasTerm<'tcx>) -> ty::AliasTermKind { } DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy, DefKind::TyAlias => ty::AliasTermKind::FreeTy, - DefKind::Const => ty::AliasTermKind::FreeConst, + DefKind::Const { .. } => ty::AliasTermKind::FreeConst, DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => { ty::AliasTermKind::UnevaluatedConst } @@ -237,7 +237,7 @@ fn trait_ref_and_own_args_for_alias( def_id: DefId, args: ty::GenericArgsRef<'tcx>, ) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) { - debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst); + debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst { .. }); let trait_def_id = self.parent(def_id); debug_assert_matches!(self.def_kind(trait_def_id), DefKind::Trait); let trait_ref = ty::TraitRef::from_assoc(self, trait_def_id, args); @@ -755,6 +755,8 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { CoroutineReturn, CoroutineYield, DynMetadata, + FieldBase, + FieldType, FutureOutput, Metadata, // tidy-alphabetical-end @@ -786,6 +788,7 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { Destruct, DiscriminantKind, Drop, + Field, Fn, FnMut, FnOnce, diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index d37ad56c2e83..d1561c37172c 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -1,5 +1,3 @@ -use std::{mem, ptr}; - use rustc_data_structures::sync; use super::{GlobalCtxt, TyCtxt}; @@ -11,7 +9,6 @@ /// executing a new query. Whenever there's a `TyCtxt` value available /// you should also have access to an `ImplicitCtxt` through the functions /// in this module. -#[derive(Clone)] pub struct ImplicitCtxt<'a, 'tcx> { /// The current `TyCtxt`. pub tcx: TyCtxt<'tcx>, @@ -89,29 +86,6 @@ pub fn with_context(f: F) -> R with_context_opt(|opt_context| f(opt_context.expect("no ImplicitCtxt stored in tls"))) } -/// Allows access to the current `ImplicitCtxt` whose tcx field is the same as the tcx argument -/// passed in. This means the closure is given an `ImplicitCtxt` with the same `'tcx` lifetime -/// as the `TyCtxt` passed in. -/// This will panic if you pass it a `TyCtxt` which is different from the current -/// `ImplicitCtxt`'s `tcx` field. -#[inline] -pub fn with_related_context<'tcx, F, R>(tcx: TyCtxt<'tcx>, f: F) -> R -where - F: FnOnce(&ImplicitCtxt<'_, 'tcx>) -> R, -{ - with_context(|context| { - // The two gcx have different invariant lifetimes, so we need to erase them for the comparison. - assert!(ptr::eq( - context.tcx.gcx as *const _ as *const (), - tcx.gcx as *const _ as *const () - )); - - let context: &ImplicitCtxt<'_, '_> = unsafe { mem::transmute(context) }; - - f(context) - }) -} - /// Allows access to the `TyCtxt` in the current `ImplicitCtxt`. /// Panics if there is no `ImplicitCtxt` available. #[inline] diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index b3b06314e1a0..329a7b99e15b 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -572,7 +572,7 @@ pub fn suggest_constraining_type_params<'a>( err.span_suggestion_verbose(span, msg, suggestion, applicability); } else if suggestions.len() > 1 { let post = if unstable_suggestion { " (some of them are unstable traits)" } else { "" }; - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!("consider restricting type parameters{post}"), suggestions.into_iter().map(|(span, _, suggestion, _)| (span, suggestion)).collect(), applicability, diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index 95a1a1bf5bce..f06ce7324d4c 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -9,9 +9,9 @@ use rustc_data_structures::stable_hasher::{ HashStable, HashingControls, StableHasher, ToStableHashKey, }; -use rustc_query_system::ich::StableHashingContext; use tracing::trace; +use crate::ich::StableHashingContext; use crate::middle::region; use crate::{mir, ty}; diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 5c4c1733be29..b454689e243d 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -61,10 +61,9 @@ pub(crate) fn provide(providers: &mut Providers) { /// requires calling [`InhabitedPredicate::instantiate`] fn inhabited_predicate_adt(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> { if let Some(def_id) = def_id.as_local() { - if matches!(tcx.representability(def_id), ty::Representability::Infinite(_)) { - return InhabitedPredicate::True; - } + let _ = tcx.check_representability(def_id); } + let adt = tcx.adt_def(def_id); InhabitedPredicate::any( tcx, diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 0e9dd7dd169c..4cea8b62f0b6 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -1,6 +1,5 @@ -use std::fmt; +use std::{assert_matches, fmt}; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; @@ -10,7 +9,7 @@ use rustc_index::bit_set::FiniteBitSet; use rustc_macros::{Decodable, Encodable, HashStable, Lift, TyDecodable, TyEncodable}; use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::{DUMMY_SP, Span, Symbol}; +use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; use crate::error; @@ -286,15 +285,6 @@ pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option { } } - #[inline] - pub fn get_attrs( - &self, - tcx: TyCtxt<'tcx>, - attr: Symbol, - ) -> impl Iterator { - tcx.get_attrs(self.def_id(), attr) - } - /// Returns `true` if the LLVM version of this instance is unconditionally /// marked with `inline`. This implies that a copy of this instance is /// generated in every codegen unit. @@ -517,8 +507,8 @@ pub fn try_resolve( tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn - | DefKind::Const - | DefKind::AssocConst + | DefKind::Const { .. } + | DefKind::AssocConst { .. } | DefKind::AnonConst | DefKind::InlineConst | DefKind::Static { .. } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 0dcbafed3e90..fe8f8abf7f57 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1,6 +1,7 @@ use std::ops::Bound; use std::{cmp, fmt}; +use rustc_abi as abi; use rustc_abi::{ AddressSpace, Align, ExternAbi, FieldIdx, FieldsShape, HasDataLayout, LayoutData, PointeeInfo, PointerKind, Primitive, ReprFlags, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout, @@ -10,6 +11,7 @@ use rustc_errors::{ Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, msg, }; +use rustc_hir as hir; use rustc_hir::LangItem; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension}; @@ -18,7 +20,6 @@ use rustc_target::callconv::FnAbi; use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, X86Abi}; use tracing::debug; -use {rustc_abi as abi, rustc_hir as hir}; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::query::TyCtxtAt; @@ -1022,41 +1023,80 @@ fn ty_and_layout_pointee_info_at( let tcx = cx.tcx(); let typing_env = cx.typing_env(); + // Use conservative pointer kind if not optimizing. This saves us the + // Freeze/Unpin queries, and can save time in the codegen backend (noalias + // attributes in LLVM have compile-time cost even in unoptimized builds). + let optimize = tcx.sess.opts.optimize != OptLevel::No; + let pointee_info = match *this.ty.kind() { - ty::RawPtr(p_ty, _) if offset.bytes() == 0 => { - tcx.layout_of(typing_env.as_query_input(p_ty)).ok().map(|layout| PointeeInfo { - size: layout.size, - align: layout.align.abi, - safe: None, - }) - } - ty::FnPtr(..) if offset.bytes() == 0 => { - tcx.layout_of(typing_env.as_query_input(this.ty)).ok().map(|layout| PointeeInfo { - size: layout.size, - align: layout.align.abi, - safe: None, - }) + ty::RawPtr(_, _) | ty::FnPtr(..) if offset.bytes() == 0 => { + Some(PointeeInfo { safe: None, size: Size::ZERO, align: Align::ONE }) } ty::Ref(_, ty, mt) if offset.bytes() == 0 => { - // Use conservative pointer kind if not optimizing. This saves us the - // Freeze/Unpin queries, and can save time in the codegen backend (noalias - // attributes in LLVM have compile-time cost even in unoptimized builds). - let optimize = tcx.sess.opts.optimize != OptLevel::No; - let kind = match mt { - hir::Mutability::Not => { - PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, typing_env) } - } - hir::Mutability::Mut => PointerKind::MutableRef { - unpin: optimize - && ty.is_unpin(tcx, typing_env) - && ty.is_unsafe_unpin(tcx, typing_env), - }, - }; + tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| { + let (size, kind); + match mt { + hir::Mutability::Not => { + let frozen = optimize && ty.is_freeze(tcx, typing_env); + + // Non-frozen shared references are not necessarily dereferenceable for the entire duration of the function + // (see ) + // (if we had "dereferenceable on entry", we could support this) + size = if frozen { layout.size } else { Size::ZERO }; + + kind = PointerKind::SharedRef { frozen }; + } + hir::Mutability::Mut => { + let unpin = optimize + && ty.is_unpin(tcx, typing_env) + && ty.is_unsafe_unpin(tcx, typing_env); + + // Mutable references to potentially self-referential types are not + // necessarily dereferenceable for the entire duration of the function + // (see ) + // (if we had "dereferenceable on entry", we could support this) + size = if unpin { layout.size } else { Size::ZERO }; + + kind = PointerKind::MutableRef { unpin }; + } + }; + PointeeInfo { safe: Some(kind), size, align: layout.align.abi } + }) + } + + ty::Adt(..) + 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), + }), + + // `Box` are not necessarily dereferenceable for the entire duration of the function as + // they can be deallocated at any time. + // (if we had "dereferenceable on entry", we could support this) + size: Size::ZERO, - tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| PointeeInfo { - size: layout.size, align: layout.align.abi, - safe: Some(kind), + }) + } + + ty::Adt(adt_def, ..) if adt_def.is_maybe_dangling() => { + Self::ty_and_layout_pointee_info_at(this.field(cx, 0), cx, offset).map(|info| { + PointeeInfo { + // Mark the pointer as raw + // (thus removing noalias/readonly/etc in case of the llvm backend) + safe: None, + // Make sure we don't assert dereferenceability of the pointer. + size: Size::ZERO, + // Preserve the alignment assertion! That is required even inside `MaybeDangling`. + align: info.align, + } }) } @@ -1098,7 +1138,7 @@ fn ty_and_layout_pointee_info_at( } } Variants::Multiple { .. } => None, - _ => Some(this), + Variants::Empty | Variants::Single { .. } => Some(this), }; if let Some(variant) = data_variant @@ -1135,24 +1175,6 @@ fn ty_and_layout_pointee_info_at( } } - // Fixup info for the first field of a `Box`. Recursive traversal will have found - // the raw pointer, so size and align are set to the boxed type, but `pointee.safe` - // will still be `None`. - if let Some(ref mut pointee) = result { - if offset.bytes() == 0 - && let Some(boxed_ty) = this.ty.boxed_ty() - { - debug_assert!(pointee.safe.is_none()); - let optimize = tcx.sess.opts.optimize != OptLevel::No; - pointee.safe = Some(PointerKind::Box { - unpin: optimize - && boxed_ty.is_unpin(tcx, typing_env) - && boxed_ty.is_unsafe_unpin(tcx, typing_env), - global: this.ty.is_box_global(tcx), - }); - } - } - result } }; @@ -1369,11 +1391,56 @@ fn fn_abi_of_fn_ptr( ) } - /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for - /// direct calls to an `fn`. + /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for direct calls* + /// to an `fn`. Indirectly-passed parameters in the returned ABI might not include all possible + /// codegen optimization attributes (such as `ReadOnly` or `CapturesNone`), as deducing these + /// requires inspection of function bodies that can lead to cycles when performed during typeck. + /// Post typeck, you should prefer the optimized ABI returned by `fn_abi_of_instance`. /// - /// NB: that includes virtual calls, which are represented by "direct calls" - /// to an `InstanceKind::Virtual` instance (of `::fn`). + /// NB: the ABI returned by this query must not differ from that returned by + /// `fn_abi_of_instance` in any other way. + /// + /// * that includes virtual calls, which are represented by "direct calls" to an + /// `InstanceKind::Virtual` instance (of `::fn`). + #[inline] + #[tracing::instrument(level = "debug", skip(self))] + fn fn_abi_of_instance_no_deduced_attrs( + &self, + instance: ty::Instance<'tcx>, + extra_args: &'tcx ty::List>, + ) -> Self::FnAbiOfResult { + // FIXME(eddyb) get a better `span` here. + let span = self.layout_tcx_at_span(); + let tcx = self.tcx().at(span); + + MaybeResult::from( + tcx.fn_abi_of_instance_no_deduced_attrs( + self.typing_env().as_query_input((instance, extra_args)), + ) + .map_err(|err| { + // HACK(eddyb) at least for definitions of/calls to `Instance`s, + // we can get some kind of span even if one wasn't provided. + // However, we don't do this early in order to avoid calling + // `def_span` unconditionally (which may have a perf penalty). + let span = if !span.is_dummy() { span } else { tcx.def_span(instance.def_id()) }; + self.handle_fn_abi_err( + *err, + span, + FnAbiRequest::OfInstance { instance, extra_args }, + ) + }), + ) + } + + /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for direct calls* + /// to an `fn`. Indirectly-passed parameters in the returned ABI will include applicable + /// codegen optimization attributes, including `ReadOnly` and `CapturesNone` -- deduction of + /// which requires inspection of function bodies that can lead to cycles when performed during + /// typeck. During typeck, you should therefore use instead the unoptimized ABI returned by + /// `fn_abi_of_instance_no_deduced_attrs`. + /// + /// * that includes virtual calls, which are represented by "direct calls" to an + /// `InstanceKind::Virtual` instance (of `::fn`). #[inline] #[tracing::instrument(level = "debug", skip(self))] fn fn_abi_of_instance( diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 036397709925..6abe7d146699 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -16,7 +16,7 @@ use std::marker::PhantomData; use std::num::NonZero; use std::ptr::NonNull; -use std::{fmt, iter, str}; +use std::{assert_matches, fmt, iter, str}; pub use adt::*; pub use assoc::*; @@ -26,18 +26,19 @@ use rustc_abi::{ Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, ScalableElt, VariantIdx, }; +use rustc_ast as ast; use rustc_ast::AttrVec; use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree}; use rustc_ast::node_id::NodeMap; pub use rustc_ast_ir::{Movability, Mutability, try_visit}; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer}; -use rustc_hir::attrs::{AttributeKind, StrippedCfgItem}; +use rustc_hir as hir; +use rustc_hir::attrs::StrippedCfgItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; use rustc_hir::{LangItem, attrs as attr, find_attr}; @@ -47,11 +48,12 @@ BlobDecodable, Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, extension, }; -use rustc_query_system::ich::StableHashingContext; use rustc_serialize::{Decodable, Encodable}; +use rustc_session::config::OptLevel; pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol}; +use rustc_target::callconv::FnAbi; pub use rustc_type_ir::data_structures::{DelayedMap, DelayedSet}; pub use rustc_type_ir::fast_reject::DeepRejectCtxt; #[allow( @@ -67,7 +69,6 @@ use rustc_type_ir::{InferCtxtLike, Interner}; use tracing::{debug, instrument, trace}; pub use vtable::*; -use {rustc_ast as ast, rustc_hir as hir}; pub use self::closure::{ BorrowKind, CAPTURE_STRUCT_LOCAL, CaptureInfo, CapturedPlace, ClosureTypeInfo, @@ -112,6 +113,7 @@ Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind, }; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; +use crate::ich::StableHashingContext; use crate::metadata::{AmbigModChild, ModChild}; use crate::middle::privacy::EffectiveVisibilities; use crate::mir::{Body, CoroutineLayout, CoroutineSavedLocal, SourceInfo}; @@ -120,7 +122,7 @@ use crate::ty::codec::{TyDecoder, TyEncoder}; pub use crate::ty::diagnostics::*; use crate::ty::fast_reject::SimplifiedType; -use crate::ty::layout::LayoutError; +use crate::ty::layout::{FnAbiError, LayoutError}; use crate::ty::util::Discr; use crate::ty::walk::TypeWalker; @@ -196,7 +198,7 @@ pub struct ResolverGlobalCtxt { /// Resolutions that should only be used for lowering. /// This struct is meant to be consumed by lowering. #[derive(Debug)] -pub struct ResolverAstLowering { +pub struct ResolverAstLowering<'tcx> { /// Resolutions for nodes that have a single resolution. pub partial_res_map: NodeMap, /// Resolutions for import nodes, which have multiple resolutions in different namespaces. @@ -212,7 +214,7 @@ pub struct ResolverAstLowering { pub node_id_to_def_id: NodeMap, - pub trait_map: NodeMap>, + pub trait_map: NodeMap<&'tcx [hir::TraitCandidate<'tcx>]>, /// List functions and methods for which lifetime elision was successful. pub lifetime_elision_allowed: FxHashSet, @@ -1442,10 +1444,7 @@ pub fn repr_options_of_def(self, did: LocalDefId) -> ReprOptions { field_shuffle_seed ^= user_seed; } - let attributes = self.get_all_attrs(did); - let elt = find_attr!( - attributes, - AttributeKind::RustcScalableVector { element_count, .. } => element_count + let elt = find_attr!(self, did, RustcScalableVector { element_count, .. } => element_count ) .map(|elt| match elt { Some(n) => ScalableElt::ElementCount(*n), @@ -1454,7 +1453,7 @@ pub fn repr_options_of_def(self, did: LocalDefId) -> ReprOptions { if elt.is_some() { flags.insert(ReprFlags::IS_SCALABLE); } - if let Some(reprs) = find_attr!(attributes, AttributeKind::Repr { reprs, .. } => reprs) { + if let Some(reprs) = find_attr!(self, did, Repr { reprs, .. } => reprs) { for (r, _) in reprs { flags.insert(match *r { attr::ReprRust => ReprFlags::empty(), @@ -1514,7 +1513,7 @@ pub fn repr_options_of_def(self, did: LocalDefId) -> ReprOptions { } // See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details. - if find_attr!(attributes, AttributeKind::RustcPassIndirectlyInNonRusticAbis(..)) { + if find_attr!(self, did, RustcPassIndirectlyInNonRusticAbis(..)) { flags.insert(ReprFlags::PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS); } @@ -1580,7 +1579,9 @@ pub fn item_ident(self, def_id: impl IntoQueryParam) -> Ident { } pub fn opt_associated_item(self, def_id: DefId) -> Option { - if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = self.def_kind(def_id) { + if let DefKind::AssocConst { .. } | DefKind::AssocFn | DefKind::AssocTy = + self.def_kind(def_id) + { Some(self.associated_item(def_id)) } else { None @@ -1682,9 +1683,9 @@ pub fn instance_mir(self, instance: ty::InstanceKind<'tcx>) -> &'tcx Body<'tcx> let def_kind = self.def_kind(def); debug!("returned from def_kind: {:?}", def_kind); match def_kind { - DefKind::Const + DefKind::Const { .. } | DefKind::Static { .. } - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Ctor(..) | DefKind::AnonConst | DefKind::InlineConst => self.mir_for_ctfe(def), @@ -1711,11 +1712,13 @@ pub fn instance_mir(self, instance: ty::InstanceKind<'tcx>) -> &'tcx Body<'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."] pub fn get_attrs( self, did: impl Into, attr: Symbol, ) -> impl Iterator { + #[allow(deprecated)] self.get_all_attrs(did).iter().filter(move |a: &&hir::Attribute| a.has_name(attr)) } @@ -1723,6 +1726,7 @@ pub fn get_attrs( /// /// To see if an item has a specific attribute, you should use /// [`rustc_hir::find_attr!`] so you can use matching. + #[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."] pub fn get_all_attrs(self, did: impl Into) -> &'tcx [hir::Attribute] { let did: DefId = did.into(); if let Some(did) = did.as_local() { @@ -1745,17 +1749,21 @@ pub fn get_attrs_by_path( } } + #[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."] pub fn get_attr(self, did: impl Into, attr: Symbol) -> Option<&'tcx hir::Attribute> { if cfg!(debug_assertions) && !rustc_feature::is_valid_for_get_attr(attr) { let did: DefId = did.into(); bug!("get_attr: unexpected called with DefId `{:?}`, attr `{:?}`", did, attr); } else { + #[allow(deprecated)] self.get_attrs(did, attr).next() } } /// Determines whether an item is annotated with an attribute. + #[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."] pub fn has_attr(self, did: impl Into, attr: Symbol) -> bool { + #[allow(deprecated)] self.get_attrs(did, attr).next().is_some() } @@ -1987,10 +1995,7 @@ pub fn is_builtin_derived(self, def_id: DefId) -> bool { && let Some(def_id) = def_id.as_local() && let outer = self.def_span(def_id).ctxt().outer_expn_data() && matches!(outer.kind, ExpnKind::Macro(MacroKind::Derive, _)) - && find_attr!( - self.get_all_attrs(outer.macro_def_id.unwrap()), - AttributeKind::RustcBuiltinMacro { .. } - ) + && find_attr!(self, outer.macro_def_id.unwrap(), RustcBuiltinMacro { .. }) { true } else { @@ -2000,7 +2005,7 @@ pub fn is_builtin_derived(self, def_id: DefId) -> bool { /// Check if the given `DefId` is `#\[automatically_derived\]`. pub fn is_automatically_derived(self, def_id: DefId) -> bool { - find_attr!(self.get_all_attrs(def_id), AttributeKind::AutomaticallyDerived(..)) + find_attr!(self, def_id, AutomaticallyDerived(..)) } /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err` @@ -2127,10 +2132,10 @@ pub fn is_conditionally_const(self, def_id: impl Into) -> bool { | DefKind::TyAlias | DefKind::ForeignTy | DefKind::TyParam - | DefKind::Const + | DefKind::Const { .. } | DefKind::ConstParam | DefKind::Static { .. } - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Macro(_) | DefKind::ExternCrate | DefKind::Use @@ -2164,6 +2169,34 @@ pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool { !self.associated_types_for_impl_traits_in_associated_fn(trait_item_def_id).is_empty() } + + /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for direct calls* + /// to an `fn`. Indirectly-passed parameters in the returned ABI will include applicable + /// codegen optimization attributes, including `ReadOnly` and `CapturesNone` -- deduction of + /// which requires inspection of function bodies that can lead to cycles when performed during + /// typeck. During typeck, you should therefore use instead the unoptimized ABI returned by + /// `fn_abi_of_instance_no_deduced_attrs`. + /// + /// For performance reasons, you should prefer to call this inherent method rather than invoke + /// the `fn_abi_of_instance_raw` query: it delegates to that query if necessary, but where + /// possible delegates instead to the `fn_abi_of_instance_no_deduced_attrs` query (thus avoiding + /// unnecessary query system overhead). + /// + /// * that includes virtual calls, which are represented by "direct calls" to an + /// `InstanceKind::Virtual` instance (of `::fn`). + #[inline] + pub fn fn_abi_of_instance( + self, + query: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)>, + ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { + // Only deduce attrs in full, optimized builds. Otherwise, avoid the query system overhead + // of ever invoking the `fn_abi_of_instance_raw` query. + if self.sess.opts.optimize != OptLevel::No && self.sess.opts.incremental.is_none() { + self.fn_abi_of_instance_raw(query) + } else { + self.fn_abi_of_instance_no_deduced_attrs(query) + } + } } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_middle/src/ty/offload_meta.rs b/compiler/rustc_middle/src/ty/offload_meta.rs index 67c00765ed57..849670d76d46 100644 --- a/compiler/rustc_middle/src/ty/offload_meta.rs +++ b/compiler/rustc_middle/src/ty/offload_meta.rs @@ -3,10 +3,16 @@ use crate::ty::{self, PseudoCanonicalInput, Ty, TyCtxt, TypingEnv}; pub struct OffloadMetadata { - pub payload_size: u64, + pub payload_size: OffloadSize, pub mode: MappingFlags, } +#[derive(Debug, Copy, Clone)] +pub enum OffloadSize { + Dynamic, + Static(u64), +} + bitflags! { /// Mirrors `OpenMPOffloadMappingFlags` from Clang/OpenMP. #[derive(Debug, Copy, Clone)] @@ -59,17 +65,18 @@ pub fn from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { } // FIXME(Sa4dUs): implement a solid logic to determine the payload size -fn get_payload_size<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> u64 { +fn get_payload_size<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> OffloadSize { match ty.kind() { ty::RawPtr(inner, _) | ty::Ref(_, inner, _) => get_payload_size(tcx, *inner), - _ => tcx - .layout_of(PseudoCanonicalInput { + _ => OffloadSize::Static( + tcx.layout_of(PseudoCanonicalInput { typing_env: TypingEnv::fully_monomorphized(), value: ty, }) .unwrap() .size .bytes(), + ), } } diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs index 6acf0aff800f..935e5da68e82 100644 --- a/compiler/rustc_middle/src/ty/pattern.rs +++ b/compiler/rustc_middle/src/ty/pattern.rs @@ -3,9 +3,7 @@ use rustc_data_structures::intern::Interned; use rustc_macros::HashStable; use rustc_type_ir::ir_print::IrPrint; -use rustc_type_ir::{ - FlagComputation, Flags, {self as ir}, -}; +use rustc_type_ir::{self as ir, FlagComputation, Flags}; use super::TyCtxt; use crate::ty; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 02b804c1ab29..8b8fe522842a 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -17,7 +17,7 @@ use rustc_macros::{Lift, extension}; use rustc_session::cstore::{ExternCrate, ExternCrateSource}; use rustc_span::{Ident, RemapPathScopeComponents, Symbol, kw, sym}; -use rustc_type_ir::{Upcast as _, elaborate}; +use rustc_type_ir::{FieldInfo, Upcast as _, elaborate}; use smallvec::SmallVec; // `pretty` is a separate module only for organization. @@ -404,7 +404,7 @@ fn force_print_trimmed_def_path(&mut self, def_id: DefId) -> Result Result) -> Result<(), PrintError> { false => write!(self, "{}", self.tcx().item_name(def_id))?, }, }, + ty::Adt(def, args) + if let Some(FieldInfo { base, variant, name, .. }) = + def.field_representing_type_info(self.tcx(), args) => + { + if let Some(variant) = variant { + write!(self, "field_of!({base}, {variant}.{name})")?; + } else { + write!(self, "field_of!({base}, {name})")?; + } + } ty::Adt(def, args) => self.print_def_path(def.did(), args)?, ty::Dynamic(data, r) => { let print_r = self.should_print_optional_region(r); @@ -1541,7 +1551,7 @@ fn pretty_print_const( match ct.kind() { ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { match self.tcx().def_kind(def) { - DefKind::Const | DefKind::AssocConst => { + DefKind::Const { .. } | DefKind::AssocConst { .. } => { self.pretty_print_value_path(def, args)?; } DefKind::AnonConst => { @@ -1920,7 +1930,7 @@ fn pretty_print_const_valtree( } // Otherwise, print the array separated by commas (or if it's a tuple) (ty::ValTreeKind::Branch(fields), ty::Array(..) | ty::Tuple(..)) => { - let fields_iter = fields.iter().copied(); + let fields_iter = fields.iter(); match *cv.ty.kind() { ty::Array(..) => { diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs index f1aa7076d98a..5035127b8c9c 100644 --- a/compiler/rustc_middle/src/ty/significant_drop_order.rs +++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs @@ -119,7 +119,7 @@ pub fn extract_component_with_significant_dtor<'tcx>( /// when we are working with current local crate. #[instrument(level = "trace", skip(tcx))] pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { - match ty.kind() { + match *ty.kind() { ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 8707b03e4b8f..04985dd3acde 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -4,7 +4,6 @@ //! to help with the tedium. use std::fmt::{self, Debug}; -use std::marker::PhantomData; use rustc_abi::TyAndLayout; use rustc_hir::def::Namespace; @@ -270,13 +269,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /////////////////////////////////////////////////////////////////////////// // Lift implementations -impl<'tcx> Lift> for PhantomData<&()> { - type Lifted = PhantomData<&'tcx ()>; - fn lift_to_interner(self, _: TyCtxt<'tcx>) -> Option { - Some(PhantomData) - } -} - impl<'tcx, T: Lift>> Lift> for Option { type Lifted = Option; fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { @@ -805,4 +797,5 @@ fn fold_with>>( &'tcx ty::List> : mk_place_elems, &'tcx ty::List> : mk_patterns, &'tcx ty::List> : mk_outlives, + &'tcx ty::List> : mk_const_list, } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 4be30d8b6c91..d0d8fad4b120 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -3,11 +3,11 @@ #![allow(rustc::usage_of_ty_tykind)] use std::borrow::Cow; +use std::debug_assert_matches; use std::ops::{ControlFlow, Range}; use hir::def::{CtorKind, DefKind}; use rustc_abi::{FIRST_VARIANT, FieldIdx, ScalableElt, VariantIdx}; -use rustc_data_structures::debug_assert_matches; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::LangItem; @@ -26,8 +26,8 @@ use crate::traits::ObligationCause; use crate::ty::InferTy::*; use crate::ty::{ - self, AdtDef, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, Region, Ty, - TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, + self, AdtDef, Const, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, Region, + Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, ValTree, }; // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here @@ -487,6 +487,35 @@ pub fn new_pat(tcx: TyCtxt<'tcx>, base: Ty<'tcx>, pat: ty::Pattern<'tcx>) -> Ty< Ty::new(tcx, Pat(base, pat)) } + #[inline] + pub fn new_field_representing_type( + tcx: TyCtxt<'tcx>, + base: Ty<'tcx>, + variant: VariantIdx, + field: FieldIdx, + ) -> Ty<'tcx> { + let Some(did) = tcx.lang_items().field_representing_type() else { + bug!("could not locate the `FieldRepresentingType` lang item") + }; + let def = tcx.adt_def(did); + let args = tcx.mk_args(&[ + base.into(), + Const::new_value( + tcx, + ValTree::from_scalar_int(tcx, variant.as_u32().into()), + tcx.types.u32, + ) + .into(), + Const::new_value( + tcx, + ValTree::from_scalar_int(tcx, field.as_u32().into()), + tcx.types.u32, + ) + .into(), + ]); + Ty::new_adt(tcx, def, args) + } + #[inline] #[instrument(level = "debug", skip(tcx))] pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { @@ -613,12 +642,12 @@ pub fn new_adt(tcx: TyCtxt<'tcx>, def: AdtDef<'tcx>, args: GenericArgsRef<'tcx>) | DefKind::AssocTy | DefKind::TyParam | DefKind::Fn - | DefKind::Const + | DefKind::Const { .. } | DefKind::ConstParam | DefKind::Static { .. } | DefKind::Ctor(..) | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Macro(..) | DefKind::ExternCrate | DefKind::Use diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 0553561fac2c..3e69eda11ca6 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -2,7 +2,6 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::{self as hir, find_attr}; @@ -241,7 +240,7 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait /// Query provider for `incoherent_impls`. pub(super) fn incoherent_impls_provider(tcx: TyCtxt<'_>, simp: SimplifiedType) -> &[DefId] { if let Some(def_id) = simp.def() - && !find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcHasIncoherentInherentImpls) + && !find_attr!(tcx, def_id, RustcHasIncoherentInherentImpls) { return &[]; } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index d534c273ff65..e7c7d5924870 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -4,13 +4,11 @@ use rustc_abi::{Float, Integer, IntegerType, Size}; use rustc_apfloat::Float as _; -use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hashes::Hash128; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_hir::limit::Limit; @@ -167,7 +165,7 @@ pub fn res_generics_def_id(self, res: Res) -> Option { | DefKind::AssocTy | DefKind::Fn | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Impl { .. }, def_id, ) => Some(def_id), @@ -404,7 +402,7 @@ pub fn calculate_dtor( validate: impl Fn(Self, LocalDefId) -> Result<(), ErrorGuaranteed>, ) -> Option { let drop_trait = self.lang_items().drop_trait()?; - self.ensure_ok().coherent_trait(drop_trait).ok()?; + self.ensure_result().coherent_trait(drop_trait).ok()?; let mut dtor_candidate = None; // `Drop` impls can only be written in the same crate as the adt, and cannot be blanket impls @@ -419,7 +417,7 @@ pub fn calculate_dtor( continue; } - let Some(item_id) = self.associated_item_def_ids(impl_did).first() else { + 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"); continue; @@ -437,7 +435,7 @@ pub fn calculate_dtor( .delay_as_bug(); } - dtor_candidate = Some(*item_id); + dtor_candidate = Some(item_id); } let did = dtor_candidate?; @@ -451,7 +449,7 @@ pub fn calculate_async_dtor( validate: impl Fn(Self, LocalDefId) -> Result<(), ErrorGuaranteed>, ) -> Option { let async_drop_trait = self.lang_items().async_drop_trait()?; - self.ensure_ok().coherent_trait(async_drop_trait).ok()?; + self.ensure_result().coherent_trait(async_drop_trait).ok()?; let mut dtor_candidate = None; // `AsyncDrop` impls can only be written in the same crate as the adt, and cannot be blanket impls @@ -1672,14 +1670,12 @@ pub fn reveal_opaque_types_in_bounds<'tcx>( /// Determines whether an item is directly annotated with `doc(hidden)`. fn is_doc_hidden(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); - attrs.iter().any(|attr| attr.is_doc_hidden()) + find_attr!(tcx, def_id, Doc(doc) if doc.hidden.is_some()) } /// Determines whether an item is annotated with `doc(notable_trait)`. pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - let attrs = tcx.get_all_attrs(def_id); - attrs.iter().any(|attr| matches!(attr, hir::Attribute::Parsed(AttributeKind::Doc(doc)) if doc.notable_trait.is_some())) + find_attr!(tcx, def_id, Doc(doc) if doc.notable_trait.is_some()) } /// Determines whether an item is an intrinsic (which may be via Abi or via the `rustc_intrinsic` attribute). @@ -1688,9 +1684,7 @@ pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { /// the compiler to make some assumptions about its shape; if the user doesn't use a feature gate, they may /// cause an ICE that we otherwise may want to prevent. pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - if tcx.features().intrinsics() - && find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcIntrinsic) - { + if tcx.features().intrinsics() && find_attr!(tcx, def_id, RustcIntrinsic) { let must_be_overridden = match tcx.hir_node_by_def_id(def_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { has_body, .. }, .. }) => { !has_body @@ -1700,10 +1694,7 @@ pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option( - tcx: Tcx, - dep_graph_data: &DepGraphData, +pub fn incremental_verify_ich<'tcx, V>( + tcx: TyCtxt<'tcx>, + dep_graph_data: &DepGraphData, result: &V, prev_index: SerializedDepNodeIndex, hash_result: Option, &V) -> Fingerprint>, format_value: fn(&V) -> String, -) where - Tcx: DepContext, -{ +) { if !dep_graph_data.is_index_green(prev_index) { incremental_verify_ich_not_green(tcx, prev_index) } @@ -26,7 +25,7 @@ pub fn incremental_verify_ich( tcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, result)) }); - let old_hash = dep_graph_data.prev_fingerprint_of(prev_index); + let old_hash = dep_graph_data.prev_value_fingerprint_of(prev_index); if new_hash != old_hash { incremental_verify_ich_failed(tcx, prev_index, &|| format_value(result)); @@ -35,13 +34,10 @@ pub fn incremental_verify_ich( #[cold] #[inline(never)] -fn incremental_verify_ich_not_green(tcx: Tcx, prev_index: SerializedDepNodeIndex) -where - Tcx: DepContext, -{ +fn incremental_verify_ich_not_green<'tcx>(tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex) { panic!( "fingerprint for green query instance not loaded from cache: {:?}", - tcx.dep_graph().data().unwrap().prev_node_of(prev_index) + tcx.dep_graph.data().unwrap().prev_node_of(prev_index) ) } @@ -50,13 +46,11 @@ fn incremental_verify_ich_not_green(tcx: Tcx, prev_index: SerializedDepNode // chew on (and filling up the final binary, too). #[cold] #[inline(never)] -fn incremental_verify_ich_failed( - tcx: Tcx, +fn incremental_verify_ich_failed<'tcx>( + tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex, result: &dyn Fn() -> String, -) where - Tcx: DepContext, -{ +) { // When we emit an error message and panic, we try to debug-print the `DepNode` // and query result. Unfortunately, this can cause us to run additional queries, // which may result in another fingerprint mismatch while we're in the middle @@ -70,16 +64,16 @@ fn incremental_verify_ich_failed( let old_in_panic = INSIDE_VERIFY_PANIC.replace(true); if old_in_panic { - tcx.sess().dcx().emit_err(crate::error::Reentrant); + tcx.dcx().emit_err(crate::error::Reentrant); } else { - let run_cmd = if let Some(crate_name) = &tcx.sess().opts.crate_name { + let run_cmd = if let Some(crate_name) = &tcx.sess.opts.crate_name { format!("`cargo clean -p {crate_name}` or `cargo clean`") } else { "`cargo clean`".to_string() }; - let dep_node = tcx.dep_graph().data().unwrap().prev_node_of(prev_index); - tcx.sess().dcx().emit_err(crate::error::IncrementCompilation { + let dep_node = tcx.dep_graph.data().unwrap().prev_node_of(prev_index); + tcx.dcx().emit_err(crate::error::IncrementCompilation { run_cmd, dep_node: format!("{dep_node:?}"), }); diff --git a/compiler/rustc_mir_build/src/builder/coverageinfo.rs b/compiler/rustc_mir_build/src/builder/coverageinfo.rs index ae36b2fb7f38..2e29600c9339 100644 --- a/compiler/rustc_mir_build/src/builder/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/builder/coverageinfo.rs @@ -1,6 +1,6 @@ +use std::assert_matches; use std::collections::hash_map::Entry; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::FxHashMap; use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageInfoHi, CoverageKind}; use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp}; 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 ed5f0b2e8f75..ad6c1f7dce5b 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -1,7 +1,7 @@ //! See docs in builder/expr/mod.rs use rustc_abi::Size; -use rustc_ast::{self as ast}; +use rustc_ast as ast; use rustc_hir::LangItem; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, Scalar}; use rustc_middle::mir::*; diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 172dbf7c31b5..b95b565322f1 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -1,9 +1,8 @@ //! See docs in build/expr/mod.rs -use std::iter; +use std::{assert_matches, iter}; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; -use rustc_data_structures::assert_matches; use rustc_hir::def_id::LocalDefId; use rustc_middle::hir::place::{Projection as HirProjection, ProjectionKind as HirProjectionKind}; use rustc_middle::mir::AssertKind::BoundsCheck; @@ -552,7 +551,6 @@ fn expr_as_place( | ExprKind::Unary { .. } | ExprKind::Binary { .. } | ExprKind::LogicalOp { .. } - | ExprKind::Box { .. } | ExprKind::Cast { .. } | ExprKind::Use { .. } | ExprKind::NeverToAny { .. } 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 8de79ab2531f..0f206c1f01ec 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -1,7 +1,6 @@ //! See docs in `build/expr/mod.rs`. use rustc_abi::FieldIdx; -use rustc_hir::lang_items::LangItem; use rustc_index::{Idx, IndexVec}; use rustc_middle::bug; use rustc_middle::middle::region::{self, TempLifetime}; @@ -124,65 +123,6 @@ pub(crate) fn as_rvalue( } block.and(Rvalue::UnaryOp(op, arg)) } - ExprKind::Box { value } => { - let value_ty = this.thir[value].ty; - let tcx = this.tcx; - let source_info = this.source_info(expr_span); - - let size = tcx.require_lang_item(LangItem::SizeOf, expr_span); - let size = Operand::unevaluated_constant(tcx, size, &[value_ty.into()], expr_span); - - let align = tcx.require_lang_item(LangItem::AlignOf, expr_span); - let align = - Operand::unevaluated_constant(tcx, align, &[value_ty.into()], expr_span); - - // malloc some memory of suitable size and align: - let exchange_malloc = Operand::function_handle( - tcx, - tcx.require_lang_item(LangItem::ExchangeMalloc, expr_span), - [], - expr_span, - ); - let storage = this.temp(Ty::new_mut_ptr(tcx, tcx.types.u8), expr_span); - let success = this.cfg.start_new_block(); - this.cfg.terminate( - block, - source_info, - TerminatorKind::Call { - func: exchange_malloc, - args: [ - Spanned { node: size, span: DUMMY_SP }, - Spanned { node: align, span: DUMMY_SP }, - ] - .into(), - destination: storage, - target: Some(success), - unwind: UnwindAction::Continue, - call_source: CallSource::Misc, - fn_span: expr_span, - }, - ); - this.diverge_from(block); - block = success; - - let result = this.local_decls.push(LocalDecl::new(expr.ty, expr_span)); - this.cfg - .push(block, Statement::new(source_info, StatementKind::StorageLive(result))); - if let Some(scope) = scope.temp_lifetime { - // schedule a shallow free of that memory, lest we unwind: - this.schedule_drop_storage_and_value(expr_span, scope, result); - } - - // Transmute `*mut u8` to the box (thus far, uninitialized): - let box_ = Rvalue::ShallowInitBox(Operand::Move(storage), value_ty); - this.cfg.push_assign(block, source_info, Place::from(result), box_); - - // initialize the box contents: - block = this - .expr_into_dest(this.tcx.mk_place_deref(Place::from(result)), block, value) - .into_block(); - block.and(Rvalue::Use(Operand::Move(Place::from(result)))) - } ExprKind::Cast { source } => { let source_expr = &this.thir[source]; diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index 1464b5e560bf..5404d9800c3f 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -64,7 +64,6 @@ pub(crate) fn of(ek: &ExprKind<'_>) -> Option { | ExprKind::Closure { .. } | ExprKind::Unary { .. } | ExprKind::Binary { .. } - | ExprKind::Box { .. } | ExprKind::Cast { .. } | ExprKind::PointerCoercion { .. } | ExprKind::Repeat { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 60e05b691a83..24d184121ffd 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -1,5 +1,6 @@ //! See docs in build/expr/mod.rs +use rustc_abi::FieldIdx; use rustc_ast::{AsmMacro, InlineAsmOptions}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -9,8 +10,8 @@ use rustc_middle::span_bug; use rustc_middle::thir::*; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; -use rustc_span::DUMMY_SP; use rustc_span::source_map::Spanned; +use rustc_span::{DUMMY_SP, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::{debug, instrument}; @@ -366,6 +367,87 @@ fn is_supported_loop_match_type(ty: Ty<'_>) -> bool { None }) } + // Some intrinsics are handled here because they desperately want to avoid introducing + // unnecessary copies. + ExprKind::Call { ty, fun, ref args, .. } + if let ty::FnDef(def_id, generic_args) = *ty.kind() + && let Some(intrinsic) = this.tcx.intrinsic(def_id) + && matches!(intrinsic.name, sym::write_via_move | sym::write_box_via_move) => + { + // We still have to evaluate the callee expression as normal (but we don't care + // about its result). + let _fun = unpack!(block = this.as_local_operand(block, fun)); + + match intrinsic.name { + sym::write_via_move => { + // `write_via_move(ptr, val)` becomes `*ptr = val` but without any dropping. + + // The destination must have unit type (so we don't actually have to store anything + // into it). + assert!(destination.ty(&this.local_decls, this.tcx).ty.is_unit()); + + // Compile this to an assignment of the argument into the destination. + let [ptr, val] = **args else { + span_bug!(expr_span, "invalid write_via_move call") + }; + let Some(ptr) = unpack!(block = this.as_local_operand(block, ptr)).place() + else { + span_bug!(expr_span, "invalid write_via_move call") + }; + let ptr_deref = ptr.project_deeper(&[ProjectionElem::Deref], this.tcx); + this.expr_into_dest(ptr_deref, block, val) + } + sym::write_box_via_move => { + // The signature is: + // `fn write_box_via_move(b: Box>, val: T) -> Box>`. + // `write_box_via_move(b, val)` becomes + // ``` + // (*b).value.value.value = val; + // b + // ``` + // One crucial aspect of this lowering is that the generated code must + // cause the borrow checker to enforce that `val` lives sufficiently + // long to be stored in `b`. The above lowering does this; anything that + // involves a `*const T` or a `NonNull` does not as those are covariant. + + // Extract the operands, compile `b`. + let [b, val] = **args else { + span_bug!(expr_span, "invalid init_box_via_move call") + }; + let Some(b) = unpack!(block = this.as_local_operand(block, b)).place() + else { + span_bug!(expr_span, "invalid init_box_via_move call") + }; + let tcx = this.tcx; + let decls = &this.local_decls; + + // `b` is a `Box>`. + let place = b.project_deeper(&[ProjectionElem::Deref], tcx); + // Current type: `MaybeUninit`. Field #1 is `ManuallyDrop`. + let place = place.project_to_field(FieldIdx::from_u32(1), decls, tcx); + // Current type: `ManuallyDrop`. Field #0 is `MaybeDangling`. + let place = place.project_to_field(FieldIdx::ZERO, decls, tcx); + // Current type: `MaybeDangling`. Field #0 is `T`. + let place = place.project_to_field(FieldIdx::ZERO, decls, tcx); + // Sanity check. + assert_eq!(place.ty(decls, tcx).ty, generic_args.type_at(0)); + + // Store `val` into place. + unpack!(block = this.expr_into_dest(place, block, val)); + + // Return `b` + this.cfg.push_assign( + block, + source_info, + destination, + // Move from `b` so that does not get dropped any more. + Rvalue::Use(Operand::Move(b)), + ); + block.unit() + } + _ => rustc_middle::bug!(), + } + } ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => { let fun = unpack!(block = this.as_local_operand(block, fun)); let args: Box<[_]> = args @@ -770,7 +852,6 @@ fn is_supported_loop_match_type(ty: Ty<'_>) -> bool { // these are the cases that are more naturally handled by some other mode ExprKind::Unary { .. } | ExprKind::Binary { .. } - | ExprKind::Box { .. } | ExprKind::Cast { .. } | ExprKind::PointerCoercion { .. } | ExprKind::Repeat { .. } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index d04322c320b4..d7edf82ae4af 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -6,12 +6,11 @@ //! function parameters. use std::borrow::Borrow; -use std::mem; use std::sync::Arc; +use std::{debug_assert_matches, mem}; use itertools::{Itertools, Position}; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; -use rustc_data_structures::debug_assert_matches; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::{BindingMode, ByRef, LangItem, LetStmt, LocalSource, Node}; @@ -2941,12 +2940,13 @@ fn static_pattern_match_inner( match pat.ctor() { Constructor::Variant(variant_index) => { - let ValTreeKind::Branch(box [actual_variant_idx]) = *valtree else { + let ValTreeKind::Branch(branch) = *valtree else { bug!("malformed valtree for an enum") }; - - let ValTreeKind::Leaf(actual_variant_idx) = *actual_variant_idx.to_value().valtree - else { + if branch.len() != 1 { + bug!("malformed valtree for an enum") + }; + let ValTreeKind::Leaf(actual_variant_idx) = **branch[0].to_value().valtree else { bug!("malformed valtree for an enum") }; diff --git a/compiler/rustc_mir_build/src/builder/matches/user_ty.rs b/compiler/rustc_mir_build/src/builder/matches/user_ty.rs index 6ba5e360ef82..f6f592d9d3b5 100644 --- a/compiler/rustc_mir_build/src/builder/matches/user_ty.rs +++ b/compiler/rustc_mir_build/src/builder/matches/user_ty.rs @@ -4,10 +4,9 @@ //! This avoids having to repeatedly clone a partly-built [`UserTypeProjections`] //! at every step of the traversal, which is what the previous code was doing. -use std::iter; +use std::{assert_matches, iter}; use rustc_abi::{FieldIdx, VariantIdx}; -use rustc_data_structures::assert_matches; use rustc_data_structures::smallvec::SmallVec; use rustc_middle::mir::{ProjectionElem, UserTypeProjection, UserTypeProjections}; use rustc_middle::ty::{AdtDef, UserTypeAnnotationIndex}; diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index fbd7aa90f49c..bcac45199437 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -24,11 +24,9 @@ use rustc_abi::{ExternAbi, FieldIdx}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_ast::attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_errors::ErrorGuaranteed; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node, find_attr}; @@ -42,7 +40,7 @@ use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; use crate::builder::expr::as_place::PlaceBuilder; use crate::builder::scope::{DropKind, LintLevel}; @@ -70,11 +68,11 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>( /// be called by the query `mir_built`. pub(crate) fn build_mir_inner_impl<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> { tcx.ensure_done().thir_abstract_const(def); - if let Err(e) = tcx.ensure_ok().check_match(def) { + if let Err(e) = tcx.ensure_result().check_match(def) { return construct_error(tcx, def, e); } - if let Err(err) = tcx.ensure_ok().check_tail_calls(def) { + if let Err(err) = tcx.ensure_result().check_tail_calls(def) { return construct_error(tcx, def, err); } @@ -492,7 +490,8 @@ fn construct_fn<'tcx>( ty => span_bug!(span_with_body, "unexpected type of body: {ty:?}"), }; - if let Some((dialect, phase)) = find_attr!(tcx.hir_attrs(fn_id), AttributeKind::CustomMir(dialect, phase, _) => (dialect, phase)) + if let Some((dialect, phase)) = + find_attr!(tcx.hir_attrs(fn_id), CustomMir(dialect, phase, _) => (dialect, phase)) { return custom::build_custom_mir( tcx, @@ -618,8 +617,8 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) - let hir_id = tcx.local_def_id_to_hir_id(def_id); let (inputs, output, coroutine) = match tcx.def_kind(def_id) { - DefKind::Const - | DefKind::AssocConst + DefKind::Const { .. } + | DefKind::AssocConst { .. } | DefKind::AnonConst | DefKind::InlineConst | DefKind::Static { .. } @@ -751,11 +750,10 @@ fn new( coroutine: Option>>, ) -> Builder<'a, 'tcx> { let tcx = infcx.tcx; - let attrs = tcx.hir_attrs(hir_id); // Some functions always have overflow checks enabled, // however, they may not get codegen'd, depending on // the settings for the crate they are codegened in. - let mut check_overflow = attr::contains_name(attrs, sym::rustc_inherit_overflow_checks); + let mut check_overflow = find_attr!(tcx.hir_attrs(hir_id), RustcInheritOverflowChecks); // Respect -C overflow-checks. check_overflow |= tcx.sess.overflow_checks(); // Constants always need overflow checks. diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 7bced8168bd1..f79adb2eb598 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -5,7 +5,6 @@ use rustc_ast::AsmMacro; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::DiagArgValue; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, find_attr}; use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind}; @@ -98,7 +97,7 @@ fn emit_deprecated_safe_fn_call(&self, span: Span, kind: &UnsafeOpKind) -> bool // from an edition before 2024. &UnsafeOpKind::CallToUnsafeFunction(Some(id)) if !span.at_least_rust_2024() - && let Some(suggestion) = find_attr!(self.tcx.get_all_attrs(id), AttributeKind::RustcDeprecatedSafe2024{suggestion} => suggestion) => + && let Some(suggestion) = find_attr!(self.tcx, id, RustcDeprecatedSafe2024{suggestion} => suggestion) => { let sm = self.tcx.sess.source_map(); let guarantee = format!("that {}", suggestion); @@ -116,12 +115,12 @@ fn emit_deprecated_safe_fn_call(&self, span: Span, kind: &UnsafeOpKind) -> bool CallToDeprecatedSafeFnRequiresUnsafe { span, function: with_no_trimmed_paths!(self.tcx.def_path_str(id)), - guarantee, sub: CallToDeprecatedSafeFnRequiresUnsafeSub { start_of_line_suggestion: suggestion, start_of_line: sm.span_extend_to_line(span).shrink_to_lo(), left: span.shrink_to_lo(), right: span.shrink_to_hi(), + guarantee, }, }, ); @@ -449,7 +448,6 @@ fn visit_expr(&mut self, expr: &'a Expr<'tcx>) { | ExprKind::LoopMatch { .. } | ExprKind::Let { .. } | ExprKind::Match { .. } - | ExprKind::Box { .. } | ExprKind::If { .. } | ExprKind::InlineAsm { .. } | ExprKind::LogicalOp { .. } @@ -473,7 +471,7 @@ fn visit_expr(&mut self, expr: &'a Expr<'tcx>) { ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => { let fn_ty = self.thir[fun].ty; let sig = fn_ty.fn_sig(self.tcx); - let (callee_features, safe_target_features): (&[_], _) = match fn_ty.kind() { + let (callee_features, safe_target_features): (&[_], _) = match *fn_ty.kind() { ty::FnDef(func_id, ..) => { let cg_attrs = self.tcx.codegen_fn_attrs(func_id); (&cg_attrs.target_features, cg_attrs.safe_target_features) @@ -1147,7 +1145,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { // Closures and inline consts are handled by their owner, if it has a body assert!(!tcx.is_typeck_child(def.to_def_id())); // Also, don't safety check custom MIR - if find_attr!(tcx.get_all_attrs(def), AttributeKind::CustomMir(..) => ()).is_some() { + if find_attr!(tcx, def, CustomMir(..) => ()).is_some() { return; } diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 58ea9ec5aa22..c362badd033c 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -3,19 +3,18 @@ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan, Subdiagnostic, msg, }; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; use rustc_pattern_analysis::errors::Uncovered; use rustc_pattern_analysis::rustc::RustcPatCtxt; use rustc_span::{Ident, Span, Symbol}; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("call to deprecated safe function `{$function}` is unsafe and requires unsafe block")] pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe { #[label("call to unsafe function")] pub(crate) span: Span, pub(crate) function: String, - pub(crate) guarantee: String, #[subdiagnostic] pub(crate) sub: CallToDeprecatedSafeFnRequiresUnsafeSub, } @@ -33,9 +32,10 @@ pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafeSub { pub(crate) left: Span, #[suggestion_part(code = " }}")] pub(crate) right: Span, + pub(crate) guarantee: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("call to unsafe function `{$function}` is unsafe and requires unsafe block", code = E0133)] #[note("consult the function's documentation for information on how to avoid undefined behavior")] pub(crate) struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe { @@ -46,7 +46,7 @@ pub(crate) struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("call to unsafe function is unsafe and requires unsafe block", code = E0133)] #[note("consult the function's documentation for information on how to avoid undefined behavior")] pub(crate) struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { @@ -56,7 +56,7 @@ pub(crate) struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("use of inline assembly is unsafe and requires unsafe block", code = E0133)] #[note("inline assembly is entirely unchecked and can cause undefined behavior")] pub(crate) struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { @@ -66,7 +66,7 @@ pub(crate) struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe block", code = E0133)] #[note( "initializing a layout restricted type's field with a value outside the valid range is undefined behavior" @@ -78,7 +78,7 @@ pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("initializing type with an unsafe field is unsafe and requires unsafe block", code = E0133)] #[note("unsafe fields may carry library invariants")] pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithUnsafeFieldRequiresUnsafe { @@ -88,7 +88,7 @@ pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithUnsafeFieldRequiresUnsaf pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("use of mutable static is unsafe and requires unsafe block", code = E0133)] #[note( "mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior" @@ -100,7 +100,7 @@ pub(crate) struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("use of extern static is unsafe and requires unsafe block", code = E0133)] #[note( "extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior" @@ -112,7 +112,7 @@ pub(crate) struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("use of unsafe field is unsafe and requires unsafe block", code = E0133)] #[note("unsafe fields may carry library invariants")] pub(crate) struct UnsafeOpInUnsafeFnUseOfUnsafeFieldRequiresUnsafe { @@ -122,7 +122,7 @@ pub(crate) struct UnsafeOpInUnsafeFnUseOfUnsafeFieldRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("dereference of raw pointer is unsafe and requires unsafe block", code = E0133)] #[note( "raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior" @@ -134,7 +134,7 @@ pub(crate) struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("access to union field is unsafe and requires unsafe block", code = E0133)] #[note( "the field may not be properly initialized: using uninitialized data will cause undefined behavior" @@ -146,7 +146,7 @@ pub(crate) struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "mutation of layout constrained field is unsafe and requires unsafe block", code = E0133 @@ -159,7 +159,7 @@ pub(crate) struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsa pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "borrow of layout constrained field with interior mutability is unsafe and requires unsafe block", code = E0133, @@ -171,7 +171,7 @@ pub(crate) struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "unsafe binder cast is unsafe and requires unsafe block information that may be required to uphold safety guarantees of a type", code = E0133, @@ -183,7 +183,7 @@ pub(crate) struct UnsafeOpInUnsafeFnUnsafeBinderCastRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block", code = E0133)] #[help( "in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> @@ -631,7 +631,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unnecessary `unsafe` block")] pub(crate) struct UnusedUnsafe { #[label("unnecessary `unsafe` block")] @@ -755,7 +755,7 @@ pub(crate) struct NonConstPath { pub(crate) span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unreachable pattern")] pub(crate) struct UnreachablePattern<'tcx> { #[label("no value can reach this")] @@ -809,7 +809,7 @@ pub(crate) struct WantedConstant { pub(crate) const_path: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unreachable {$descr}")] pub(crate) struct UnreachableDueToUninhabited<'desc, 'tcx> { pub descr: &'desc str, @@ -874,53 +874,7 @@ pub(crate) struct UpperRangeBoundCannotBeMin { pub(crate) span: Span, } -#[derive(LintDiagnostic)] -#[diag( - "leading irrefutable {$count -> - [one] pattern - *[other] patterns -} in let chain" -)] -#[note( - "{$count -> - [one] this pattern - *[other] these patterns -} will always match" -)] -#[help( - "consider moving {$count -> - [one] it - *[other] them -} outside of the construct" -)] -pub(crate) struct LeadingIrrefutableLetPatterns { - pub(crate) count: usize, -} - -#[derive(LintDiagnostic)] -#[diag( - "trailing irrefutable {$count -> - [one] pattern - *[other] patterns -} in let chain" -)] -#[note( - "{$count -> - [one] this pattern - *[other] these patterns -} will always match" -)] -#[help( - "consider moving {$count -> - [one] it - *[other] them -} into the body" -)] -pub(crate) struct TrailingIrrefutableLetPatterns { - pub(crate) count: usize, -} - -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("pattern binding `{$name}` is named the same as one of the variants of the type `{$ty_path}`", code = E0170)] pub(crate) struct BindingsWithVariantName { #[suggestion( @@ -933,7 +887,7 @@ pub(crate) struct BindingsWithVariantName { pub(crate) name: Ident, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "irrefutable `if let` {$count -> [one] pattern @@ -951,7 +905,7 @@ pub(crate) struct IrrefutableLetPatternsIfLet { pub(crate) count: usize, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "irrefutable `if let` guard {$count -> [one] pattern @@ -969,7 +923,7 @@ pub(crate) struct IrrefutableLetPatternsIfLetGuard { pub(crate) count: usize, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "irrefutable `let...else` {$count -> [one] pattern @@ -978,16 +932,17 @@ pub(crate) struct IrrefutableLetPatternsIfLetGuard { )] #[note( "{$count -> - [one] this pattern - *[other] these patterns -} will always match, so the `else` clause is useless" + [one] this pattern always matches, so the else clause is unreachable + *[other] these patterns always match, so the else clause is unreachable +}" )] -#[help("consider removing the `else` clause")] pub(crate) struct IrrefutableLetPatternsLetElse { pub(crate) count: usize, + #[help("remove this `else` block")] + pub(crate) else_span: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "irrefutable `while let` {$count -> [one] pattern diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 21817dea6cb4..783d40781dac 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -1,9 +1,7 @@ //! Construction of MIR from HIR. // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] -#![feature(if_let_guard)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 0117a10e3a8c..c5eeb8b1aa85 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -3,7 +3,6 @@ use rustc_ast::UnsafeBinderCastKind; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{LangItem, find_attr}; use rustc_index::Idx; @@ -20,7 +19,7 @@ self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, UpvarArgs, }; use rustc_middle::{bug, span_bug}; -use rustc_span::{Span, sym}; +use rustc_span::Span; use tracing::{debug, info, instrument, trace}; use crate::errors::*; @@ -385,24 +384,6 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> from_hir_call: true, fn_span: expr.span, } - } else if let ty::FnDef(def_id, _) = self.typeck_results.expr_ty(fun).kind() - && let Some(intrinsic) = self.tcx.intrinsic(def_id) - && intrinsic.name == sym::box_new - { - // We don't actually evaluate `fun` here, so make sure that doesn't miss any side-effects. - if !matches!(fun.kind, hir::ExprKind::Path(_)) { - span_bug!( - expr.span, - "`box_new` intrinsic can only be called via path expression" - ); - } - let value = &args[0]; - return Expr { - temp_scope_id: expr.hir_id.local_id, - ty: expr_ty, - span: expr.span, - kind: ExprKind::Box { value: self.mirror_expr(value) }, - }; } else { // Tuple-like ADTs are represented as ExprKind::Call. We convert them here. let adt_data = if let hir::ExprKind::Path(ref qpath) = fun.kind @@ -651,7 +632,8 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> .collect(), ) } - hir::StructTailExpr::None => AdtExprBase::None, + hir::StructTailExpr::None + | hir::StructTailExpr::NoneWithError(_) => AdtExprBase::None, }, })) } @@ -663,6 +645,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> base, hir::StructTailExpr::None | hir::StructTailExpr::DefaultFields(_) + | hir::StructTailExpr::NoneWithError(_) )); let index = adt.variant_index_with_id(variant_id); @@ -688,7 +671,10 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> hir::StructTailExpr::Base(base) => { span_bug!(base.span, "unexpected res: {:?}", res); } - hir::StructTailExpr::None => AdtExprBase::None, + hir::StructTailExpr::None + | hir::StructTailExpr::NoneWithError(_) => { + AdtExprBase::None + } }, })) } @@ -820,6 +806,10 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> lit: ScalarInt::try_from_uint(val, Size::from_bits(32)).unwrap(), user_ty: None, }; + let mk_usize_kind = |val: u64| ExprKind::NonHirLiteral { + lit: ScalarInt::try_from_target_usize(val, tcx).unwrap(), + user_ty: None, + }; let mk_call = |thir: &mut Thir<'tcx>, ty: Ty<'tcx>, variant: VariantIdx, field: FieldIdx| { let fun_ty = @@ -856,7 +846,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> }); } - expr.unwrap_or(mk_u32_kind(0)) + expr.unwrap_or_else(|| mk_usize_kind(0)) } hir::ExprKind::ConstBlock(ref anon_const) => { @@ -883,7 +873,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> hir::ExprKind::Ret(v) => ExprKind::Return { value: v.map(|v| self.mirror_expr(v)) }, hir::ExprKind::Become(call) => ExprKind::Become { value: self.mirror_expr(call) }, hir::ExprKind::Break(dest, ref value) => { - if find_attr!(self.tcx.hir_attrs(expr.hir_id), AttributeKind::ConstContinue(_)) { + if find_attr!(self.tcx.hir_attrs(expr.hir_id), ConstContinue(_)) { match dest.target_id { Ok(target_id) => { let (Some(value), Some(_)) = (value, dest.label) else { @@ -948,7 +938,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> match_source, }, hir::ExprKind::Loop(body, ..) => { - if find_attr!(self.tcx.hir_attrs(expr.hir_id), AttributeKind::LoopMatch(_)) { + if find_attr!(self.tcx.hir_attrs(expr.hir_id), LoopMatch(_)) { let dcx = self.tcx.dcx(); // Accept either `state = expr` or `state = expr;`. @@ -1136,8 +1126,8 @@ fn user_args_applied_to_res( Res::Def(DefKind::Fn, _) | Res::Def(DefKind::AssocFn, _) | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) => { + | Res::Def(DefKind::Const { .. }, _) + | Res::Def(DefKind::AssocConst { .. }, _) => { self.typeck_results.user_provided_types().get(hir_id).copied().map(Box::new) } @@ -1226,7 +1216,8 @@ fn convert_path_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, res: Res) -> ExprKi ExprKind::ConstParam { param, def_id } } - Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { + Res::Def(DefKind::Const { .. }, def_id) + | Res::Def(DefKind::AssocConst { .. }, def_id) => { let user_ty = self.user_args_applied_to_res(expr.hir_id, res); ExprKind::NamedConst { def_id, args, user_ty } } diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index b08d1d4bcf27..60cb509ee9dd 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -4,7 +4,6 @@ use rustc_data_structures::steal::Steal; use rustc_errors::ErrorGuaranteed; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; @@ -105,8 +104,7 @@ fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self { typing_env: ty::TypingEnv::non_body_analysis(tcx, def), typeck_results, body_owner: def.to_def_id(), - apply_adjustments: - !find_attr!(tcx.hir_attrs(hir_id), AttributeKind::CustomMir(..) => ()).is_some(), + apply_adjustments: !find_attr!(tcx.hir_attrs(hir_id), CustomMir(..) => ()).is_some(), } } 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 56a5aff41d8b..e844f1114d61 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -160,16 +160,16 @@ fn visit_expr(&mut self, ex: &'p Expr<'tcx>) { self.check_match(scrutinee, arms, MatchSource::Normal, span); } ExprKind::Let { box ref pat, expr } => { - self.check_let(pat, Some(expr), ex.span); + self.check_let(pat, Some(expr), ex.span, None); } ExprKind::LogicalOp { op: LogicalOp::And, .. } if !matches!(self.let_source, LetSource::None) => { let mut chain_refutabilities = Vec::new(); let Ok(()) = self.visit_land(ex, &mut chain_refutabilities) else { return }; - // If at least one of the operands is a `let ... = ...`. - if chain_refutabilities.iter().any(|x| x.is_some()) { - self.check_let_chain(chain_refutabilities, ex.span); + // Lint only single irrefutable let binding. + if let [Some((_, Irrefutable))] = chain_refutabilities[..] { + self.lint_single_let(ex.span, None); } return; } @@ -184,8 +184,9 @@ fn visit_stmt(&mut self, stmt: &'p Stmt<'tcx>) { self.with_hir_source(hir_id, |this| { let let_source = if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet }; + let else_span = else_block.map(|bid| this.thir.blocks[bid].span); this.with_let_source(let_source, |this| { - this.check_let(pattern, initializer, span) + this.check_let(pattern, initializer, span, else_span) }); visit::walk_stmt(this, stmt); }); @@ -342,7 +343,6 @@ fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool { | Binary { .. } | Block { .. } | Borrow { .. } - | Box { .. } | Call { .. } | ByUse { .. } | Closure { .. } @@ -427,22 +427,19 @@ fn analyze_patterns( } #[instrument(level = "trace", skip(self))] - fn check_let(&mut self, pat: &'p Pat<'tcx>, scrutinee: Option, span: Span) { + fn check_let( + &mut self, + pat: &'p Pat<'tcx>, + scrutinee: Option, + span: Span, + else_span: Option, + ) { assert!(self.let_source != LetSource::None); let scrut = scrutinee.map(|id| &self.thir[id]); if let LetSource::PlainLet = self.let_source { - self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span)) - } else { - let Ok(refutability) = self.is_let_irrefutable(pat, scrut) else { return }; - if matches!(refutability, Irrefutable) { - report_irrefutable_let_patterns( - self.tcx, - self.hir_source, - self.let_source, - 1, - span, - ); - } + self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span)); + } else if let Ok(Irrefutable) = self.is_let_irrefutable(pat, scrut) { + self.lint_single_let(span, else_span); } } @@ -550,74 +547,15 @@ fn check_match( } #[instrument(level = "trace", skip(self))] - fn check_let_chain( - &mut self, - chain_refutabilities: Vec>, - whole_chain_span: Span, - ) { - assert!(self.let_source != LetSource::None); - - if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, Irrefutable)))) { - // The entire chain is made up of irrefutable `let` statements - report_irrefutable_let_patterns( - self.tcx, - self.hir_source, - self.let_source, - chain_refutabilities.len(), - whole_chain_span, - ); - return; - } - - if let Some(until) = - chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, Irrefutable)))) - && until > 0 - { - // The chain has a non-zero prefix of irrefutable `let` statements. - - // Check if the let source is while, for there is no alternative place to put a prefix, - // and we shouldn't lint. - // For let guards inside a match, prefixes might use bindings of the match pattern, - // so can't always be moved out. - // For `else if let`, an extra indentation level would be required to move the bindings. - // FIXME: Add checking whether the bindings are actually used in the prefix, - // and lint if they are not. - if !matches!( - self.let_source, - LetSource::WhileLet | LetSource::IfLetGuard | LetSource::ElseIfLet - ) { - // Emit the lint - let prefix = &chain_refutabilities[..until]; - let span_start = prefix[0].unwrap().0; - let span_end = prefix.last().unwrap().unwrap().0; - let span = span_start.to(span_end); - let count = prefix.len(); - self.tcx.emit_node_span_lint( - IRREFUTABLE_LET_PATTERNS, - self.hir_source, - span, - LeadingIrrefutableLetPatterns { count }, - ); - } - } - - if let Some(from) = - chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, Irrefutable)))) - && from != (chain_refutabilities.len() - 1) - { - // The chain has a non-empty suffix of irrefutable `let` statements - let suffix = &chain_refutabilities[from + 1..]; - let span_start = suffix[0].unwrap().0; - let span_end = suffix.last().unwrap().unwrap().0; - let span = span_start.to(span_end); - let count = suffix.len(); - self.tcx.emit_node_span_lint( - IRREFUTABLE_LET_PATTERNS, - self.hir_source, - span, - TrailingIrrefutableLetPatterns { count }, - ); - } + fn lint_single_let(&mut self, let_span: Span, else_span: Option) { + report_irrefutable_let_patterns( + self.tcx, + self.hir_source, + self.let_source, + 1, + let_span, + else_span, + ); } fn analyze_binding( @@ -912,6 +850,7 @@ fn report_irrefutable_let_patterns( source: LetSource, count: usize, span: Span, + else_span: Option, ) { macro_rules! emit_diag { ($lint:tt) => {{ @@ -923,7 +862,14 @@ macro_rules! emit_diag { LetSource::None | LetSource::PlainLet | LetSource::Else => bug!(), LetSource::IfLet | LetSource::ElseIfLet => emit_diag!(IrrefutableLetPatternsIfLet), LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard), - LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse), + LetSource::LetElse => { + tcx.emit_node_span_lint( + IRREFUTABLE_LET_PATTERNS, + id, + span, + IrrefutableLetPatternsLetElse { count, else_span }, + ); + } LetSource::WhileLet => emit_diag!(IrrefutableLetPatternsWhileLet), } } @@ -1033,7 +979,7 @@ fn find_fallback_pattern_typo<'tcx>( continue; }; if let Some(value_ns) = path.res.value_ns - && let Res::Def(DefKind::Const, id) = value_ns + && let Res::Def(DefKind::Const { .. }, id) = value_ns && infcx.can_eq(param_env, ty, cx.tcx.type_of(id).instantiate_identity()) { if cx.tcx.visibility(id).is_accessible_from(parent, cx.tcx) { @@ -1050,7 +996,7 @@ fn find_fallback_pattern_typo<'tcx>( } } } - if let DefKind::Const = cx.tcx.def_kind(item.owner_id) + if let DefKind::Const { .. } = cx.tcx.def_kind(item.owner_id) && infcx.can_eq(param_env, ty, cx.tcx.type_of(item.owner_id).instantiate_identity()) { // Look for local consts. @@ -1190,7 +1136,7 @@ fn is_const_pat_that_looks_like_binding<'tcx>(tcx: TyCtxt<'tcx>, pat: &Pat<'tcx> // 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? } - && matches!(tcx.def_kind(def_id), DefKind::Const) + && matches!(tcx.def_kind(def_id), DefKind::Const { .. }) && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(pat.span) && snippet.chars().all(|c| c.is_alphanumeric() || c == '_') { 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 cce5776293e2..ff9d456f7e70 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 @@ -5,7 +5,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Diag, msg}; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::find_attr; use rustc_index::Idx; use rustc_infer::infer::TyCtxtInferExt; @@ -75,13 +74,14 @@ fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box> { if let ty::ConstKind::Unevaluated(uv) = self.c.kind() { let def_kind = self.tcx.def_kind(uv.def); - if let hir::def::DefKind::AssocConst = def_kind + if let hir::def::DefKind::AssocConst { .. } = def_kind && let Some(def_id) = uv.def.as_local() { // Include the container item in the output. err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), ""); } - if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind { + if let hir::def::DefKind::Const { .. } | hir::def::DefKind::AssocConst { .. } = def_kind + { err.span_label(self.tcx.def_span(uv.def), msg!("constant defined here")); } } @@ -117,7 +117,7 @@ fn unevaluated_to_pat( // We've emitted an error on the original const, it would be redundant to complain // on its use as well. if let ty::ConstKind::Unevaluated(uv) = self.c.kind() - && let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = + && let hir::def::DefKind::Const { .. } | hir::def::DefKind::AssocConst { .. } = self.tcx.def_kind(uv.def) { err.downgrade_to_delayed_bug(); @@ -488,8 +488,7 @@ fn type_has_partial_eq_impl<'tcx>( let mut structural_peq = false; let mut impl_def_id = None; for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) { - automatically_derived = - find_attr!(tcx.get_all_attrs(def_id), AttributeKind::AutomaticallyDerived(..)); + automatically_derived = find_attr!(tcx, def_id, AutomaticallyDerived(..)); impl_def_id = Some(def_id); } for _ in tcx.non_blanket_impls_for_ty(structural_partial_eq_trait_id, ty) { diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs index 095023a471b9..9a5cc08e6c4c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/migration.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs @@ -129,7 +129,7 @@ fn format_subdiagnostics(self, diag: &mut Diag<'_, impl EmissionGuarantee>) { // FIXME(dianne): for peace of mind, don't risk emitting a 0-part suggestion (that panics!) debug_assert!(!self.suggestion.is_empty()); if !self.suggestion.is_empty() { - diag.multipart_suggestion_verbose(msg, self.suggestion, applicability); + diag.multipart_suggestion(msg, self.suggestion, applicability); } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index acf20cb092e2..67cde0e2c886 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -4,12 +4,12 @@ mod const_to_pat; mod migration; +use std::assert_matches; use std::cmp::Ordering; use std::sync::Arc; use rustc_abi::{FieldIdx, Integer}; use rustc_ast::LitKind; -use rustc_data_structures::assert_matches; use rustc_errors::codes::*; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; @@ -635,7 +635,8 @@ fn lower_path( let res = self.typeck_results.qpath_res(qpath, id); let (def_id, user_ty) = match res { - Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { + Res::Def(DefKind::Const { .. }, def_id) + | Res::Def(DefKind::AssocConst { .. }, def_id) => { (def_id, self.typeck_results.user_provided_types().get(id)) } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index db8b2518981d..27dec99696f5 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -223,11 +223,6 @@ fn print_expr_kind(&mut self, expr_kind: &ExprKind<'tcx>, depth_lvl: usize) { self.print_expr(*value, depth_lvl + 2); print_indented!(self, "}", depth_lvl); } - Box { value } => { - print_indented!(self, "Box {", depth_lvl); - self.print_expr(*value, depth_lvl + 1); - print_indented!(self, "}", depth_lvl); - } If { if_then_scope, cond, then, else_opt } => { print_indented!(self, "If {", depth_lvl); print_indented!(self, format!("if_then_scope: {:?}", if_then_scope), depth_lvl + 1); @@ -402,9 +397,10 @@ fn print_expr_kind(&mut self, expr_kind: &ExprKind<'tcx>, depth_lvl: usize) { } Index { lhs, index } => { print_indented!(self, "Index {", depth_lvl); - print_indented!(self, format!("index: {:?}", index), depth_lvl + 1); print_indented!(self, "lhs:", depth_lvl + 1); self.print_expr(*lhs, depth_lvl + 2); + print_indented!(self, "index:", depth_lvl + 1); + self.print_expr(*index, depth_lvl + 2); print_indented!(self, "}", depth_lvl); } VarRef { id } => { diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs index 0093bb762110..37ef76d31864 100644 --- a/compiler/rustc_mir_build/src/thir/util.rs +++ b/compiler/rustc_mir_build/src/thir/util.rs @@ -1,4 +1,5 @@ -use rustc_data_structures::assert_matches; +use std::assert_matches; + use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_middle::bug; diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index c4b9b4ce6416..6c0f2e8d7305 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -8,7 +8,7 @@ use regex::Regex; use rustc_graphviz as dot; -use rustc_hir::attrs::{AttributeKind, BorrowckGraphvizFormatKind, RustcMirKind}; +use rustc_hir::attrs::{BorrowckGraphvizFormatKind, RustcMirKind}; use rustc_hir::find_attr; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{ @@ -97,8 +97,7 @@ impl RustcMirAttrs { fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Self { let mut ret = RustcMirAttrs::default(); - let attrs = tcx.get_all_attrs(def_id); - if let Some(rustc_mir_attrs) = find_attr!(attrs, AttributeKind::RustcMir(kind) => kind) { + if let Some(rustc_mir_attrs) = find_attr!(tcx, def_id, RustcMir(kind) => kind) { for attr in rustc_mir_attrs { match attr { RustcMirKind::BorrowckGraphvizPostflow { path } => { diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 4b2c52ad7999..d5548266aa01 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -86,7 +86,6 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { Rvalue::Cast(..) | Rvalue::Ref(_, BorrowKind::Fake(_), _) - | Rvalue::ShallowInitBox(..) | Rvalue::Use(..) | Rvalue::ThreadLocalRef(..) | Rvalue::Repeat(..) diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 6a0881ec2bcb..dee08d34427f 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -1,5 +1,6 @@ +use std::assert_matches; + use rustc_abi::VariantIdx; -use rustc_data_structures::assert_matches; use rustc_index::Idx; use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_middle::bug; diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 692591a41a15..c5026c1c92aa 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -1,5 +1,4 @@ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(exact_size_is_empty)] diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index ced9bd735ba2..224abf6901b3 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -391,15 +391,7 @@ fn gather_statement(&mut self, stmt: &Statement<'tcx>) { } StatementKind::Assign(box (place, rval)) => { self.create_move_path(*place); - if let RvalueInitializationState::Shallow = rval.initialization_state() { - // Box starts out uninitialized - need to create a separate - // move-path for the interior so it will be separate from - // the exterior. - self.create_move_path(self.tcx.mk_place_deref(*place)); - self.gather_init(place.as_ref(), InitKind::Shallow); - } else { - self.gather_init(place.as_ref(), InitKind::Deep); - } + self.gather_init(place.as_ref(), InitKind::Deep); self.gather_rvalue(rval); } StatementKind::FakeRead(box (_, place)) => { @@ -435,7 +427,6 @@ fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { Rvalue::Use(ref operand) | Rvalue::Repeat(ref operand, _) | Rvalue::Cast(_, ref operand, _) - | Rvalue::ShallowInitBox(ref operand, _) | Rvalue::UnaryOp(_, ref operand) | Rvalue::WrapUnsafeBinder(ref operand, _) => self.gather_operand(operand), Rvalue::BinaryOp(ref _binop, box (ref lhs, ref rhs)) => { diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 1c5b38361669..5b5afd7ecc7d 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::{AttributeKind, RustcMirKind}; +use rustc_hir::attrs::RustcMirKind; use rustc_hir::find_attr; use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -16,8 +16,7 @@ pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let def_id = body.source.def_id(); - let attrs = tcx.get_all_attrs(def_id); - if let Some(kind) = find_attr!(attrs, AttributeKind::RustcMir(kind) => kind) { + if let Some(kind) = find_attr!(tcx, def_id, RustcMir(kind) => kind) { let move_data = MoveData::gather_moves(body, tcx, |_| true); debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); if kind.contains(&RustcMirKind::PeekMaybeInit) { diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index f102b7bb50f5..055d124386cf 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -1,8 +1,8 @@ +use std::debug_assert_matches; use std::fmt::{Debug, Formatter}; use std::ops::Range; use rustc_abi::{FieldIdx, VariantIdx}; -use rustc_data_structures::debug_assert_matches; use rustc_data_structures::fx::{FxHashMap, FxIndexSet, StdEntry}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_index::IndexVec; diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index e06883dd0fd1..f481a73bfbe5 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -82,7 +82,6 @@ fn insert_alignment_check<'tcx>( // If this target does not have reliable alignment, further limit the mask by anding it with // the mask for the highest reliable alignment. - #[allow(irrefutable_let_patterns)] if let max_align = tcx.sess.target.max_reliable_alignment() && max_align < Align::MAX { diff --git a/compiler/rustc_mir_transform/src/check_inline.rs b/compiler/rustc_mir_transform/src/check_inline.rs index 1f65bd1ba69b..aa945266413d 100644 --- a/compiler/rustc_mir_transform/src/check_inline.rs +++ b/compiler/rustc_mir_transform/src/check_inline.rs @@ -1,7 +1,7 @@ //! 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::{AttributeKind, InlineAttr}; +use rustc_hir::attrs::InlineAttr; use rustc_hir::def_id::DefId; use rustc_hir::find_attr; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -42,7 +42,7 @@ pub(super) fn is_inline_valid_on_fn<'tcx>( ) -> Result<(), &'static str> { let codegen_attrs = tcx.codegen_fn_attrs(def_id); - if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcNoMirInline) { + if find_attr!(tcx, def_id, RustcNoMirInline) { return Err("#[rustc_no_mir_inline]"); } @@ -63,7 +63,7 @@ pub(super) fn is_inline_valid_on_fn<'tcx>( // but at this stage we don't know whether codegen knows the intrinsic, // so just conservatively don't inline it. This also ensures that we do not // accidentally inline the body of an intrinsic that *must* be overridden. - if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcIntrinsic) { + if find_attr!(tcx, def_id, RustcIntrinsic) { return Err("callee is an intrinsic"); } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 705551c58f32..c83b10a5e583 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -64,7 +64,6 @@ use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::pluralize; -use rustc_hir::attrs::AttributeKind; use rustc_hir::lang_items::LangItem; use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind, find_attr}; use rustc_index::bit_set::{BitMatrix, DenseBitSet, GrowableBitSet}; @@ -1988,9 +1987,7 @@ fn check_must_not_suspend_def( hir_id: hir::HirId, data: SuspendCheckData<'_>, ) -> bool { - if let Some(reason_str) = - find_attr!(tcx.get_all_attrs(def_id), AttributeKind::MustNotSupend {reason} => reason) - { + if let Some(reason_str) = find_attr!(tcx, def_id, MustNotSupend {reason} => reason) { let reason = reason_str.map(|s| errors::MustNotSuspendReason { span: data.source_span, reason: s }); tcx.emit_node_span_lint( diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 63c550c27fe4..85870be912b5 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::{AttributeKind, CoverageAttrKind}; +use rustc_hir::attrs::CoverageAttrKind; use rustc_hir::find_attr; use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -49,9 +49,7 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { /// Query implementation for `coverage_attr_on`. fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // Check for a `#[coverage(..)]` attribute on this def. - if let Some(kind) = - find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_sp, kind) => kind) - { + if let Some(kind) = find_attr!(tcx, def_id, Coverage(_sp, kind) => kind) { match kind { CoverageAttrKind::On => return true, CoverageAttrKind::Off => return false, diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index da133861617f..19ffffdd1eca 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::{AttributeKind, InlineAttr}; +use rustc_hir::attrs::InlineAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::find_attr; @@ -8,7 +8,6 @@ use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::config::{InliningThreshold, OptLevel}; -use rustc_span::sym; use crate::{inline, pass_manager as pm}; @@ -37,14 +36,11 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { } // FIXME(autodiff): replace this as per discussion in https://github.com/rust-lang/rust/pull/149033#discussion_r2535465880 - if tcx.has_attr(def_id, sym::autodiff_forward) - || tcx.has_attr(def_id, sym::autodiff_reverse) - || tcx.has_attr(def_id, sym::rustc_autodiff) - { + if find_attr!(tcx, def_id, RustcAutodiff(..)) { return true; } - if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcIntrinsic) { + if find_attr!(tcx, def_id, RustcIntrinsic) { // Intrinsic fallback bodies are always cross-crate inlineable. // To ensure that the MIR inliner doesn't cluelessly try to inline fallback // bodies even when the backend would implement something better, we stop @@ -158,7 +154,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _: Location) { // But intrinsics don't have a body that gets assigned to a CGU, so they are // ignored. if let Some((fn_def_id, _)) = func.const_fn_def() - && find_attr!(tcx.get_all_attrs(fn_def_id), AttributeKind::RustcIntrinsic) + && find_attr!(tcx, fn_def_id, RustcIntrinsic) { return; } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 604f1da1a3ab..122429276e3c 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -2,6 +2,7 @@ //! //! Currently, this pass only propagates scalar values. +use std::assert_matches; use std::cell::RefCell; use std::fmt::Formatter; @@ -10,7 +11,6 @@ use rustc_const_eval::interpret::{ ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable, interp_ok, }; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_middle::bug; @@ -467,7 +467,6 @@ fn handle_rvalue( Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map), Rvalue::Use(operand) => return self.handle_operand(operand, state), Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), - Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in runtime MIR"), Rvalue::Ref(..) | Rvalue::RawPtr(..) => { // We don't track such places. return ValueOrPlace::TOP; diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index 1b3e01f3a380..ad94c23f229c 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -121,17 +121,29 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location // `f` passes. Note that function arguments are the only situation in which this problem can // arise: every other use of `move` in MIR doesn't actually write to the value it moves // from. - if let TerminatorKind::Call { ref args, .. } = terminator.kind { - for arg in args { - if let Operand::Move(place) = arg.node - && !place.is_indirect_first_projection() - && let Some(i) = self.as_param(place.local) - { - self.usage[i] |= UsageSummary::MUTATE; - self.usage[i] |= UsageSummary::CAPTURE; + match terminator.kind { + TerminatorKind::Call { ref args, .. } => { + for arg in args { + if let Operand::Move(place) = arg.node + && !place.is_indirect_first_projection() + && let Some(i) = self.as_param(place.local) + { + self.usage[i] |= UsageSummary::MUTATE; + self.usage[i] |= UsageSummary::CAPTURE; + } } } - }; + + // 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, ... }`. + TerminatorKind::TailCall { .. } => { + for usage in self.usage.iter_mut() { + *usage |= UsageSummary::MUTATE; + *usage |= UsageSummary::CAPTURE; + } + } + _ => {} + } self.super_terminator(terminator, location); } diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index 808be19cbd81..68c47ec4c192 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -1,12 +1,8 @@ //! This pass transforms derefs of Box into a deref of the pointer inside Box. //! //! Box is not actually a pointer so it is incorrect to dereference it directly. -//! -//! `ShallowInitBox` being a device for drop elaboration to understand deferred assignment to box -//! contents, we do not need this any more on runtime MIR. -use rustc_abi::{FieldIdx, VariantIdx}; -use rustc_index::{IndexVec, indexvec}; +use rustc_abi::FieldIdx; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; use rustc_middle::span_bug; @@ -89,68 +85,6 @@ fn visit_place( self.super_place(place, context, location); } - - fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) { - self.super_statement(stmt, location); - - let tcx = self.tcx; - let source_info = stmt.source_info; - - if let StatementKind::Assign(box (_, ref mut rvalue)) = stmt.kind - && let Rvalue::ShallowInitBox(ref mut mutptr_to_u8, pointee) = *rvalue - && let ty::Adt(box_adt, box_args) = Ty::new_box(tcx, pointee).kind() - { - let args = tcx.mk_args(&[pointee.into()]); - let (unique_ty, nonnull_ty, ptr_ty) = - build_ptr_tys(tcx, pointee, self.unique_def, self.nonnull_def); - let adt_kind = |def: ty::AdtDef<'tcx>, args| { - Box::new(AggregateKind::Adt(def.did(), VariantIdx::ZERO, args, None, None)) - }; - let zst = |ty| { - Operand::Constant(Box::new(ConstOperand { - span: source_info.span, - user_ty: None, - const_: Const::zero_sized(ty), - })) - }; - - let constptr = self.patch.new_temp(ptr_ty, source_info.span); - self.patch.add_assign( - location, - constptr.into(), - Rvalue::Cast(CastKind::Transmute, mutptr_to_u8.clone(), ptr_ty), - ); - - let nonnull = self.patch.new_temp(nonnull_ty, source_info.span); - self.patch.add_assign( - location, - nonnull.into(), - Rvalue::Aggregate( - adt_kind(self.nonnull_def, args), - indexvec![Operand::Move(constptr.into())], - ), - ); - - let unique = self.patch.new_temp(unique_ty, source_info.span); - let phantomdata_ty = - self.unique_def.non_enum_variant().fields[FieldIdx::ONE].ty(tcx, args); - self.patch.add_assign( - location, - unique.into(), - Rvalue::Aggregate( - adt_kind(self.unique_def, args), - indexvec![Operand::Move(nonnull.into()), zst(phantomdata_ty)], - ), - ); - - let global_alloc_ty = - box_adt.non_enum_variant().fields[FieldIdx::ONE].ty(tcx, box_args); - *rvalue = Rvalue::Aggregate( - adt_kind(*box_adt, box_args), - indexvec![Operand::Move(unique.into()), zst(global_alloc_ty)], - ); - } - } } pub(super) struct ElaborateBoxDerefs; diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 8998be26412d..e17629215b79 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -160,7 +160,7 @@ struct DropCtxt<'a, 'b, 'tcx, D> /// /// The passed `elaborator` is used to determine what should happen at the drop terminator. It /// decides whether the drop can be statically determined or whether it needs a dynamic drop flag, -/// and whether the drop is "open", ie. should be expanded to drop all subfields of the dropped +/// and whether the drop is "open", i.e. should be expanded to drop all subfields of the dropped /// value. /// /// When this returns, the MIR patch in the `elaborator` contains the necessary changes. @@ -251,19 +251,16 @@ fn build_async_drop( // impl_item_refs may be empty if drop fn is not implemented in 'impl AsyncDrop for ...' // (#140974). // Such code will report error, so just generate sync drop here and return - let Some(drop_fn_def_id) = tcx - .associated_item_def_ids(drop_trait) - .first() - .and_then(|def_id| { + let Some(drop_fn_def_id) = + tcx.associated_item_def_ids(drop_trait).first().and_then(|&def_id| { if tcx.def_kind(def_id) == DefKind::AssocFn - && tcx.check_args_compatible(*def_id, trait_args) + && tcx.check_args_compatible(def_id, trait_args) { Some(def_id) } else { None } }) - .copied() else { tcx.dcx().span_delayed_bug( self.elaborator.body().span, @@ -326,12 +323,23 @@ fn build_async_drop( const_: Const::zero_sized(pin_obj_new_unchecked_fn), })); + // Create an intermediate block that does StorageDead(fut) then jumps to succ. + // This is necessary because `succ` may differ from `self.succ` (e.g. when + // build_async_drop is called from drop_loop, `succ` is the loop header). + // Placing StorageDead directly at `self.succ` would miss the loop-back edge, + // causing StorageLive(fut) to fire again without a preceding StorageDead. + let succ_with_dead = self.new_block_with_statements( + unwind, + vec![Statement::new(self.source_info, StatementKind::StorageDead(fut.local))], + TerminatorKind::Goto { target: succ }, + ); + // #3:drop_term_bb let drop_term_bb = self.new_block( unwind, TerminatorKind::Drop { place, - target: succ, + target: succ_with_dead, unwind: unwind.into_action(), replace: false, drop: dropline, @@ -381,12 +389,6 @@ fn build_async_drop( fn_span: self.source_info.span, }, ); - - // StorageDead(fut) in self.succ block (at the begin) - self.elaborator.patch().add_statement( - Location { block: self.succ, statement_index: 0 }, - StatementKind::StorageDead(fut.local), - ); // StorageDead(fut) in unwind block (at the begin) if let Unwind::To(block) = unwind { self.elaborator.patch().add_statement( @@ -420,11 +422,8 @@ fn build_async_drop( fn build_drop(&mut self, bb: BasicBlock) { let drop_ty = self.place_ty(self.place); - if self.tcx().features().async_drop() - && self.elaborator.body().coroutine.is_some() - && self.elaborator.allow_async_drops() - && !self.elaborator.patch_ref().block(self.elaborator.body(), bb).is_cleanup - && drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env()) + if !self.elaborator.patch_ref().block(self.elaborator.body(), bb).is_cleanup + && self.check_if_can_async_drop(drop_ty, false) { self.build_async_drop( self.place, @@ -450,6 +449,46 @@ fn build_drop(&mut self, bb: BasicBlock) { } } + /// Function to check if we can generate an async drop here + fn check_if_can_async_drop(&mut self, drop_ty: Ty<'tcx>, call_destructor_only: bool) -> bool { + let is_async_drop_feature_enabled = if self.tcx().features().async_drop() { + true + } else { + // Check if the type needing async drop comes from a dependency crate. + if let ty::Adt(adt_def, _) = drop_ty.kind() { + !adt_def.did().is_local() && adt_def.async_destructor(self.tcx()).is_some() + } else { + false + } + }; + + // Short-circuit before calling needs_async_drop/is_async_drop, as those + // require the `async_drop` lang item to exist (which may not be present + // in minimal/custom core environments like cranelift's mini_core). + if !is_async_drop_feature_enabled + || !self.elaborator.body().coroutine.is_some() + || !self.elaborator.allow_async_drops() + { + return false; + } + + let needs_async_drop = if call_destructor_only { + drop_ty.is_async_drop(self.tcx(), self.elaborator.typing_env()) + } else { + drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env()) + }; + + // Async drop in libstd/libcore would become insta-stable — catch that mistake. + if needs_async_drop && self.tcx().features().staged_api() { + span_bug!( + self.source_info.span, + "don't use async drop in libstd, it becomes insta-stable" + ); + } + + needs_async_drop + } + /// This elaborates a single drop instruction, located at `bb`, and /// patches over it. /// @@ -1001,12 +1040,7 @@ fn destructor_call_block( ) -> BasicBlock { debug!("destructor_call_block({:?}, {:?})", self, succ); let ty = self.place_ty(self.place); - if self.tcx().features().async_drop() - && self.elaborator.body().coroutine.is_some() - && self.elaborator.allow_async_drops() - && !unwind.is_cleanup() - && ty.is_async_drop(self.tcx(), self.elaborator.typing_env()) - { + if !unwind.is_cleanup() && self.check_if_can_async_drop(ty, true) { self.build_async_drop(self.place, ty, None, succ, unwind, dropline, true) } else { self.destructor_call_block_sync((succ, unwind)) @@ -1076,12 +1110,7 @@ fn drop_loop( let loop_block = self.elaborator.patch().new_block(loop_block); let place = tcx.mk_place_deref(ptr); - if self.tcx().features().async_drop() - && self.elaborator.body().coroutine.is_some() - && self.elaborator.allow_async_drops() - && !unwind.is_cleanup() - && ety.needs_async_drop(self.tcx(), self.elaborator.typing_env()) - { + if !unwind.is_cleanup() && self.check_if_can_async_drop(ety, false) { self.build_async_drop( place, ety, @@ -1366,12 +1395,7 @@ fn drop_block_simple(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBloc fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { let drop_ty = self.place_ty(self.place); - if self.tcx().features().async_drop() - && self.elaborator.body().coroutine.is_some() - && self.elaborator.allow_async_drops() - && !unwind.is_cleanup() - && drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env()) - { + if !unwind.is_cleanup() && self.check_if_can_async_drop(drop_ty, false) { self.build_async_drop( self.place, drop_ty, diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 7421a55f2a79..6f0f9cc23a55 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -1,8 +1,10 @@ use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, EmissionGuarantee, LintDiagnostic, Subdiagnostic, msg}; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_errors::{ + Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, Subdiagnostic, msg, +}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::mir::AssertKind; -use rustc_middle::query::Key; +use rustc_middle::query::QueryKey; use rustc_middle::ty::TyCtxt; use rustc_session::lint::{self, Lint}; use rustc_span::def_id::DefId; @@ -48,7 +50,7 @@ pub(crate) fn emit_inline_always_target_feature_diagnostic<'a, 'tcx>( ); } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("function cannot return without recursing")] #[help("a `loop` may express intention better if this is on purpose")] pub(crate) struct UnconditionalRecursion { @@ -70,7 +72,7 @@ pub(crate) struct InvalidForceInline { pub reason: &'static str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum ConstMutate { #[diag("attempting to modify a `const` item")] #[note( @@ -129,21 +131,26 @@ pub(crate) enum AssertLintKind { UnconditionalPanic, } -impl<'a, P: std::fmt::Debug> LintDiagnostic<'a, ()> for AssertLint

{ - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(match self.lint_kind { - AssertLintKind::ArithmeticOverflow => { - msg!("this arithmetic operation will overflow") - } - AssertLintKind::UnconditionalPanic => { - msg!("this operation will panic at runtime") - } - }); +impl<'a, P: std::fmt::Debug> Diagnostic<'a, ()> for AssertLint

-/// This type is not properly implemented yet, and the documentation below is thus not accurate. -///
-/// /// That is, if a reference (or a `Box`) is wrapped in `MaybeDangling` (including when in a /// (nested) field of a compound type wrapped in `MaybeDangling`), it does not have to follow /// pointer aliasing rules or be dereferenceable. @@ -73,6 +69,7 @@ #[repr(transparent)] #[rustc_pub_transparent] #[derive(Debug, Copy, Clone, Default)] +#[lang = "maybe_dangling"] pub struct MaybeDangling(P); impl MaybeDangling", + "\ +
\ +
This impl block contains no public items.
\ +
", )?; } write!(w, "
{doc}
")?; @@ -2779,46 +2786,12 @@ fn render_call_locations( let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES; let locations_encoded = serde_json::to_string(&line_ranges).unwrap(); - let source_map = tcx.sess.source_map(); - let files = source_map.files(); - let local = tcx.sess.local_crate_source_file().unwrap(); - - let get_file_start_pos = || { - let crate_src = local.clone().into_local_path()?; - let abs_crate_src = crate_src.canonicalize().ok()?; - let crate_root = abs_crate_src.parent()?.parent()?; - let rel_path = path.strip_prefix(crate_root).ok()?; - files - .iter() - .find(|file| match &file.name { - FileName::Real(real) => real.local_path().map_or(false, |p| p == rel_path), - _ => false, - }) - .map(|file| file.start_pos) - }; - - // Look for the example file in the source map if it exists, otherwise - // return a span to the local crate's source file - let Some(file_span) = get_file_start_pos() - .or_else(|| { - files - .iter() - .find(|file| match &file.name { - FileName::Real(file_name) => file_name == &local, - _ => false, - }) - .map(|file| file.start_pos) - }) - .map(|start_pos| { - rustc_span::Span::with_root_ctxt( - start_pos + BytePos(byte_min), - start_pos + BytePos(byte_max), - ) - }) - else { - // if the fallback span can't be built, don't render the code for this example - return false; - }; + // For scraped examples, we don't need a real span from the SourceMap. + // The URL is already provided in ScrapedInfo, and sources::print_src + // will use that directly. We use DUMMY_SP as a placeholder. + // Note: DUMMY_SP is safe here because href_from_span won't be called + // for scraped examples. + let file_span = rustc_span::DUMMY_SP; let mut decoration_info = FxIndexMap::default(); decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]); diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 7c493653e77c..5f69cc030e26 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -13,7 +13,6 @@ use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::thin_vec::ThinVec; -use rustc_hir::attrs::AttributeKind; use rustc_hir::find_attr; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; @@ -1464,7 +1463,7 @@ pub(crate) fn build_index( return None; } let path = if item.ty == ItemType::Macro - && find_attr!(tcx.get_all_attrs(defid), AttributeKind::MacroExport { .. }) + && find_attr!(tcx, defid, MacroExport { .. }) { // `#[macro_export]` always exports to the crate root. vec![tcx.crate_name(defid.krate)] @@ -1977,7 +1976,7 @@ pub(crate) fn get_function_type_for_search( clean::ForeignFunctionItem(ref f, _) | clean::FunctionItem(ref f) | clean::MethodItem(ref f, _) - | clean::RequiredMethodItem(ref f) => { + | clean::RequiredMethodItem(ref f, _) => { get_fn_inputs_and_outputs(f, tcx, impl_or_trait_generics, cache) } clean::ConstantItem(ref c) => make_nullary_fn(&c.type_), @@ -2050,6 +2049,7 @@ fn get_index_type_id( } // Not supported yet clean::Type::Pat(..) + | clean::Type::FieldOf(..) | clean::Generic(_) | clean::SelfTy | clean::ImplTrait(_) diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs index fb1f0271c2ad..5d5b8d5df685 100644 --- a/src/librustdoc/html/render/type_layout.rs +++ b/src/librustdoc/html/render/type_layout.rs @@ -3,9 +3,8 @@ use askama::Template; use rustc_abi::{Primitive, TagEncoding, Variants}; use rustc_hir::def_id::DefId; -use rustc_middle::span_bug; use rustc_middle::ty::layout::LayoutError; -use rustc_middle::ty::{self}; +use rustc_middle::{span_bug, ty}; use rustc_span::symbol::Symbol; use crate::html::render::Context; diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 89fd78979839..dda9b7c55351 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -344,9 +344,15 @@ pub(crate) fn print_src( lines += line_info.start_line as usize; } let code = fmt::from_fn(move |fmt| { - let current_href = context - .href_from_span(clean::Span::new(file_span), false) - .expect("only local crates should have sources emitted"); + // For scraped examples, use the URL from ScrapedInfo directly. + // For regular sources, derive it from the span. + let current_href = if let SourceContext::Embedded(info) = source_context { + info.url.to_string() + } else { + context + .href_from_span(clean::Span::new(file_span), false) + .expect("only local crates should have sources emitted") + }; highlight::write_code( fmt, s, diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 2edf7891be40..bc9ad1606b8a 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -296,7 +296,7 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum MethodItem(m, _) => { ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer)) } - RequiredMethodItem(m) => { + RequiredMethodItem(m, _) => { ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer)) } ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)), @@ -579,6 +579,8 @@ fn from_clean(ty: &clean::Type, renderer: &JsonRenderer<'_>) -> Self { type_: Box::new(t.into_json(renderer)), __pat_unstable_do_not_use: p.to_string(), }, + // FIXME(FRTs): implement + clean::Type::FieldOf(..) => todo!(), ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)), Infer => Type::Infer, RawPointer(mutability, type_) => Type::RawPointer { @@ -916,7 +918,7 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) }; vec![match kind { - AK::Deprecation { .. } => return Vec::new(), // Handled separately into Item::deprecation. + AK::Deprecated { .. } => return Vec::new(), // Handled separately into Item::deprecation. AK::DocComment { .. } => unreachable!("doc comments stripped out earlier"), AK::MacroExport { .. } => Attribute::MacroExport, @@ -1039,10 +1041,10 @@ fn name_value_attr( for attr_span in test_attrs { // FIXME: This is ugly, remove when `test_attrs` has been ported to new attribute API. if let Ok(snippet) = source_map.span_to_snippet(*attr_span) { - ret.push(Attribute::Other(format!("#[doc(test(attr({snippet})))"))); + ret.push(Attribute::Other(format!("#[doc(test(attr({snippet})))]"))); } } - toggle_attr(&mut ret, "no_crate_inject", no_crate_inject); + toggle_attr(&mut ret, "test(no_crate_inject)", no_crate_inject); return ret; } diff --git a/src/librustdoc/json/ids.rs b/src/librustdoc/json/ids.rs index 737148bad4e8..31043b8028f0 100644 --- a/src/librustdoc/json/ids.rs +++ b/src/librustdoc/json/ids.rs @@ -6,9 +6,9 @@ //! other phases think of as an "item". use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_span::{Symbol, sym}; +use rustc_hir::find_attr; +use rustc_span::Symbol; use rustdoc_json_types as types; use super::JsonRenderer; @@ -88,16 +88,11 @@ fn id_from_item_inner( // We need this workaround because primitive types' DefId actually refers to // their parent module, which isn't present in the output JSON items. So // instead, we directly get the primitive symbol - if matches!(self.tcx.def_kind(def_id), DefKind::Mod) - && let Some(prim) = self - .tcx - .get_attrs(def_id, sym::rustc_doc_primitive) - .find_map(|attr| attr.value_str()) - { - Some(prim) - } else { - self.tcx.opt_item_name(def_id) - } + find_attr!( + self.tcx, def_id, + RustcDocPrimitive(_, prim) => *prim + ) + .or_else(|| self.tcx.opt_item_name(def_id)) } }; diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index a201f661d9f7..7c8e7b7669cd 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -280,7 +280,8 @@ fn after_krate(self) -> Result<(), Error> { types::ExternalCrate { name: e.name(self.tcx).to_string(), html_root_url: match external_location { - ExternalLocation::Remote(s) => Some(s.clone()), + // FIXME: relative extern URLs are not resolved here + ExternalLocation::Remote { url, .. } => Some(url.clone()), _ => None, }, path: self diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 6ce3345fc607..55075001e0fe 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -1,5 +1,4 @@ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/" @@ -9,7 +8,6 @@ #![feature(box_patterns)] #![feature(file_buffered)] #![feature(formatting_options)] -#![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iter_order_by)] #![feature(rustc_private)] @@ -74,7 +72,6 @@ use rustc_errors::DiagCtxtHandle; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_hir::lints::DelayedLint; use rustc_interface::interface; use rustc_middle::ty::TyCtxt; use rustc_session::config::{ErrorOutputType, RustcOptGroup, make_crate_type_option}; @@ -910,23 +907,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { for owner_id in tcx.hir_crate_items(()).delayed_lint_items() { if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) { for lint in &delayed_lints.lints { - match lint { - DelayedLint::AttributeParsing(attribute_lint) => { - tcx.node_span_lint( - attribute_lint.lint_id.lint, - attribute_lint.id, - attribute_lint.span, - |diag| { - rustc_lint::decorate_attribute_lint( - tcx.sess, - Some(tcx), - &attribute_lint.kind, - diag, - ); - }, - ); - } - } + rustc_hir_analysis::emit_delayed_lint(lint, tcx); } } } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index a68e9dc87ae5..697b35ad8e85 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -15,7 +15,7 @@ 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}; +use rustc_hir::{Attribute, Mutability, Safety, find_attr}; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::{bug, span_bug, ty}; use rustc_resolve::rustdoc::pulldown_cmark::LinkType; @@ -125,9 +125,10 @@ fn disambiguator_suggestion(self) -> Suggestion { DefKind::Trait => "trait", DefKind::Union => "union", DefKind::Mod => "mod", - DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst => { - "const" - } + DefKind::Const { .. } + | DefKind::ConstParam + | DefKind::AssocConst { .. } + | DefKind::AnonConst => "const", DefKind::Static { .. } => "static", DefKind::Field => "field", DefKind::Variant | DefKind::Ctor(..) => "variant", @@ -393,7 +394,10 @@ fn resolve<'path>( if let Some(res) = self.resolve_path(path_str, ns, item_id, module_id) { return Ok(match res { Res::Def( - DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy | DefKind::Variant, + DefKind::AssocFn + | DefKind::AssocConst { .. } + | DefKind::AssocTy + | DefKind::Variant, def_id, ) => { vec![(Res::from_def_id(self.cx.tcx, self.cx.tcx.parent(def_id)), Some(def_id))] @@ -490,7 +494,7 @@ fn resolve_self_ty<'tcx>( let self_id = match tcx.def_kind(item_id) { def_kind @ (DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::AssocTy | DefKind::Variant | DefKind::Field) => { @@ -1109,7 +1113,7 @@ fn resolve_links(&mut self, item: &Item) { // Also resolve links in the note text of `#[deprecated]`. for attr in &item.attrs.other_attrs { - let Attribute::Parsed(AttributeKind::Deprecation { span: depr_span, deprecation }) = + let Attribute::Parsed(AttributeKind::Deprecated { span: depr_span, deprecation }) = attr else { continue; @@ -1127,14 +1131,8 @@ fn resolve_links(&mut self, item: &Item) { // inlined item. // let item_id = if let Some(inline_stmt_id) = item.inline_stmt_id - && tcx.get_all_attrs(inline_stmt_id).iter().any(|attr| { - matches!( - attr, - Attribute::Parsed(AttributeKind::Deprecation { - span: attr_span, .. - }) if attr_span == depr_span, - ) - }) { + && find_attr!(tcx, inline_stmt_id, Deprecated { span, ..} if span == depr_span) + { inline_stmt_id.to_def_id() } else { item.item_id.expect_def_id() @@ -1216,7 +1214,7 @@ fn validate_link(&self, original_did: DefId) -> bool { let tcx = self.cx.tcx; let def_kind = tcx.def_kind(original_did); let did = match def_kind { - DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant => { + DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::Variant => { // documented on their parent's page tcx.parent(original_did) } @@ -1404,7 +1402,13 @@ fn verify_disambiguator( // Disallow e.g. linking to enums with `struct@` debug!("saw kind {kind:?} with disambiguator {disambiguator:?}"); match (kind, disambiguator) { - | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const))) + | ( + DefKind::Const { .. } + | DefKind::ConstParam + | DefKind::AssocConst { .. } + | DefKind::AnonConst, + Some(Disambiguator::Kind(DefKind::Const { .. })), + ) // NOTE: this allows 'method' to mean both normal functions and associated functions // This can't cause ambiguity because both are in the same namespace. | (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn))) @@ -1727,7 +1731,7 @@ fn from_str(link: &str) -> Result, (String, Range Kind(DefKind::Trait), "union" => Kind(DefKind::Union), "module" | "mod" => Kind(DefKind::Mod), - "const" | "constant" => Kind(DefKind::Const), + "const" | "constant" => Kind(DefKind::Const { is_type_const: false }), "static" => Kind(DefKind::Static { mutability: Mutability::Not, nested: false, @@ -2128,11 +2132,11 @@ fn resolution_failure( | Field | Closure | AssocTy - | AssocConst + | AssocConst { .. } | AssocFn | Fn | Macro(_) - | Const + | Const { .. } | ConstParam | ExternCrate | Use diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index c2a69baf2989..b58daceed070 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -3,9 +3,9 @@ //! struct implements that trait. use rustc_data_structures::fx::FxHashSet; -use rustc_hir::Attribute; use rustc_hir::attrs::{AttributeKind, DocAttribute}; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE}; +use rustc_hir::{Attribute, find_attr}; use rustc_middle::ty; use tracing::debug; @@ -66,16 +66,10 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> for &impl_def_id in tcx.trait_impls_in_crate(LOCAL_CRATE) { let mut parent = Some(tcx.parent(impl_def_id)); while let Some(did) = parent { - attr_buf.extend(tcx.get_all_attrs(did).iter().filter_map(|attr| match attr { - Attribute::Parsed(AttributeKind::Doc(d)) if !d.cfg.is_empty() => { - // The only doc attributes we're interested into for trait impls are the - // `cfg`s for the `doc_cfg` feature. So we create a new empty `DocAttribute` - // and then only clone the actual `DocAttribute::cfg` field. - let mut new_attr = DocAttribute::default(); - new_attr.cfg = d.cfg.clone(); - Some(Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr)))) - } - _ => None, + attr_buf.extend(find_attr!(tcx, did, Doc(d) if !d.cfg.is_empty() => { + let mut new_attr = DocAttribute::default(); + new_attr.cfg = d.cfg.clone(); + Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr))) })); parent = tcx.opt_parent(did); } @@ -100,7 +94,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> // While the `impl` blocks themselves are only in `libcore`, the module with `doc` // attached is directly included in `libstd` as well. if did.is_local() { - for def_id in prim.impls(tcx).filter(|def_id| { + for def_id in prim.impls(tcx).filter(|&def_id| { // Avoid including impl blocks with filled-in generics. // https://github.com/rust-lang/rust/issues/94937 // diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index 5749b4ac081e..e94fd996947b 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -5,7 +5,7 @@ use rustc_data_structures::sync::Lock; use rustc_errors::emitter::Emitter; -use rustc_errors::translation::{Translator, to_fluent_args}; +use rustc_errors::formatting::format_diag_message; use rustc_errors::{Applicability, DiagCtxt, DiagInner}; use rustc_parse::{source_str_to_stream, unwrap_or_emit_fatal}; use rustc_resolve::rustdoc::source_span_for_markdown_range; @@ -35,8 +35,7 @@ fn check_rust_syntax( code_block: RustCodeBlock, ) { let buffer = Arc::new(Lock::new(Buffer::default())); - let translator = rustc_driver::default_translator(); - let emitter = BufferEmitter { buffer: Arc::clone(&buffer), translator }; + let emitter = BufferEmitter { buffer: Arc::clone(&buffer) }; let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); @@ -145,18 +144,13 @@ struct Buffer { struct BufferEmitter { buffer: Arc>, - translator: Translator, } impl Emitter for BufferEmitter { fn emit_diagnostic(&mut self, diag: DiagInner) { let mut buffer = self.buffer.borrow_mut(); - let fluent_args = to_fluent_args(diag.args.iter()); - let translated_main_message = self - .translator - .translate_message(&diag.messages[0].0, &fluent_args) - .unwrap_or_else(|e| panic!("{e}")); + let translated_main_message = format_diag_message(&diag.messages[0].0, &diag.args); buffer.messages.push(format!("error from rustc: {translated_main_message}")); if diag.is_error() { @@ -167,8 +161,4 @@ fn emit_diagnostic(&mut self, diag: DiagInner) { fn source_map(&self) -> Option<&SourceMap> { None } - - fn translator(&self) -> &Translator { - &self.translator - } } diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index 27065d7675bb..e1feccb13df8 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -2,7 +2,6 @@ use std::mem; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::find_attr; use rustc_middle::ty::TyCtxt; @@ -115,7 +114,7 @@ fn fold_item(&mut self, i: Item) -> Option { // If the macro has the `#[macro_export]` attribute, it means it's accessible at the // crate level so it should be handled differently. clean::MacroItem(..) => { - find_attr!(&i.attrs.other_attrs, AttributeKind::MacroExport { .. }) + find_attr!(&i.attrs.other_attrs, MacroExport { .. }) } _ => false, }; diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 1b3c96d82b41..cc78dec4eafa 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -5,8 +5,8 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::DiagCtxtHandle; +use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir}; use rustc_macros::{Decodable, Encodable}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index 4d31409afe82..9f6bf009a054 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -35,8 +35,8 @@ fn visit_inner_recur(&mut self, kind: &'a ItemKind) { | StaticItem(_) | ConstantItem(..) | TraitAliasItem(_) - | RequiredMethodItem(_) - | MethodItem(_, _) + | RequiredMethodItem(..) + | MethodItem(..) | StructFieldItem(_) | ForeignFunctionItem(..) | ForeignStaticItem(..) diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 0f4460bed35a..fd6ea21847c1 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -6,7 +6,7 @@ use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir as hir; -use rustc_hir::attrs::{AttributeKind, DocInline}; +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}; @@ -167,7 +167,7 @@ pub(crate) fn visit(mut self) -> Module<'tcx> { if !child.reexport_chain.is_empty() && let Res::Def(DefKind::Macro(_), def_id) = child.res && let Some(local_def_id) = def_id.as_local() - && find_attr!(self.cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }) + && find_attr!(self.cx.tcx, def_id, MacroExport { .. }) && inserted.insert(def_id) { let item = self.cx.tcx.hir_expect_item(local_def_id); @@ -249,7 +249,7 @@ fn maybe_inline_local( // Don't inline `doc(hidden)` imports so they can be stripped at a later stage. let is_no_inline = find_attr!( use_attrs, - AttributeKind::Doc(d) + Doc(d) if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::NoInline) ) || (document_hidden && use_attrs.iter().any(|attr| attr.is_doc_hidden())); @@ -385,7 +385,7 @@ fn add_to_current_mod( || match item.kind { hir::ItemKind::Impl(..) => true, hir::ItemKind::Macro(_, _, _) => { - find_attr!(self.cx.tcx.get_all_attrs(item.owner_id.def_id), AttributeKind::MacroExport{..}) + find_attr!(self.cx.tcx, item.owner_id.def_id, MacroExport{..}) } _ => false, } @@ -471,7 +471,7 @@ fn visit_item_inner( if is_pub && self.inside_public_path { let please_inline = find_attr!( attrs, - AttributeKind::Doc(d) + Doc(d) if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) ); let ident = match kind { @@ -502,8 +502,7 @@ fn visit_item_inner( let def_id = item.owner_id.to_def_id(); let is_macro_2_0 = !macro_def.macro_rules; - let nonexported = - !find_attr!(tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }); + let nonexported = !find_attr!(tcx, def_id, MacroExport { .. }); if is_macro_2_0 || nonexported || self.inlining { self.add_to_current_mod(item, renamed, import_id); diff --git a/src/llvm-project b/src/llvm-project index a306f6a8c534..41f177ed26a5 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit a306f6a8c53494d32c171c346c6809c97124c697 +Subproject commit 41f177ed26a5252a30ea6090a4da66ce0a96bf44 diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 9a59de4f844a..54fb833f40b6 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -682,6 +682,39 @@ pub enum ItemEnum { }, } +impl ItemEnum { + /// Returns the [`ItemKind`] of this item. + pub fn item_kind(&self) -> ItemKind { + match self { + ItemEnum::Module(_) => ItemKind::Module, + ItemEnum::ExternCrate { .. } => ItemKind::ExternCrate, + ItemEnum::Use(_) => ItemKind::Use, + ItemEnum::Union(_) => ItemKind::Union, + ItemEnum::Struct(_) => ItemKind::Struct, + ItemEnum::StructField(_) => ItemKind::StructField, + ItemEnum::Enum(_) => ItemKind::Enum, + ItemEnum::Variant(_) => ItemKind::Variant, + ItemEnum::Function(_) => ItemKind::Function, + ItemEnum::Trait(_) => ItemKind::Trait, + ItemEnum::TraitAlias(_) => ItemKind::TraitAlias, + ItemEnum::Impl(_) => ItemKind::Impl, + ItemEnum::TypeAlias(_) => ItemKind::TypeAlias, + ItemEnum::Constant { .. } => ItemKind::Constant, + ItemEnum::Static(_) => ItemKind::Static, + ItemEnum::ExternType => ItemKind::ExternType, + ItemEnum::Macro(_) => ItemKind::Macro, + ItemEnum::ProcMacro(pm) => match pm.kind { + MacroKind::Bang => ItemKind::Macro, + MacroKind::Attr => ItemKind::ProcAttribute, + MacroKind::Derive => ItemKind::ProcDerive, + }, + ItemEnum::Primitive(_) => ItemKind::Primitive, + ItemEnum::AssocConst { .. } => ItemKind::AssocConst, + ItemEnum::AssocType { .. } => ItemKind::AssocType, + } + } +} + /// A module declaration, e.g. `mod foo;` or `mod foo {}`. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Module { @@ -823,10 +856,10 @@ pub enum VariantKind { /// } /// ``` Struct { - /// The list of variants in the enum. - /// All of the corresponding [`Item`]s are of kind [`ItemEnum::Variant`]. + /// The list of named fields in the variant. + /// All of the corresponding [`Item`]s are of kind [`ItemEnum::StructField`]. fields: Vec, - /// Whether any variants have been removed from the result, due to being private or hidden. + /// Whether any fields have been removed from the result, due to being private or hidden. has_stripped_fields: bool, }, } diff --git a/src/stage0 b/src/stage0 index 26b97dc25cd7..40be3f7a66a8 100644 --- a/src/stage0 +++ b/src/stage0 @@ -13,544 +13,570 @@ nightly_branch=main # All changes below this comment will be overridden the next time the # tool is executed. -compiler_channel_manifest_hash=198f721db4d4f48112a8afadd11b63165d14038596d94402e36d16c7e528cda8 -compiler_git_commit_hash=9b1f8ff42d110b0ca138116745be921df5dc97e7 -compiler_date=2026-01-20 +compiler_channel_manifest_hash=18261f24c2dc26c5bfc6647e837c2e820a478f52d55aea7ff98eb24f43c750dd +compiler_git_commit_hash=ad726b5063362ec9897ef3d67452fc5606ee70fa +compiler_date=2026-03-05 compiler_version=beta -rustfmt_channel_manifest_hash=9136a5de582c2615de15a18c3fed0ba01bdfe0a1edc45467ced81b91ea6d2668 -rustfmt_git_commit_hash=5c49c4f7c8393c861b849441d27f5d40e0f1e33b -rustfmt_date=2026-01-21 +rustfmt_channel_manifest_hash=b9fb9e2c2cb55ccaccfc5d359d1de156896d3d4bda1b59b8e2e2fc9f987f6d29 +rustfmt_git_commit_hash=b90dc1e597db0bbc0cab0eccb39747b1a9d7e607 +rustfmt_date=2026-03-05 rustfmt_version=nightly -dist/2026-01-20/rustc-beta-aarch64-apple-darwin.tar.gz=5cc0e51c8a3130f63cd6d13f5c0cab6723ae9fbab0e0a6b95ba11be873eb1b3a -dist/2026-01-20/rustc-beta-aarch64-apple-darwin.tar.xz=c7c7fb474f9b1d2b5a7cff6d4c1503fc3e51624dec02b91c44b437a2fbc1c8fe -dist/2026-01-20/rustc-beta-aarch64-pc-windows-gnullvm.tar.gz=998f017140a97b52cc028410ebaa69dd9d3c40e66e666681cfbae2b567985bb0 -dist/2026-01-20/rustc-beta-aarch64-pc-windows-gnullvm.tar.xz=a73ea4d00dd8465721f3257420494ba24ffc620450949b768f375cb7420eb72d -dist/2026-01-20/rustc-beta-aarch64-pc-windows-msvc.tar.gz=8a9354bb17d8f8bce4a2313f24216b6c52d01f9b54d178b3f4fe51942ef23bff -dist/2026-01-20/rustc-beta-aarch64-pc-windows-msvc.tar.xz=de2e668147224840fa33f4dbf7d4bf97bdee18c29805dcc285e1ed6699f68235 -dist/2026-01-20/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=012e6afd73999fab248497b052c0b7a20c7570bc1a9082806bee1c9171c671a3 -dist/2026-01-20/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=662fdd1472e45b2ca15f1c3fbe6439d9d58f0b7edbfbc54f87a78644c273e52d -dist/2026-01-20/rustc-beta-aarch64-unknown-linux-musl.tar.gz=bba6f41e853989d3b7728d3f1169afa7137196cbc4696aeb43974b5b545b9e18 -dist/2026-01-20/rustc-beta-aarch64-unknown-linux-musl.tar.xz=70717b6bca2409266fdd670d2dcad076f000e6a0da0d6696b27e81c2ae0c132b -dist/2026-01-20/rustc-beta-aarch64-unknown-linux-ohos.tar.gz=54829f68a0fc79b84e80a0d47bc322954f09a59c0a06493bc56bb3ed1552b27e -dist/2026-01-20/rustc-beta-aarch64-unknown-linux-ohos.tar.xz=eb62be9276e425186c3adb0ce918a8d6de03f6f5b7f4e8fa6988369cd965450a -dist/2026-01-20/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=103e62ea960d1d89524c8383bd2dc9ef236eb8334e3813d861092305cf22ed78 -dist/2026-01-20/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=b78e4ce929bba3bd6222a0ff21dc98dbe91f37ab59c5b00b997c67307f7dbcab -dist/2026-01-20/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=76b0e61c0e412744651e847ca7388832b06173a9d78b92f6311fd3bbb9c56a13 -dist/2026-01-20/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=0fa5e9c25a4e5c404e6e2cb8c71212c80259d9cddf6462138a540fa83ecb15a6 -dist/2026-01-20/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=e86e88bdff894de048566f5e50f9fbedb7465a9cd3ca8e5f60e1a2be36c1bb8f -dist/2026-01-20/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=2415d59e1b95ac67cd7dd7d4df0eb24e1c4086134127b0664856fa10f8b45b39 -dist/2026-01-20/rustc-beta-i686-pc-windows-gnu.tar.gz=3ea0d68636eb5d46bbcc14fcd89201f6b1d3e3dc2b5b6aae62eab881e6a51888 -dist/2026-01-20/rustc-beta-i686-pc-windows-gnu.tar.xz=110b6c903c2e835dee00e699bcdded24152421e77ef595f1de59135a4562a88f -dist/2026-01-20/rustc-beta-i686-pc-windows-msvc.tar.gz=1a92b391333148cc6e4edfb405712fc91e7985d9eecfddadf57c1b028a9fd3f5 -dist/2026-01-20/rustc-beta-i686-pc-windows-msvc.tar.xz=92cac54e86536bdd2955e7612884c58a6112f701e923552259c5b18317f6fbcd -dist/2026-01-20/rustc-beta-i686-unknown-linux-gnu.tar.gz=da3501275f4583d4e0edca756eaaffe8844276a3479a47348fa41e6ec9f2bddf -dist/2026-01-20/rustc-beta-i686-unknown-linux-gnu.tar.xz=1373a0d9a5c1d9fa8c6e0001423187084e43f1670583a134497b0d9cb9172737 -dist/2026-01-20/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=215901dbf0a4281ec49ed87ab9a615e0576a19b29fa7678d6cd82e73a34af665 -dist/2026-01-20/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=df87f0d1b6db8fb4092ee2cd854fc60cbf61cfa648059f376bb438a51eb4400d -dist/2026-01-20/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=c1ee374b6daca40863eb3c4a6b5158d745d08f98a5fab103ca348efb7e23c93a -dist/2026-01-20/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=7ad8372e9b1f333050c57b9e645c19c45766babaef7ff654c1944415001c1eca -dist/2026-01-20/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=85803d3595fe9d52de6e10e415a8509e9c006dee044640b20ca1cb85adc42122 -dist/2026-01-20/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=4981ce9ab5bb0a8b8d8b6d41836b533193195e415669bf054e8cd30db4a7480b -dist/2026-01-20/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=4ccb74c4e2d2768f0774ed05feaa22d0dcb2613f6e5dfe8e4d10059c195fbd4f -dist/2026-01-20/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=c807173335735810b0fc53d368eb142c93014509e1bb834f23260fccc9b6caa4 -dist/2026-01-20/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=679d2047f602d36782709a34db884efb252887502035131bd6426c1035a10e99 -dist/2026-01-20/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=41c41dbfaf76abddf39633ac1c4fa3b228428ea4dcbd85c07961c6d4350b93fd -dist/2026-01-20/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=0b05419ba70cea7cb2f3171bceefa35844721c26e34ce36bd2a37ab437d0813e -dist/2026-01-20/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=77a7e18d4841114c555d6624beffa355ea1685ab74ea0974014252cd2ced8adb -dist/2026-01-20/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=af4a951fd83e6e22f902b8dde0ed7cdd3342f6d91dcf844488be3fb88e0edadf -dist/2026-01-20/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=30f471ac01b7ac6e6052c0b11fd1fa33dba0af2abdfc8eaf11fa5a48627d5c07 -dist/2026-01-20/rustc-beta-s390x-unknown-linux-gnu.tar.gz=788dd17f29c7594bb425ec9f2686453eeaeedf45c11388b714b1b6f0b35fb018 -dist/2026-01-20/rustc-beta-s390x-unknown-linux-gnu.tar.xz=2117b3e3ad7f252e0790500add59204038b9e4709710df7757ffe2dbc0d264bf -dist/2026-01-20/rustc-beta-sparcv9-sun-solaris.tar.gz=4ef69aa3d5ec31ce15ee7c38667aac9b1c101b948ee3095da23bde2fee75d3a4 -dist/2026-01-20/rustc-beta-sparcv9-sun-solaris.tar.xz=815f1800251b3bb79a681dfdcfc192971465c260ed7c6a57ee964b80962f2ab4 -dist/2026-01-20/rustc-beta-x86_64-apple-darwin.tar.gz=c2ec3532fa91de3fdb0e321db0505bb991349f6029ddcd460998b2141504a38d -dist/2026-01-20/rustc-beta-x86_64-apple-darwin.tar.xz=5c73b88d90f6ae44541d7f4c3dfcce184279f619f74bf3865147008d08ea7889 -dist/2026-01-20/rustc-beta-x86_64-pc-solaris.tar.gz=d278027e01143a16596be0efd73673f721f66618050687f344e98bbb9c248128 -dist/2026-01-20/rustc-beta-x86_64-pc-solaris.tar.xz=8bcc061f863c77b33638f66cc5c2288a3af75a3ce9ac85c9ff6ef2664916f572 -dist/2026-01-20/rustc-beta-x86_64-pc-windows-gnu.tar.gz=2458c5b4cf577e02dddd5bf41b1ef774f3cbb100ed6efbd290f44fdd2539dda0 -dist/2026-01-20/rustc-beta-x86_64-pc-windows-gnu.tar.xz=8bdbce81fb1a1ededf46e8c59e90213110f96c9bdeb3432a9440998968438173 -dist/2026-01-20/rustc-beta-x86_64-pc-windows-gnullvm.tar.gz=ea6132e6b43420787771edd7c1737b22e93e70c87234460053b9061d4404f763 -dist/2026-01-20/rustc-beta-x86_64-pc-windows-gnullvm.tar.xz=06ad990dc69585f8e465b28bf9974e009f846dcaaa0af651fb01b0f6b7d64eb9 -dist/2026-01-20/rustc-beta-x86_64-pc-windows-msvc.tar.gz=3e4700196332989cf177a182f545602e7731e16484b05ae1c30545cbb936d7b8 -dist/2026-01-20/rustc-beta-x86_64-pc-windows-msvc.tar.xz=7c381cfed5cf944beb19ba1349a779c88fbb4a007d2670fb2d5d63d8a2dbe976 -dist/2026-01-20/rustc-beta-x86_64-unknown-freebsd.tar.gz=a2448467346e77904fba417cf6758982711391b1eea6f576d438b886813d9281 -dist/2026-01-20/rustc-beta-x86_64-unknown-freebsd.tar.xz=9153cf38b57677a9f433a2cf70086a13f913445142692513a2b23155d4ae22c5 -dist/2026-01-20/rustc-beta-x86_64-unknown-illumos.tar.gz=b1118dde7d41c97409fc2febd068331c08d8bcca6d281078cfe0a4ce44789fa8 -dist/2026-01-20/rustc-beta-x86_64-unknown-illumos.tar.xz=7f4e7002f9cd612541a4809f907f6e18c0eb1765c621a0a944e87e28c5c6bb8a -dist/2026-01-20/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=cbb5bd4b16665096beb06875c8eab8fb1a0de0043e6d70057bfc826ec4f8894f -dist/2026-01-20/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=04382df840d1a3e24bf8c6f17fe7b39d2ca455204e161a51a0644be30a0c9176 -dist/2026-01-20/rustc-beta-x86_64-unknown-linux-musl.tar.gz=635629efe95735dd0e606e930b466170a40876c295903acdc308a8709976d1c3 -dist/2026-01-20/rustc-beta-x86_64-unknown-linux-musl.tar.xz=005a9e66acb005844eb48c616ad9fde6f830d45099cd68c56868a63bf85ca9a6 -dist/2026-01-20/rustc-beta-x86_64-unknown-netbsd.tar.gz=dbd411c130ef46a8d2618930906615358b77f2805d35cb5c898c63e5220bbaac -dist/2026-01-20/rustc-beta-x86_64-unknown-netbsd.tar.xz=d6de09699651dee3946be112bda37221caafb0f0679181255a512d9045bfd7d0 -dist/2026-01-20/rust-std-beta-aarch64-apple-darwin.tar.gz=cc56445cdf88e1e987991c654b65076132e687e1f81ae0ecde119316db7ee6f8 -dist/2026-01-20/rust-std-beta-aarch64-apple-darwin.tar.xz=157b72888a9c6b358152c5e7876c2a9a28dcc2bc637d24a9e80c2f7b3b2d7c73 -dist/2026-01-20/rust-std-beta-aarch64-apple-ios.tar.gz=337b5e932725b7e7323b3954cc2cbeaddd7060dec14cac5aefae3d139207e0ae -dist/2026-01-20/rust-std-beta-aarch64-apple-ios.tar.xz=941011b45dd518033aee4529fb96c3c18fba386beac6b80b0bde7c67f3e20cbd -dist/2026-01-20/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=56d61f46311c4e60b23337c83a60bf628769da2ae2f998aad704d8e0bdaf46f2 -dist/2026-01-20/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=28c1904b991c23789bb6a947842a01640e6c94826a895f7e251490d7e1245ecf -dist/2026-01-20/rust-std-beta-aarch64-apple-ios-sim.tar.gz=47ce08569d02aa034403416d9082c9c37b248a0c2c95d5e3bc494135686c61cf -dist/2026-01-20/rust-std-beta-aarch64-apple-ios-sim.tar.xz=6d8498fcbf8cf374dc0df7799263f498f9c9bd5696e56d1d405b8c9af1b26b92 -dist/2026-01-20/rust-std-beta-aarch64-linux-android.tar.gz=d192fdcad5fce22b0e33d7112e8e82846efc7df2955e45ed054e33af8f561b0a -dist/2026-01-20/rust-std-beta-aarch64-linux-android.tar.xz=d1b676986e7f14009468d4de953316594cec57454ab66e2c33442045bd786bca -dist/2026-01-20/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=b118206b25b341e54b6a1924fba1af128682d439a50922142eabd955492f1506 -dist/2026-01-20/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=b6942f69d9cf556692975ddd6e9c6492fc27775543681eda9bbf7b47f7d8e837 -dist/2026-01-20/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=fc1c722efb6c101abf8a9c19be977917f5255b528094a61d47488f2a106df18e -dist/2026-01-20/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=45f76965465de191ffd41391899db000183b3ffe7e09122071b66b53ce3157e5 -dist/2026-01-20/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=e18ae77c355b0f18dd50196cc066a821b349fd51444f60d2cd0513d645f860f6 -dist/2026-01-20/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=dd09b9a2b3123f93bb75d869cefae9629d774afa1ae836baadd4718fad38d03c -dist/2026-01-20/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=4e5cd07f6d08ee4508866a8746677c4078cb8ae3575fd0d6b360900601f8afd7 -dist/2026-01-20/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=d1431f472132af6df49700e7713f57127a9436c4997e02cace5fef111aee2677 -dist/2026-01-20/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=a7816e9fd0aa5ea9f8069e7bdb29d851197324de17145af343503d6519d09381 -dist/2026-01-20/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=4d0a144e7483d51c5a6c78a727ccc93e0bb13174bcb06746431804f558c130b3 -dist/2026-01-20/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=b6e562eb9e07dfe65e84b43fd1ff9886ac7b36952d97c6bcdb56232895797914 -dist/2026-01-20/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=b52b1e208ab363e1f2a2d8076afaa4db22d83b86ba0eb78c8caee322d270fe8a -dist/2026-01-20/rust-std-beta-aarch64-unknown-none.tar.gz=8d199014e8362b55551f4e05e25ab2a5a7df482ff0f69716dd4fb7fa86c24320 -dist/2026-01-20/rust-std-beta-aarch64-unknown-none.tar.xz=8fe7e72b6cc670c085411f93c821db270c3bee61e96961c199c6dce1ac81898e -dist/2026-01-20/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=aa1bf4114d684bd97f97b47f38279075670995db4ba545d5428cc8756664113d -dist/2026-01-20/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=54ef07430e12fab3107e90f4780a8d831626db8158f86e2ea4dce8d0a7a6f080 -dist/2026-01-20/rust-std-beta-aarch64-unknown-uefi.tar.gz=248c804b45350e176e7cf9d3fd9fc1111a5e92f28a709829474f4bc7f73a9868 -dist/2026-01-20/rust-std-beta-aarch64-unknown-uefi.tar.xz=4bc2a262d351cb47e91f075e2febbba413e3bbba38dde88f86c9ad7647d62b4e -dist/2026-01-20/rust-std-beta-arm-linux-androideabi.tar.gz=25fe2b01984a70d184f00692b437a670bc9765172171a04c1027e58d8eb41320 -dist/2026-01-20/rust-std-beta-arm-linux-androideabi.tar.xz=bdf81424c056768e5b2de3d8f47f0fa8c6961609bd771f57014b091751e06137 -dist/2026-01-20/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=043e50c2670339b87b0544b38fea2ae2d232bdd39c36f452ac2fce36347c57d1 -dist/2026-01-20/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=143f95f8a2d3d2bd8981de4d9d329a33c90d91b7496f9ba7df02f270f3df0155 -dist/2026-01-20/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=40c2c0205bde6a79ed5d04a3a14ee0f764dd8d18ccfdd85c90f20cb42c832c08 -dist/2026-01-20/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=3099ccc2867df16e9b1217dd04e81ba109443e80da32a70169ac7b262ebf95fe -dist/2026-01-20/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=424dc7f5da843c2d31e5b05be1ecebd284504905da218a33ddd200db09cca2f8 -dist/2026-01-20/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=062bfea00af6a21989bf65bff127ff3e8fd7a7fe3d219adddccf88ba0bb66662 -dist/2026-01-20/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=74b0625bec34ecba335939d2f43e3b5762d11671e67870c1bd02f3d48e540c80 -dist/2026-01-20/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=738a1803a385c41f95af4a0e43a8e5aa8d1aa368ab4a9253c4a1a26471e02779 -dist/2026-01-20/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=c4eeebec7e64ba5e4ec37e6e971b81acabf72f9183ba6133839048de208dbb62 -dist/2026-01-20/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=13953caa8c05ee02d3c4ae0e94e0a0e635cf3ba7afc4873f3f14485d39ae8797 -dist/2026-01-20/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=6d3a651bbe5abc175a94e725ea2f0e4c36608f42de4a66610f879d8ddd694af0 -dist/2026-01-20/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=01a8574f91e57e8327017566eb9be470473b45cfee3bf6a9793715421a4d0735 -dist/2026-01-20/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=6387160de6758466609c206ddd853be3cf376388f6cf17b616f2a5cfa587232a -dist/2026-01-20/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=bbb47df59bcc69584d8cd2915e2bb04b80585b495823e7f8494db1fa93bfcdc7 -dist/2026-01-20/rust-std-beta-armv7-linux-androideabi.tar.gz=44b233bc802b61a535ba1cdfec7e2019d16d7a4e5f004289d329aca394515bc6 -dist/2026-01-20/rust-std-beta-armv7-linux-androideabi.tar.xz=e21f2779120fd6f421562c9298594d4477b1cc50b12153dc486665055a45aeef -dist/2026-01-20/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=e5c97d03ed3abd6ddb966e9eec940a90f1d47fe9a30b32618f0e0a9ca12f59b6 -dist/2026-01-20/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=ce82bf2aba7b75c6dcb19a7eee70acc934a3b76e01a12048a0f008f971d5ca80 -dist/2026-01-20/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=f5446ab9d2cd61669a22a0f1ae4cba1af43e3c681bcb8f76c4c74ab02a6be5d9 -dist/2026-01-20/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=aaa788f52d1a35307c3483e18d5c2c942fd3bab5770557999d5f96eff65e9054 -dist/2026-01-20/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=f0d13f8c265bfd2bd262f465f4dbe3a5199a0c64d1e7f732698d4a6a3ce485ae -dist/2026-01-20/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=f63d8760019bb5147afe3f41b7bee32e77bf145bde7cbd66d967a80cf8732af5 -dist/2026-01-20/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=74025d9b500d5d42046ed5d5f61f8e5dfddaca6d4f8e3a70e54770a517206b74 -dist/2026-01-20/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=13434d08a93291ca7cdb393723c53550a8fe75edc20533bc6f08500cf700270d -dist/2026-01-20/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=0fe9c1366b0e98bd6539d16f912a6e05b8927282540d087471c64f69e3cf6b4b -dist/2026-01-20/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=2fd56632b57cb681ac6076c554c8666504bf995202e5075588f7c34ed8085807 -dist/2026-01-20/rust-std-beta-armv7a-none-eabi.tar.gz=278acce49b80ffb09a9782d16e0d6a720700c872c75113dd2bcdb85a3026845f -dist/2026-01-20/rust-std-beta-armv7a-none-eabi.tar.xz=e0e52abb27568171ecf80f45690febe6e396d0af5bc38d6e73e7377fabd69899 -dist/2026-01-20/rust-std-beta-armv7a-none-eabihf.tar.gz=a553da7e46e629be7e12329fe236c9abe473e1ccf1a06f6aaf2e0a0d5f3d691b -dist/2026-01-20/rust-std-beta-armv7a-none-eabihf.tar.xz=002fe75542e437e718845832e570982e785202505a8041fecb3123514b2f961b -dist/2026-01-20/rust-std-beta-armv7r-none-eabi.tar.gz=254b5d4747b7ff23620490fbcf66084c8ed25dc0e771eb90e670d87bc0c7f0b7 -dist/2026-01-20/rust-std-beta-armv7r-none-eabi.tar.xz=b6c86fbb8dc2583eca27a7b041fc4f7fe711c663a8cb46f155b468cdc6c7229c -dist/2026-01-20/rust-std-beta-armv7r-none-eabihf.tar.gz=ce2a9737eeda7f8982ecc41e720f9ed4b762288d8223735df5cca77ac2236c78 -dist/2026-01-20/rust-std-beta-armv7r-none-eabihf.tar.xz=acd2ab7fa3a80031880e4ce5061fb2c560ff083b729953225a179e5f8ed0061b -dist/2026-01-20/rust-std-beta-armv8r-none-eabihf.tar.gz=5ad87901a799ae80d3e91b5e17a657d329ae7bd34606cebc5c986a628480c4dc -dist/2026-01-20/rust-std-beta-armv8r-none-eabihf.tar.xz=fa83c2e59bc76031d342aaa57708c68dac145f6e658a645bca872d201e7fca9c -dist/2026-01-20/rust-std-beta-i586-unknown-linux-gnu.tar.gz=fcebb4149026d36215fbac5c30d7b135ceafc963878becc49f4393424230f087 -dist/2026-01-20/rust-std-beta-i586-unknown-linux-gnu.tar.xz=03698b5571e5e7be170ec4ba882158736b232d69ba8465c289827c5509569c9e -dist/2026-01-20/rust-std-beta-i586-unknown-linux-musl.tar.gz=b390bd31913d3b018166df661729e9ee552d46789b6c9ca2d3d5e21026a2195a -dist/2026-01-20/rust-std-beta-i586-unknown-linux-musl.tar.xz=d11de928cb1857eccd2125dabe724d70461d287d3a05e53d0c39c7a020a6b83b -dist/2026-01-20/rust-std-beta-i686-linux-android.tar.gz=8a54e4c05cf8e42c0633c32ff66b6a6d9704688076fb0ccfc3a6ec7eebbce329 -dist/2026-01-20/rust-std-beta-i686-linux-android.tar.xz=875d34834e32b35e76de275d38063be134de8672a692a9739f9abe6197ae25e2 -dist/2026-01-20/rust-std-beta-i686-pc-windows-gnu.tar.gz=38c84e49a2f8f8df4966b3092af8d4cf82a952c89d3bf57ee19a0eab428cd67e -dist/2026-01-20/rust-std-beta-i686-pc-windows-gnu.tar.xz=651da8be90cb2ca29bdbc96bd9fe56d1654fccc2f80eda79d9358159120405a4 -dist/2026-01-20/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=74bdff560b2c21c66e7ebc8e028f05260a9f4d542893f843a49cc0a5904e2e92 -dist/2026-01-20/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=34dbf2186ee00a37482cc2c5540631d0fe879167977685fe1f5f9687b4a13e1a -dist/2026-01-20/rust-std-beta-i686-pc-windows-msvc.tar.gz=ef96af22d000a26b7ccf071c2a57910c761a95d4ba7490eb5a5a6422851ef4bb -dist/2026-01-20/rust-std-beta-i686-pc-windows-msvc.tar.xz=6680b37f26878c91012b20297c8ad3ff318cb1800a7434f3392fb606cd78227a -dist/2026-01-20/rust-std-beta-i686-unknown-freebsd.tar.gz=491599ff71f37c43ffc190f6f48833ac97e7abf2b23399c8f7a7395dfb54cc9e -dist/2026-01-20/rust-std-beta-i686-unknown-freebsd.tar.xz=46a91f11cd54ee4b1cad3b1b93a3b865404b6c87cd077fd9e4b89d84bc5ba836 -dist/2026-01-20/rust-std-beta-i686-unknown-linux-gnu.tar.gz=a4e8882af3628b45a7e09909cdda626df3b18fcac88ee2d295bd16f6aa0b09d6 -dist/2026-01-20/rust-std-beta-i686-unknown-linux-gnu.tar.xz=0f0899389080913cbac8dd531ca3d3eb8503653a551b71f59d238015e025dfe7 -dist/2026-01-20/rust-std-beta-i686-unknown-linux-musl.tar.gz=75e9760f5b416021f8f82f8a20283c6307584df12046680a2981ed306cfcb542 -dist/2026-01-20/rust-std-beta-i686-unknown-linux-musl.tar.xz=b51312fd213eaa7e0f8e374b2421c61822e8912734fbb228f97b045d27121e6d -dist/2026-01-20/rust-std-beta-i686-unknown-uefi.tar.gz=b643b865409e819267c925fe69c84914d71eb634d17591edad1738dabaed9c2a -dist/2026-01-20/rust-std-beta-i686-unknown-uefi.tar.xz=03a9a056d82ef3aeca9ff64e2d3ab5f0bdc0f8ff496393beb63fefbc5e633e84 -dist/2026-01-20/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=3f770cac4814cf73bd31aba5c59005cf60aa4f5a40da64d037c9a7235e248291 -dist/2026-01-20/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=fa74c6b92a7e61e8f2a33e2c3a2163bfe91273664f1a59e435c6748fe16cb43f -dist/2026-01-20/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=96cfb6c20488f53791be313e70e147f3257353f39588e7949c487b69fbd0c937 -dist/2026-01-20/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=7c16dcd6685ef79d8b4e84b2bb06bedcec67498e3112a1322c663118ef49d02d -dist/2026-01-20/rust-std-beta-loongarch64-unknown-none.tar.gz=2bc1dd28e95012670733527d59b391bd578218f38aebcb6990e629e8130d10c8 -dist/2026-01-20/rust-std-beta-loongarch64-unknown-none.tar.xz=d65dc641b81e561ab30823c99cdee32615b9ca56d0a6a75450f44eb25ee66ac6 -dist/2026-01-20/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=04fda933fde52c3bb35b52dc38fe973b30d2e1a2369d1d500d20781e7014b65a -dist/2026-01-20/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=d2ce8dbade8c3a7c8ad9fb697d6f4b8551845263e8e9993772679c9a2e8334ff -dist/2026-01-20/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=819464b843b4218f3e77613094615701e3b6b31fdff1ef8781c98d482f7bb6a7 -dist/2026-01-20/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=fa5dca88bcec7f44f8ac52f207ba8029e167de23d377e03ba7f60e8e2211b2c8 -dist/2026-01-20/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=bd5667a613dcb10a5d7d86f63123acd967e8572c16c0d39969dfadfc8c7182ca -dist/2026-01-20/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=4b1092fcd8481a8e611b7f621348ae09f22eec3b50d1ca1aefdcd8c7d22a7d5c -dist/2026-01-20/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=3a9f61f1da7d463ee4a06bda52dc534b8550beb09c82227c21ff02b0e2b94b44 -dist/2026-01-20/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=cc2c6a28f7b5182675c18f1cc87ba87a215eb91bfce45726970a23554c98ce25 -dist/2026-01-20/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=14b80b314e8778a057c45164ca13f5c66a621a2f882143fd3bf21f2804a7547e -dist/2026-01-20/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=ad7082205aeff5014dfb7923bbf313cbfa8bcd97da098be6f267a4c45c0d53a1 -dist/2026-01-20/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=eaad6ab10d812cf3e5eec9b829614d011f9f9afc171c9e9226660ad54e40fb2e -dist/2026-01-20/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=6bb3bf669bf53eb5b13e9387c057b33bff4e6ed63af92e0a611c3813032ef3dd -dist/2026-01-20/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=2520f4f0569554108302f174dd1586752f6e987956e3e9a451ead4174a7a534e -dist/2026-01-20/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=935a00898ae51312b31a74f626f54901f1ea20a7ad34c46b0afe52e73030336d -dist/2026-01-20/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=704b2ffb62e06b81ba554eec92ecf764660cf34afa2a6c24baeb352da70505b9 -dist/2026-01-20/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=f673b92435126d88ab3282b25e20738aedb997db4c4a5ea74d49b8f4498c45e9 -dist/2026-01-20/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=b8cde078357091c89532baca3d1703db779a762e32b2f0dd10a2be6b10fabae1 -dist/2026-01-20/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=fc5ca415261fbaa65ffde2641bab65473201aa966d3bf99f11030af32f79685d -dist/2026-01-20/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=636f2ba1ba6b443a76d7a03561a001a874e10559cfc0d2941a003eb6b2eab810 -dist/2026-01-20/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=5cbe945f2db55cdf1b06e0424f342aa509bb0689a131a309812da45a33e6b56b -dist/2026-01-20/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=89dbfe19f8841cd04d59b8cfed9475df1accc991d60c0f14f66ec78341182abf -dist/2026-01-20/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=975bf46c3ac09ff68d6f2f3ac151c173a626e730243d52834de8c2304ffec18f -dist/2026-01-20/rust-std-beta-riscv64a23-unknown-linux-gnu.tar.gz=22e2d02b26cef510ad3c9dbe3d90bc37e024a3845e6a6310dad9a550910823e2 -dist/2026-01-20/rust-std-beta-riscv64a23-unknown-linux-gnu.tar.xz=9c3f58c9bebf264afb837cba63a09ae77ac5ca22f4063d290eca7e79c27253f9 -dist/2026-01-20/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=c8151863450d144882557691a8903912605f713fc9d1423d71270aff7e0cfa9d -dist/2026-01-20/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=c896fc03d5df106d1ffd92a9d8eefbe68ded5298ef7802816460a184da99ecf7 -dist/2026-01-20/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=753981a75540a01945ed929105f5f484e58e2c2e16d8e1a00217fd1cc1fbfc2b -dist/2026-01-20/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=d6186cd8f829c57357c1d494e11d0a1ddf58d10f7cc0fc063eca63099bab832b -dist/2026-01-20/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=f89ffb6abceef381d0305f35b4366349d7a1739ede655071b105511b602a1f8e -dist/2026-01-20/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=02a077f1aca2d02ab9d14531ea2c3728d7e4ed6551b065aaea0a7831f9d5a7d4 -dist/2026-01-20/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=88e3800176211ee1f149a7073caf1b9c81fc51e77b8f2f20fd0fed19382cddc1 -dist/2026-01-20/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=3824e9b78247a6213456b015dc7158702e05adda4b7147a6336a106dc94dc740 -dist/2026-01-20/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=95456e51cb0c764293879722c4a54d89c5fe362f7fd5aad886591df807fc95f7 -dist/2026-01-20/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=9b448fc6cee52ec53baca1bd5f93387e2f94f8f90d27172f50b10173e9e5b891 -dist/2026-01-20/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=407517411ae7ba6c7914bb64ed326e04e269bb5a8dfe0f832cc3176d04f7198d -dist/2026-01-20/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=b026f85cef121272c1e5cfbb289a65c398c463b9c9f3b03fe8691bc58b5778f8 -dist/2026-01-20/rust-std-beta-sparcv9-sun-solaris.tar.gz=c768d7d28d68c0e0b38d9e20abfe150e0bf883b5033f59d82b57daf63b7e5aa8 -dist/2026-01-20/rust-std-beta-sparcv9-sun-solaris.tar.xz=084fcfaa613aad7ee636c51d17c50455c6561b1287feef061e92d3acc3ee2be4 -dist/2026-01-20/rust-std-beta-thumbv6m-none-eabi.tar.gz=e14071a8e9b46f2d9a4f1e07c44bb28d884dfa1764d15fb7a2b255ac1aece1d7 -dist/2026-01-20/rust-std-beta-thumbv6m-none-eabi.tar.xz=d4ed2c2cab6e1522a8b571151da07a52350e795b5cb40a25409de49ea93fbd82 -dist/2026-01-20/rust-std-beta-thumbv7em-none-eabi.tar.gz=b8669b9842251cc2192a57471968765b431ebaa8753a1e6e18296e9cd6a157c8 -dist/2026-01-20/rust-std-beta-thumbv7em-none-eabi.tar.xz=dad30dafe030d3527762152d3df1b4e32d752341c8a53a7bb0946c818b85cfea -dist/2026-01-20/rust-std-beta-thumbv7em-none-eabihf.tar.gz=758a607c2f1d4000e6f70ef504da421d3177b810f42c0869d11063a6ffe31fbe -dist/2026-01-20/rust-std-beta-thumbv7em-none-eabihf.tar.xz=354853d1edf7546feef1de3256608d1c1dddb41774063b4356fa62de5bbefbee -dist/2026-01-20/rust-std-beta-thumbv7m-none-eabi.tar.gz=080c85f6510a78226c9f538a9735f4c878a2e1ae5ecc37e96dc68a618ed0c3c0 -dist/2026-01-20/rust-std-beta-thumbv7m-none-eabi.tar.xz=840ee359261a182053592c27f6428593bb5c20ff9c9574a09613f1c75fb9a2cf -dist/2026-01-20/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=14569501fcc481b5453e912bc5ed6f3b106c2d3f5068582beedc120d488c720a -dist/2026-01-20/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=50fb6c749858767db6e1bfe52205bf7761c2d287c3b8e2f882137c5096912745 -dist/2026-01-20/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=05d86630c938e3902d339e9cee306a97e459de5f4588aeac2075c5686f79cd35 -dist/2026-01-20/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=1ff3979060d0b74ef841148ad0b3c124989a49b97fbc2cec01f0f342a758f610 -dist/2026-01-20/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=6031caa8a04bbc2cbfa2bad982743dfba1d74215b21fb799c5d5ea21d598aebe -dist/2026-01-20/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=4ab93e3d8b24dee229b76cecfd17acf37a785eceeb6f18b9b0131b18ee879dff -dist/2026-01-20/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=02f3083cedee8072f7028de80f22a5ae769af34163ef60736012d262b2d971f2 -dist/2026-01-20/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=87cb4cf2256f8027a05543182431f8fc95f1fa51c10faefedb45de5eaccee3d2 -dist/2026-01-20/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=900474351bc843213c48e3d6a9e6de76555184b3993853ed547115e85f003545 -dist/2026-01-20/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=4cce3bd9620f6f895ea6fb9a26008ea6c482680059982a39ffd77c20bb803150 -dist/2026-01-20/rust-std-beta-wasm32-unknown-emscripten.tar.gz=fd050397dc018d910262d8269b55eae0f78582094b7c7eb7b69891f25768f60c -dist/2026-01-20/rust-std-beta-wasm32-unknown-emscripten.tar.xz=665e65eed6dd420566b971104b649521468df5eef5510b1a7fa2c5414f5dab94 -dist/2026-01-20/rust-std-beta-wasm32-unknown-unknown.tar.gz=2588ec7952e575d48d2584f7697c782b4daa61c35a54919d434552034c0f494a -dist/2026-01-20/rust-std-beta-wasm32-unknown-unknown.tar.xz=42718adabbe3effd1e89c08b446e8a18851d13ec7a50e426898402fbba00204c -dist/2026-01-20/rust-std-beta-wasm32-wasip1.tar.gz=9659b9e17e7fb0ecb14885dc404a47f17a35612490fcf834040050876c4a87b0 -dist/2026-01-20/rust-std-beta-wasm32-wasip1.tar.xz=c81860bb8271c0d6fb2df38b1b4a0ba006b47af9aab9c6fd88610780d708ff79 -dist/2026-01-20/rust-std-beta-wasm32-wasip1-threads.tar.gz=138cfe4a6414bcac1a4fed1713840f245fe57ab732e86b46ea66f89b38814837 -dist/2026-01-20/rust-std-beta-wasm32-wasip1-threads.tar.xz=b3b7ff4265e0d22aaea372e8355265d30f38fc73ab4e491eb9e36e261e107e5d -dist/2026-01-20/rust-std-beta-wasm32-wasip2.tar.gz=2b0ed2d38970294665f8c5515f2b23b8c447d19159c2317d456efe723ccdc282 -dist/2026-01-20/rust-std-beta-wasm32-wasip2.tar.xz=eb208d4ff325e675a5396acca80ac4594f1d8487136b044cb063140c38c9f900 -dist/2026-01-20/rust-std-beta-wasm32v1-none.tar.gz=2fc3a05f25b26e82b7589eba95b06f60923d83dc6d75566ee64967dd27392be0 -dist/2026-01-20/rust-std-beta-wasm32v1-none.tar.xz=733cfb74da7faae63b7d471d069c18e35e6d3ebd49bb7af929aeddc16a6bf41c -dist/2026-01-20/rust-std-beta-x86_64-apple-darwin.tar.gz=77a9a5614f59a026cbd265ad97d7acda3ecfbc441e550f82146e4eecae43c0d5 -dist/2026-01-20/rust-std-beta-x86_64-apple-darwin.tar.xz=8fb75751c0494fb331cfcd40de32877fb4eb3419d31867296d0c4c9b65884764 -dist/2026-01-20/rust-std-beta-x86_64-apple-ios.tar.gz=db33e16445c89a739ef351699dad17ff7f0317c299ffe3a45dad681ef023b3fc -dist/2026-01-20/rust-std-beta-x86_64-apple-ios.tar.xz=55377880102139275bcc523f0936e47a88486887f0d66ecfe0b90bdb4ecbd2e3 -dist/2026-01-20/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=ecb3e64fe4b05426446a7cf49d203c82501bdf4222724189c2e0718b2392360e -dist/2026-01-20/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=e39ef0d40e8beccb2704ee197b2d3986314aa0ec56dff73c284cc8920518c4dc -dist/2026-01-20/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=e8fb4aeb988c62140928da488091c73e96181b2c87af9716e80a3eb8fb841448 -dist/2026-01-20/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=7f42f9dd7a47b784186d7ede3643185e92a1d85932e6238668097efa7f45cfd0 -dist/2026-01-20/rust-std-beta-x86_64-linux-android.tar.gz=098e55a9e7ed0dec4235682cd70836cf5e1b327de7dfcf8e18c49faf3d3e7b56 -dist/2026-01-20/rust-std-beta-x86_64-linux-android.tar.xz=1e8ec49d649cdcfb9ee49740639f1f123d3c9d8b722553f104858fccae0e6d1d -dist/2026-01-20/rust-std-beta-x86_64-pc-solaris.tar.gz=b21ec57c249842b9407ffeb67e0732ed69991470b2d6e726adc3d3b06ab79705 -dist/2026-01-20/rust-std-beta-x86_64-pc-solaris.tar.xz=da37239ac84b376190f99bc7c103f315a153b33c22393a453a80e2f0c3867c55 -dist/2026-01-20/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=63dda15c97c519e336382ad12e02f8d59b19310ca87be4bfccd668616ad839e9 -dist/2026-01-20/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=4a3fcde123276fe18e70fde97c04cd5e5083cd2079911a4be9f203fc079820cb -dist/2026-01-20/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=124d9b525f103c033f4f3178fbef407ffe54a1ccfa2f93c5ca29923717bcb10e -dist/2026-01-20/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=7eb9ec5f20575d9a1aa3d252b3c92a077c8edb845cb16eb8df127cffea5c12f1 -dist/2026-01-20/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=72944c39dbb92c4d08d962cc0de6aa70e4c2519d457796db9d81ce26850690f4 -dist/2026-01-20/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=4fd4289e64c6a0f26de760a6dfe42065dcba2b7c1a68b3ce21080d89f21cc439 -dist/2026-01-20/rust-std-beta-x86_64-unknown-freebsd.tar.gz=175fa2f4353203445f3accfc9a3958f94490b69ad3b3dbde4fa706c95dfb4208 -dist/2026-01-20/rust-std-beta-x86_64-unknown-freebsd.tar.xz=8baa09381fa21da8b0d5c24212696975ca83f7bfb3ee29bd64a8f2c47bda9bfe -dist/2026-01-20/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=81fe291afe4619e75e3d7fc509e5dd6dee7e60e2fbb35410da13ddadcc6ae4f2 -dist/2026-01-20/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=498874af8343cfe6bc588f0bf6a90c393586c9d683feeab34dcf5a2dadf2cdeb -dist/2026-01-20/rust-std-beta-x86_64-unknown-illumos.tar.gz=69de9a4ceeb07503f5d8a9e0a263a25b005657ce42ca0a83c52579869f2883d5 -dist/2026-01-20/rust-std-beta-x86_64-unknown-illumos.tar.xz=a218bab24c2b85fbc11ca846da6090f6fb0cee74d86ff4a540a98297379d4588 -dist/2026-01-20/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=58e74c1f7459b13130713007dfc70c7444f0627f016154dbd564a84016c1df72 -dist/2026-01-20/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=ae2b69792ddff5f60b2c0060d59b2b17491769f6fcf6abd44b64435c5920e7cc -dist/2026-01-20/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=8efeaae9589fd41ef558bb6587f1619c356c2da2ab2a7ae63b31961cc2a7c2be -dist/2026-01-20/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=451dcc4dbdf2b9e9b08165745ceb7d9d5c2c93f65288023b24d818d567a3d827 -dist/2026-01-20/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=cbf91ce153ae8a9108e0396beab208cf1c263351fe4638eb1f4ff90d5f00e018 -dist/2026-01-20/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=b5392dae5fef54f9418318ecedab57404f03aef618231a6431162078a283aa34 -dist/2026-01-20/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=b071ea7128235d11560e6e58666311f96940454070a641bca27709bfbe608bb4 -dist/2026-01-20/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=8a85c937fed532e558e5d0919913071a5873db08fe3f8c7121c708df2f2c10ae -dist/2026-01-20/rust-std-beta-x86_64-unknown-netbsd.tar.gz=e77192be66c874d102d68848bc750007328a4034047c1379138a6e12e5cb5c7d -dist/2026-01-20/rust-std-beta-x86_64-unknown-netbsd.tar.xz=ce899ed2702c32b813e5f754cc0af85ce0a7bd08770440291de758b30457abc9 -dist/2026-01-20/rust-std-beta-x86_64-unknown-none.tar.gz=2b5a6e82c398ddd10cbfc28714ee82778126906a102af59f6689eeb485cfa59c -dist/2026-01-20/rust-std-beta-x86_64-unknown-none.tar.xz=75f5a1f61f3fbd5f59b8da5b3d68612d879356a3a76a3325e115cda69bbec8ba -dist/2026-01-20/rust-std-beta-x86_64-unknown-redox.tar.gz=c42257ac8de158a1baeae3a0ccc786f7ce44e2119e222a0ae145c8d087d25ff5 -dist/2026-01-20/rust-std-beta-x86_64-unknown-redox.tar.xz=93bee91a6cf012da2a244bb77c2ecf6b5079d4139510e0cadf827e3f1d0f949f -dist/2026-01-20/rust-std-beta-x86_64-unknown-uefi.tar.gz=bb7721b8cece5bc774bc24cd75126e9be5625a1fbb46f7df343069b8f9941e0c -dist/2026-01-20/rust-std-beta-x86_64-unknown-uefi.tar.xz=fb5b68cdaab325cb6c7ac91803b694f3c13df87af9069426a26bb33a0029e378 -dist/2026-01-20/cargo-beta-aarch64-apple-darwin.tar.gz=68018a555a01fd5fb0a614668c64502d512ec21b8bad10186928d6ee351743a2 -dist/2026-01-20/cargo-beta-aarch64-apple-darwin.tar.xz=61ccbaeb46baa8a5cb984f6bb389c2b2e9e56784ae5f94500cd85c876e84e9a6 -dist/2026-01-20/cargo-beta-aarch64-pc-windows-gnullvm.tar.gz=6c94f4be14fc692f0cf30abdbaf6500aedf2de4bb30fd6cea3c6df004cfab3e5 -dist/2026-01-20/cargo-beta-aarch64-pc-windows-gnullvm.tar.xz=e5b104152c93682c2c04d0735547611cb9ddf0ca97655da9181a72186bf1e148 -dist/2026-01-20/cargo-beta-aarch64-pc-windows-msvc.tar.gz=6e362f5864b79406982592c497e8e8706cc543452d12856baf440df6d8e1c2b4 -dist/2026-01-20/cargo-beta-aarch64-pc-windows-msvc.tar.xz=80634e4cc4b3a90660be35e50b358c924a44d28feae7350a7cd4540aa58cb759 -dist/2026-01-20/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=0826dccf72f8a52127ce935fef0b260c41a1e6e677c0e0b35c3180395efa257e -dist/2026-01-20/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=bc1cea64723b5ff68c3cb7a7848a1e3d058d60d4dd01b68e2bb0dc9f7e377883 -dist/2026-01-20/cargo-beta-aarch64-unknown-linux-musl.tar.gz=e959fc9b4dced7f1fc7ddb5be1f59cbaa80e67fba4c8b811e66c980920f50a89 -dist/2026-01-20/cargo-beta-aarch64-unknown-linux-musl.tar.xz=7f44f2e9696333dcc1bdd9ad27761025573a2a29f16ece6e952136278bf0c0c7 -dist/2026-01-20/cargo-beta-aarch64-unknown-linux-ohos.tar.gz=39100832eb910130177359850f1dbc63c7006f763313313d046132b9b408ec2b -dist/2026-01-20/cargo-beta-aarch64-unknown-linux-ohos.tar.xz=4da74538df99ffc89f5a4e4b920f8b91b63809814032d9be4747d856c2481391 -dist/2026-01-20/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=1640df82cffe0e12b78ba50c778441d468da647508275a1624d9ddee42bf80a4 -dist/2026-01-20/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=f7e5ae4dffa52e89d5cf0983255e2c4e12b482c1b9bd8f9121a27f9d859291ae -dist/2026-01-20/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=76fd2a8a2a0cf4583c16c0f0d58c19651a259d947b5f570e934c9ea8a288ec95 -dist/2026-01-20/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=159491a9f53719b0cef45c38483632fb3ea3cb1d8edc3a2069eb6bdfc74af59a -dist/2026-01-20/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=debd3631a510f713276a2ff7feb163414e1c5617df75c7b8fb012568f2bf0f37 -dist/2026-01-20/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=d1ee0a450c4d2f34d76a0b4f904e344f68de0102777aca20c2e1c4bd6249006d -dist/2026-01-20/cargo-beta-i686-pc-windows-gnu.tar.gz=cd339637c333db74c684597d9c817aa80bde1b3a2ca436678b16cf3b4954a8c1 -dist/2026-01-20/cargo-beta-i686-pc-windows-gnu.tar.xz=aad0ae08d8da64076f20479de9efe6cb4dac0ef3b35940b86b13d1035c033dfb -dist/2026-01-20/cargo-beta-i686-pc-windows-msvc.tar.gz=2b24c735d2cbc73d9cdda6877e15e443a975acae78d9e7a6756464b2cca30b0e -dist/2026-01-20/cargo-beta-i686-pc-windows-msvc.tar.xz=8e5ae923bd2627354783c5ea036c31bac672c5ebabe01cd09ae591ae982d25ba -dist/2026-01-20/cargo-beta-i686-unknown-linux-gnu.tar.gz=3d4b141d6a79db23e5fca439c2dcd60b578404a0487fa8444d2310b7d48962d8 -dist/2026-01-20/cargo-beta-i686-unknown-linux-gnu.tar.xz=98ee2d83f2d9a2a8bad7706f9dd3fc94b4292c5ebe68abf1daa295e5f762617f -dist/2026-01-20/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=5cf7b73a394c70935b3a3734db36e59b8118ebe576e5a8115c8cb134ec08a0a0 -dist/2026-01-20/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=f02d05725f2d0f50588ab2c6a07794225787f25b914498d68a863b0572e3a35e -dist/2026-01-20/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=6a040c1db1261aa353fa0b76b604d390ba55b128a8e228d5fa6f3aa7a2f9d649 -dist/2026-01-20/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=de23eb854b648a895993402f9b8331819ac407047c52e4ed688e04377d31ac52 -dist/2026-01-20/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=b228b304546200fbc1336d2d4411a9a6b5b87a5e345c880d879a8f79ee491a80 -dist/2026-01-20/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=5b5c759ffff62c12b1629d2e7b651a1155ce88e7588be2a2d0b4d9ca3d0a726a -dist/2026-01-20/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=8436db28b1d269969e6607c8838ccb2147b1bd0c7a284826c11cd4ce35b89847 -dist/2026-01-20/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=9c585cf6ac5893c156e1b9d747656d9575611f75bb8032267a898b34845069d9 -dist/2026-01-20/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=e5b2ec927cd671d52e0506d4df7185750fdb564692a14ea49d8c23dc53a534b2 -dist/2026-01-20/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=69a1b43bb32d784719903694eb7ede8301b86ecea3eebfc58844c41cd393f84f -dist/2026-01-20/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=95adea6787309366ec745933daacdc1a821f981bdacdd648697eb9dbb6a5a8b9 -dist/2026-01-20/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=3d67b80fdc9f47822082664b7f5dc1cc0b456db00ef479c8744f660de7873902 -dist/2026-01-20/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=cc1b3ad4ca1f4bf0ef3a7b5e5d58985d40fad6142099776bfea8c6e81fece140 -dist/2026-01-20/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=9f0a936690f17b320c84a70b3135526bfc7aa5d36d5cfb97547c5fc53b657c04 -dist/2026-01-20/cargo-beta-s390x-unknown-linux-gnu.tar.gz=d44fde497ca4c2a750453f75a0eec1200ac928ddd8078cc233c53fe2b066b19a -dist/2026-01-20/cargo-beta-s390x-unknown-linux-gnu.tar.xz=fa6d932e55b8aa814e428b973a62fb7db6a603e728b114f7e2165e90721ec05e -dist/2026-01-20/cargo-beta-sparcv9-sun-solaris.tar.gz=ed500af7a213c1892eceb574cf06b1f3c621355f5084f998cb501b5f87345e07 -dist/2026-01-20/cargo-beta-sparcv9-sun-solaris.tar.xz=29e4a58324e846421f10e33c7aaeb9bdf6bef646c59aade71468f29c4b16b789 -dist/2026-01-20/cargo-beta-x86_64-apple-darwin.tar.gz=8ed894b81f29b0a4d27333d0ed5e68de0e5f98163b5ce7a3e71c66e3b1cc2f12 -dist/2026-01-20/cargo-beta-x86_64-apple-darwin.tar.xz=52b7f245f126a55054aca75f61e44a29c5adbb80aa4524287d414703de8e8332 -dist/2026-01-20/cargo-beta-x86_64-pc-solaris.tar.gz=7bd13a3901013768bc18fa4a317c5998478a4ba2fcd865329fc2d29a39e5fe61 -dist/2026-01-20/cargo-beta-x86_64-pc-solaris.tar.xz=791386564dad9e76d2904311a5bbbd0383df8a8557d59e2f110a745d332eba75 -dist/2026-01-20/cargo-beta-x86_64-pc-windows-gnu.tar.gz=a4adf7a0298ffed28396c2863d8ed61d37255602295a34c5c7570c03221a85e2 -dist/2026-01-20/cargo-beta-x86_64-pc-windows-gnu.tar.xz=2e274955324ff915c569e9069e9a88ce6984803d3dc2d86d8f46b634b2ff0e42 -dist/2026-01-20/cargo-beta-x86_64-pc-windows-gnullvm.tar.gz=dfc4395f5415555772b1ac9de9b9c7ea27154ae74382a323c4878f5bd0775bcb -dist/2026-01-20/cargo-beta-x86_64-pc-windows-gnullvm.tar.xz=3b512d1cc1550612bf321a645752b46f6c13ac42ef2dfd010d44f678bacb2e44 -dist/2026-01-20/cargo-beta-x86_64-pc-windows-msvc.tar.gz=6c438b7e06c492118c41a990a16c7e369781e0d603482ac4af43c2e416edd52c -dist/2026-01-20/cargo-beta-x86_64-pc-windows-msvc.tar.xz=9c2c2d29221a02ed062d59bce2e66679c89e30a1b0fe06f66ffdd8d5aa6978f9 -dist/2026-01-20/cargo-beta-x86_64-unknown-freebsd.tar.gz=900cdde5911be20bff4ddb571b48584c2bc5d2aa86ef15f4c285a3f84a683e7c -dist/2026-01-20/cargo-beta-x86_64-unknown-freebsd.tar.xz=6b3143539a6d938a236364eb8c6b5a042f154daaa120e2ae00e7610c60e5e0a7 -dist/2026-01-20/cargo-beta-x86_64-unknown-illumos.tar.gz=b5444471825952571816fd9862fd395b24baf33afa7b029e6c86421dcd8c2093 -dist/2026-01-20/cargo-beta-x86_64-unknown-illumos.tar.xz=d5b06bba257688118366ee99b4f9f0e744236aca8a8b445146ff0875f84d2cc5 -dist/2026-01-20/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=83876014627bddf2919ae79ba4f96bef45087691cfcaff7a9736d4c2ad9948a9 -dist/2026-01-20/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=f6d9fb70437613afddad7b6ae5114173154167701ff532e3303a0af1212fc35e -dist/2026-01-20/cargo-beta-x86_64-unknown-linux-musl.tar.gz=d4614e938888253e68f38ef5fd3a78b243b068c6e4c675e7ba320a85dbfefb5c -dist/2026-01-20/cargo-beta-x86_64-unknown-linux-musl.tar.xz=05db3574910b1d8d6ae8ebd938008d8d38aeeb1e3b6b59dfbbad6cbeb1bca315 -dist/2026-01-20/cargo-beta-x86_64-unknown-netbsd.tar.gz=16b8c2c5e61f8ff700024e4dd958e93b6f1376e3a06f4d7184ce0849f1dd860a -dist/2026-01-20/cargo-beta-x86_64-unknown-netbsd.tar.xz=f8b0133051d851c42da4c774793b91c68e5073dfd51a7e55e7360bbadb034dc4 -dist/2026-01-20/clippy-beta-aarch64-apple-darwin.tar.gz=f7cfef036026ec14dc58c3ce72e247b2abfc3b14d77a69dfc66ca7c02dfac56e -dist/2026-01-20/clippy-beta-aarch64-apple-darwin.tar.xz=dc4af216099b3d12b0bec8914ad8dfd0bb0f0c6e09c98c54b0f0208a1aa63fc6 -dist/2026-01-20/clippy-beta-aarch64-pc-windows-gnullvm.tar.gz=dce2a442cccf2d571db1f57a44361ed4ee8fd1c6364a12d5fed2afa5c2bb03bb -dist/2026-01-20/clippy-beta-aarch64-pc-windows-gnullvm.tar.xz=232fc9cc1f2c49b893a139d4551b13acb439d06d0188c736439004dd0632f294 -dist/2026-01-20/clippy-beta-aarch64-pc-windows-msvc.tar.gz=fc62e5afc9d5f3feafdf666e4db4fe49b28df1e6155af1d4a2f72de82c923ee6 -dist/2026-01-20/clippy-beta-aarch64-pc-windows-msvc.tar.xz=880ae8ad600f0ec4fd5a6d8dfa581477d4e60f5d5c184e30e44f0f7b2254100d -dist/2026-01-20/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=ccce4428c7962a7f0afa8e80a714a7b9b0377afe385330326c65ec819c7dbbdb -dist/2026-01-20/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=30423c514d1f0ac7f091c4573bdefed369325107b34a8aaeb4d5c94c347e927a -dist/2026-01-20/clippy-beta-aarch64-unknown-linux-musl.tar.gz=d1b1c05f6f7dbcbb3aeff8db0138c4d06ff42d7db61fccacaebffeeea105fd9d -dist/2026-01-20/clippy-beta-aarch64-unknown-linux-musl.tar.xz=11e49da32d955bfad341153b4b0e929f0786d380a9e9480fe89e409a0c399eda -dist/2026-01-20/clippy-beta-aarch64-unknown-linux-ohos.tar.gz=6d2975db64417367af5216eb2c1d873e214e7fccf1ea74ce4a1ad92cc42f9f94 -dist/2026-01-20/clippy-beta-aarch64-unknown-linux-ohos.tar.xz=45d2c04bb0e80d9b6e3a0bb257d1d483837e423450d93beae96c99fc9fd8ce8b -dist/2026-01-20/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=8000aa8009d7277981d9bd535947afb85a0002408e433db61332b1a4860492d9 -dist/2026-01-20/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=5934f3238d15d6ad4c739d9a4583dd7ac1a66a767ed876c093becb87c7d3f568 -dist/2026-01-20/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=d7d2ee5e23e543fb75ccfd4329df70a9d41b4e567815034f83bf56110a55acf1 -dist/2026-01-20/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=d453d595d4af2106724441f381e77dadd4734aeff6f1040ed66e07ac8d7b7074 -dist/2026-01-20/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=580508614f633e31e913443581ce17dbcc0e8778412b19ada22e80faa164ac58 -dist/2026-01-20/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=b035f1890147e91f09f16dd4787df44f790b9fbd3089f784eadcc4a62969a79e -dist/2026-01-20/clippy-beta-i686-pc-windows-gnu.tar.gz=1ad03bd2b785f50261fc19204474ee99880dd0637e9cd7837eeb6f03c61ae822 -dist/2026-01-20/clippy-beta-i686-pc-windows-gnu.tar.xz=659adaa8aeb3155461989dae4d9de803652fe22a17add9257d44a60ef962e626 -dist/2026-01-20/clippy-beta-i686-pc-windows-msvc.tar.gz=2b1b31b8fc2e73630f2b457d2795a2b4dace68c69410c54571edc7f642c3ed34 -dist/2026-01-20/clippy-beta-i686-pc-windows-msvc.tar.xz=8071e38db44b00ea6d1b5fbf8428406d2a4138f0d05df2e0ebca245995ca508c -dist/2026-01-20/clippy-beta-i686-unknown-linux-gnu.tar.gz=76b77a78d8f28b0d71ad03b5c7a56574378a8e1a9366754197fcd278640430ca -dist/2026-01-20/clippy-beta-i686-unknown-linux-gnu.tar.xz=c70373e4e3bd2a2c5b2c98a26aadc3626adbe30365ebaa67d2c6754fecceb7be -dist/2026-01-20/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=a41df8095cb7587ba984b7bf1e2d1bf024c69ba3301f829ec1d1bdda1b8b4fc4 -dist/2026-01-20/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=ec6a573da42202239275c2fb0454016d213f0e4e2a6f923c8a27a576d586199a -dist/2026-01-20/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=bf0fdb37814bf8e8917d6a282e6e3a243e28f6962b79159aea5e0bcedf16ebc6 -dist/2026-01-20/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=c968aed1e04970f170b8a9350c69265eb9147ad12f1ef51b2e8b38994147f686 -dist/2026-01-20/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=60e18cfc96bf3e31f019a4e8c9bc33b06931c3d0784c69ca06d26cba0bc780e3 -dist/2026-01-20/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=a9e0907635db2ae0bf67aefb3f709266c6b538fb3c7dad1ba4401959a8a2eee7 -dist/2026-01-20/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=0bfbc082f733c5e11b85a96ae2261ac0eef097660f170f0b4d06602daeb08513 -dist/2026-01-20/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=de78563496c3017aa234783ea1d33693279870f7a4c7c20d197187a525762d81 -dist/2026-01-20/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=1d657f6310b161ccac78f7b630e28a50620b1afbfb056a53cd94543a3e75ad30 -dist/2026-01-20/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=edfe6128c0d45f69155605379a2f5684de11779c1f2778ed92c5ec428f293c34 -dist/2026-01-20/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=b3afd87d12e3117408b6e8047c20cded8400ea5c37a4eeb014b8a24b9dcf6cc6 -dist/2026-01-20/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=77391e9ac30a1e60f30b1893fffe5241ab2bcf7636d125dd71ff885dfac2b7c0 -dist/2026-01-20/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=926303f213e55c98571e2f901491ab4d17f446ac6f45de356b6965093df54219 -dist/2026-01-20/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=edd50203651d647ad23e1a729327d7ba9c8e9b6a27a0f4cc3142afa3517aeba8 -dist/2026-01-20/clippy-beta-s390x-unknown-linux-gnu.tar.gz=c04a8c4d6a04b204b2efc549e21ce06e72dbbffcd73ee33cb56eba6dc203086c -dist/2026-01-20/clippy-beta-s390x-unknown-linux-gnu.tar.xz=fa5b4a1c53b98885294572536e218eb9325491fed01af40f3c6383f8dbff5560 -dist/2026-01-20/clippy-beta-sparcv9-sun-solaris.tar.gz=95f86bc253baa53f720282f43274091cf43ca940e97eb349c4943068b5f0b458 -dist/2026-01-20/clippy-beta-sparcv9-sun-solaris.tar.xz=b605395af4e223ae367bf1beafbed1e956983997b48f81e71b5fdecf96268102 -dist/2026-01-20/clippy-beta-x86_64-apple-darwin.tar.gz=2b3b01bd6d6b8cc28e5562b5f1eb5349f738a5fd9a30476c951e318714b50ac9 -dist/2026-01-20/clippy-beta-x86_64-apple-darwin.tar.xz=11d1ce0a7246929e7cd15506d846a48f258d63eadbdaf24a451f1b70e1581467 -dist/2026-01-20/clippy-beta-x86_64-pc-solaris.tar.gz=4f57f33c563555eb7b4256116564a458b517cdc353adabb7bca41b592529e900 -dist/2026-01-20/clippy-beta-x86_64-pc-solaris.tar.xz=14243b22bcbec5b294be7c2844f55c4f1274a164c6cf012f3cb6164776069982 -dist/2026-01-20/clippy-beta-x86_64-pc-windows-gnu.tar.gz=74c09292424154c7ffb03a440eb46c0f71a37756002bcce5d0d14000ffd274c0 -dist/2026-01-20/clippy-beta-x86_64-pc-windows-gnu.tar.xz=4d902583e62f6355e786cc3f37a8d708cbff1ecaa1ab341b799f2b4b54b3b9aa -dist/2026-01-20/clippy-beta-x86_64-pc-windows-gnullvm.tar.gz=ef8f5a386a8749142c4039c31823f742f88db4d401b1d276a555d42ffbe353f3 -dist/2026-01-20/clippy-beta-x86_64-pc-windows-gnullvm.tar.xz=30cfea6a64414b471896d592756761c87646f06e6bfcd82e0c906c932b83b28f -dist/2026-01-20/clippy-beta-x86_64-pc-windows-msvc.tar.gz=0a302c0457662833894a3e55e6748a83fc787cabf756fc5a9d79412478694552 -dist/2026-01-20/clippy-beta-x86_64-pc-windows-msvc.tar.xz=ce09b46b20b7fce72a5e6081dc7ab98f5607b6a4145506f2d1d982ef7244bf1b -dist/2026-01-20/clippy-beta-x86_64-unknown-freebsd.tar.gz=d8bd34c3bff9fdd878631faf0597cd0c412c1057e1b49c0b7389d363b0da59e8 -dist/2026-01-20/clippy-beta-x86_64-unknown-freebsd.tar.xz=ff2be9e56354cc83444420b7273502b58043fb02c83903e02e84e51d92522d10 -dist/2026-01-20/clippy-beta-x86_64-unknown-illumos.tar.gz=ab0f8fa1b33d20d336dd0db526cff16524c701987bdbe337758be39d58e7f267 -dist/2026-01-20/clippy-beta-x86_64-unknown-illumos.tar.xz=495c84f3c712105152438428e0d9f021ea9ecba702622e09414b7f7e36a1db24 -dist/2026-01-20/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=cf7303b5c60576390900a1fff488aa86a1c1c78a0bb120fbcb6b9af3bfd63811 -dist/2026-01-20/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=803f29605e90e31c47e6ae33c3372f183738ec4492d284ef5f97e736aacbf739 -dist/2026-01-20/clippy-beta-x86_64-unknown-linux-musl.tar.gz=ae482a158faacc4553b57c8acf6e4662d4f1f903b69a22dfe69b1532315cc8f5 -dist/2026-01-20/clippy-beta-x86_64-unknown-linux-musl.tar.xz=bfe63363bd713b6ab862081a8208c4d8212a0aa542a603aaee595277e546f07e -dist/2026-01-20/clippy-beta-x86_64-unknown-netbsd.tar.gz=905da9ea5d1713ff3bd7b1997f20f4cad58f00c4a863b28164b42d738300a628 -dist/2026-01-20/clippy-beta-x86_64-unknown-netbsd.tar.xz=45f890606a33a3ca0cb07015df19d2e1862531100448f55ffa77f79c1f7b9751 -dist/2026-01-20/rust-beta-aarch64-pc-windows-gnullvm.msi=6d26fbdb64ee9c02e7794bf3b011e19ae1587a4cf5c5e78a5b8d0419a8a03547 -dist/2026-01-20/rust-beta-aarch64-pc-windows-msvc.msi=eb21e2dcd91047d24ea166385c429069703abcb23206cdd914b8f19390229101 -dist/2026-01-20/rust-beta-i686-pc-windows-gnu.msi=6eb532574513a21ac1a8d6d2839d17e7534a452adf37d944069d383d5769944a -dist/2026-01-20/rust-beta-i686-pc-windows-msvc.msi=77b733b05099cfa7c0073d7a748ec2bcc9f040d865897518e0f1786688e9b8d2 -dist/2026-01-20/rust-beta-x86_64-pc-windows-gnu.msi=1273f7f4055ee165476843034d5411e267729d4789dd439939582160d57f29f7 -dist/2026-01-20/rust-beta-x86_64-pc-windows-gnullvm.msi=b22d27a6b3db65eac75b8ad1cd3484bd344ec22c653b3ee939ea6e7649cd69a5 -dist/2026-01-20/rust-beta-x86_64-pc-windows-msvc.msi=5ccaf24b67a8096f1090e27a1c71ff69e721de076ea2fafbcc8f788988bd51d6 -dist/2026-01-20/rust-beta-aarch64-apple-darwin.pkg=5280c8dadec5a0bee55f4a682b20df0330369bce0eb1528036078e779248bf6f -dist/2026-01-20/rust-beta-x86_64-apple-darwin.pkg=51b525faa1b74c7d90b9a5b0858697d22aeb214e7d85c5dfb6d56cef1d2351d6 -dist/2026-01-20/rustc-beta-src.tar.gz=6737690a0fa28bcfdb154384a49428a7d752586f8e8ea22bd1e2029e6da2a2bc -dist/2026-01-20/rustc-beta-src.tar.xz=3184947f6744d0b049fcbc528f9cad6b0861daba8f883650d7cf0591e144d52e -dist/2026-01-21/rustfmt-nightly-aarch64-apple-darwin.tar.gz=affc4e2ba7673815113ebd077d40981b0eba06bd321f503b8fda8f4ad041993c -dist/2026-01-21/rustfmt-nightly-aarch64-apple-darwin.tar.xz=63ff46d1b84d4a5f0aae251fd6636fde72a9ead0f379bb5af709aa781f9010f0 -dist/2026-01-21/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.gz=37f974a21309fde6201af7f738cc243ee1f5d18f4f43833c5dec95623bc48681 -dist/2026-01-21/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.xz=a69d9654017ff44ec6cb0b2c47ba62c07f0ba114d1f50bb8322994d646f99c50 -dist/2026-01-21/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=08bac0b794d237022227043b4bca1a94561eaad7f047dc050c2ddd9a638f9382 -dist/2026-01-21/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=14919590f33efb9a005a7dedab82cd74d520799c46ecd9243a9c265a0d7fc7dc -dist/2026-01-21/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=d0047fc62080e4a30f398de07c2dda5f6b2c1b28afbf1acd6932d826cf111827 -dist/2026-01-21/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=18c1476d5052da02ea353a6b7881c9125231f044b1ed64edbf4a996ac5e677eb -dist/2026-01-21/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=380ca0dfa172a5eb6f2f3e1254e4d4c2cf96518d991ca09a6c582cad8a8b9bf7 -dist/2026-01-21/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=8c4aec7b93972e480b0ca9664fd231b47c4197ea0ae06ecbbf978aec7238b787 -dist/2026-01-21/rustfmt-nightly-aarch64-unknown-linux-ohos.tar.gz=68573867ceec9311464e45e31bb2e4b65d68eca62a488afc2309a40a3c1bb003 -dist/2026-01-21/rustfmt-nightly-aarch64-unknown-linux-ohos.tar.xz=affab0babcbb414de642817ae1f667c36e429e22d0db6e4dad62321fdb35b126 -dist/2026-01-21/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=435f927bcbc1a5b3625abcdb1c38aaaa26d8cae0928740b425246c92bf2427e0 -dist/2026-01-21/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=7d6482df5e8e0530f39e1661bc9ba56765512e800c1e2a387657f62c67fc4062 -dist/2026-01-21/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=b7c509220780e000f6f5f21527d6faa4e53fc099aab70b566c2f6ae16a407caf -dist/2026-01-21/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=c2a8ba39291834512596eb7861f486af6c6e34123da36c0030841f9fa0b8265f -dist/2026-01-21/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=7da502260349e6d6b7f79280f8973f1a5368821ab4bd88f92df7f2b04ccd0529 -dist/2026-01-21/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=0a0390f87cd6c3c8e522a8729ac836285521ac563afb6d3d1b42117ef846922b -dist/2026-01-21/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=d19be564735e74a31cc3ce32afd0b19bb1e2bc8cda682661d8de179ae1c6daf4 -dist/2026-01-21/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=ecc5e809442b3e775158d81a16844d9cccf89d48e5e404780459c4b9a3257d2a -dist/2026-01-21/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=8a888e80fb00b3f7351848947a1f44f1a7fa63ca52c279c7da4067e0db6c3be2 -dist/2026-01-21/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=dd3cdf3a1a3984e2e563bb83aa0e97548a5d02f10aa1d0da50127bc56869428d -dist/2026-01-21/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=8670a561b2c7e356415d930b3ddbb561c6957351cb4bc048a804edc14a92a308 -dist/2026-01-21/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=51899130ab87607e203a6c9f27f0c22ad34642e6df40df2ab77f4863b582697e -dist/2026-01-21/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=1485f91c3cefeeab023faef5a8b93f7c2af91d4c47d5ac79683ff58f7c1557c1 -dist/2026-01-21/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=05a3d5b87af407b42f6985b84515463d924b8af126cb927f57de5e1bc0f8ab07 -dist/2026-01-21/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=def8a13af57f5a8603b3efad06031a78ad8b0ff111123970594ed61c52a28e9a -dist/2026-01-21/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=f4b47d15291cf78830c88d5bb404035f9ce5dffbb5b6bc79ea987ae0995b3e30 -dist/2026-01-21/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=8d4574b8a8407d0740ef3978ef6ee44f59fc7586893ad76ad176785ca12f091d -dist/2026-01-21/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=f59e764f38cfcfc620f522f99be7fa2ed36ef23b8d45887da78839afb0a91083 -dist/2026-01-21/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=2a98d3ac86e927646884c50338fc87ef218ea026c5b3ff1a899bccb6ed67a4d3 -dist/2026-01-21/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=6b282883509ff1b1b74e7e484f124018e5044dc13e83cd626a12690577be9a02 -dist/2026-01-21/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=b18dc4ee75f208fabf5fd5765572d33bc7f8862fefc1628bcd61438e83ab525f -dist/2026-01-21/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=d1b158a365a28d440459aeeff4154106d7e340db2b64f2defc53c6b102d839c0 -dist/2026-01-21/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=62eff4eac793fd4b4e4ccd377e9d6925812a65196e5d17b6c80aca7b2387b624 -dist/2026-01-21/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=fd95f22a380c281cfa919c45d060418d753923cc14dfc4f13885ba9aa23425ef -dist/2026-01-21/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=f139664d1bb30c7c0e01ebe66c0a24372fa22c95f32d49e9d4422c36eb83005b -dist/2026-01-21/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=00317b285892b5f294db45957214cea31b7cbf238c329b752fe6b38d89a74574 -dist/2026-01-21/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=29e9bb3622b98e198fb552ba83de4c54b60c60ee3a06824eff9e814ebf5b4f78 -dist/2026-01-21/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=8fd331c828e5a4b9b0ed29c4931b299d08464db303985398f466aee05f214338 -dist/2026-01-21/rustfmt-nightly-sparcv9-sun-solaris.tar.gz=3f590d494a001ddeeab24ca3754a8344b0397839cf3df82d6ab571804910a9b7 -dist/2026-01-21/rustfmt-nightly-sparcv9-sun-solaris.tar.xz=ad4a1b9d2446cb037cd1429c77781473c2c50892fa3e3c97a15abf4a34952a95 -dist/2026-01-21/rustfmt-nightly-x86_64-apple-darwin.tar.gz=ac4b6f7d51288d756765fa72faff773a5f57681ffed9b9230db3f116f4b1ce5e -dist/2026-01-21/rustfmt-nightly-x86_64-apple-darwin.tar.xz=e8dc9b7c5230e2fcff79cc78da9e426cf46e3b0925524e672376ca454d928aea -dist/2026-01-21/rustfmt-nightly-x86_64-pc-solaris.tar.gz=300937f554725ab7faa68bdc37655c93a40c49dc29a9a2706f188ecd26fdd76b -dist/2026-01-21/rustfmt-nightly-x86_64-pc-solaris.tar.xz=6c5c7d56a4434cf282499a6c7e6c6aa6029313a1fb92a6d09e6a42d4b11df2e1 -dist/2026-01-21/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=297c396dc372f7e5230c012c082d45b322adf7ef959d560d213cc338d556eb60 -dist/2026-01-21/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=43b9f55634b0d276d348271696f024ad01744ab96a8d5ac66b88bd9f7c471efa -dist/2026-01-21/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.gz=40330c41b78296e15401f48732b8de62120085c5455bef223906f220919506b1 -dist/2026-01-21/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.xz=e437a17c2aecfa01c35ae3a2cbd199b0dc6b6c1039be7e45b089e05ae5cabaee -dist/2026-01-21/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=7915476f152fd05c8b1b3f2114a2762fa2241ec873e40a817bc478934ed5b631 -dist/2026-01-21/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=931f4b5d7028e34fe56b01d4f6d2796fad15e99868054390d0b0e929b96a794a -dist/2026-01-21/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=0246e43067b01d5418926c978da82d99341610ab6449911993bb92b4934f2bbb -dist/2026-01-21/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=18dd4a5564dadaa55e3ce102cefc98ebcfaaf5aa8d51e5176ef6419be45efaab -dist/2026-01-21/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=ad3c0773583835431b7b284005ca0cc859b27e1bc7a68201746bd969af16755d -dist/2026-01-21/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=528856180a0b335f1fe28e10021a921ceaca8db61fb98d2d732831dff0bc3467 -dist/2026-01-21/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=2e517f4312bc3fe31ed294fea9df818bd271f8d97b094a199275c8c229284c69 -dist/2026-01-21/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=69a37d60e9de3e3e718e08d31b7c5807c06d482e9d3b1d0679571493e9cd26db -dist/2026-01-21/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=ee8cf1139bd721c7e1d8bf2755ca1778e2d324d117c714c06ab1e9fa3af5c27a -dist/2026-01-21/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=032bfb8c944a2edf113703a8154bc2fd27a73112a6f5fcd6cd137617df1d0966 -dist/2026-01-21/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=93f89369e38d2f452ec887b1c6e5e987320ebde2675613e8f370ce46adcb2e6d -dist/2026-01-21/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=e6db8c3bcd35c942ff6b97e600f5021c57c8da8b0523f93d4367de27a1e19d28 -dist/2026-01-21/rustc-nightly-aarch64-apple-darwin.tar.gz=05ca50a36c2b8681ba6467f71800942da3120ed74b80f25a9a53f859fb2c4cca -dist/2026-01-21/rustc-nightly-aarch64-apple-darwin.tar.xz=aefda689c6c6aed7eb68ca8acac0809c1cbbbb35fa96e60f50bf79a0f80147f8 -dist/2026-01-21/rustc-nightly-aarch64-pc-windows-gnullvm.tar.gz=9554e05afa28b776f85b50100582d1287320046612eebd1b1e1eb9e99b425559 -dist/2026-01-21/rustc-nightly-aarch64-pc-windows-gnullvm.tar.xz=fb0d1852343fbd21b94001729f9185202c704a3f0ce387079225112ed1795b36 -dist/2026-01-21/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=795158ddd8aaf40878fb477145f327c91f3c51cad195272f80e693488ca815e5 -dist/2026-01-21/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=d816aa3f7b5f81550f02982300b9b0d8c258d1ed5a1dac837e45db1067eee984 -dist/2026-01-21/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=62852a17246080ae06159eaa290910fefc5a6ad43e3e801e348a1e04998b2002 -dist/2026-01-21/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=ce688272bec1372fd1c27c5e6c228c1a6696376ed33c034da7070fa0353c1c5c -dist/2026-01-21/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=1d6c4f926c4fd7f09b4bff2f0754191fc94758fd80dec401a18b6f5cac907ac2 -dist/2026-01-21/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=1f75d8d3e04da5d44770b45073fc71bf54d08a079a9aeba34effa7e620e4ffdd -dist/2026-01-21/rustc-nightly-aarch64-unknown-linux-ohos.tar.gz=312ffa2180b4f0e99d577e584a8c8e89ec763d83ea3347b3c905e5bd0871e0c9 -dist/2026-01-21/rustc-nightly-aarch64-unknown-linux-ohos.tar.xz=7dbdc7c25ef72dd904d7565b8d4b038dcfb51ea451107ab100027a6e8dccae88 -dist/2026-01-21/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=628f2ef937685ec2642fc49b834bd30fcc1c64ad6019feb659877e20b3e3884d -dist/2026-01-21/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=bad0dc6b0454ab24527aed9640fc68f422ec3efbe7fe7d9c4c07fa273d3915a7 -dist/2026-01-21/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=a2635ca207aa8461296a6b40283f92865ddb15b95927ccb28eee332fb9d1e9bd -dist/2026-01-21/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=9558ef1d896813b02203b5b5f784627d74a678c62fc7f3580b5d2a4bfda9ed44 -dist/2026-01-21/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=fa8e5603562a361dc2335e4aed12dc9d997ef53a0eb6ad6fd9503a70d2816f7a -dist/2026-01-21/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=3f6f152fd6f8dc3dd9ee204f4ee1408a82a619e817c08c739b90c56d992fa3f6 -dist/2026-01-21/rustc-nightly-i686-pc-windows-gnu.tar.gz=f1bcd3ef4d74260eac3fec3ab623c5bdb506c5ebd506602957601e24c11516b0 -dist/2026-01-21/rustc-nightly-i686-pc-windows-gnu.tar.xz=29f5072a55299a2a93a2842318608735967e46b744af550aa19fc6b43ebce2f0 -dist/2026-01-21/rustc-nightly-i686-pc-windows-msvc.tar.gz=77001ad2709e35f22be3410afcb061d060eee6d2a7690ad93371d85c77163d87 -dist/2026-01-21/rustc-nightly-i686-pc-windows-msvc.tar.xz=88d617bef85fc9b99d730346f22a884363db3f111e1301d9fe0f807966e13bd3 -dist/2026-01-21/rustc-nightly-i686-unknown-linux-gnu.tar.gz=a8dd200f6f6df430c023707797f3793670d0e5df1f71d74edc67c300c9ea9ee7 -dist/2026-01-21/rustc-nightly-i686-unknown-linux-gnu.tar.xz=c7578f1360c23af97bdd3d76b802f0ab5da289f1000f49909803a5aa957ab2b6 -dist/2026-01-21/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=ebdbe1939f4fe80b7b90222eac5a76dcf40c99dff11342cd3f8b4af32d5e85e3 -dist/2026-01-21/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=4c8894c0c888075cb54834b00250871ca8c71d42d1ad2bded20e8678deff3683 -dist/2026-01-21/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=40851b65b3ab065ddf184096ed58b8deddd80bc33047f5d4524361ef99123f16 -dist/2026-01-21/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=657f707ea176d03b36bc73abedc8947c6f1b47e8836f8154a21770273fa05bd8 -dist/2026-01-21/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=4cec47f661bd413f415a21f0951a028f2f7aecd22cb708713f08a7ea1d5828fe -dist/2026-01-21/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=f9984bf28f179296a1d66de91920c5b6e482430f3aab278db82fb8d4a6b96e00 -dist/2026-01-21/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=8433b87fb9d5f257e4439f2d5db3559b9c54dcf7f504331b1f9e10b4290e3d61 -dist/2026-01-21/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=e84f672d2e4cc6a6e3d13e1cf429f498fe64327bb85e82d6437cdd527332b462 -dist/2026-01-21/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=981e05ff04a005d193d2f5feb12656f2bd5d2dbad4e8bf7d40421eafe52b7f44 -dist/2026-01-21/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=fa05d796d40ecc3b8d552f86c690b465044462b0820cd3774eb8957c1fc05c99 -dist/2026-01-21/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=74dd0095d0ac98a29b217728edd3cb1f521ab59fbc2a3927281d26cfbc5d5829 -dist/2026-01-21/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=4a4f61d37ae4f9a2c26069cd6422acdbb3ee43a6a4b4e32a20ca76a855a5b40f -dist/2026-01-21/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=1038bec45e312d6c7c5802914ee31603fe0a14628786c216ef4feef0399ccb26 -dist/2026-01-21/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=534d286cb8b5131e34263edb189253f4f83c4274678017d4161c92ddcd2b925c -dist/2026-01-21/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=01c14dda8ba60125208377dffbfecd074ef9c42acd21a1cd330a6b772d419e6a -dist/2026-01-21/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=d52e035c78aec546b820dd085ddc097dc30b111ce6b6616751f15e60a294e7c1 -dist/2026-01-21/rustc-nightly-sparcv9-sun-solaris.tar.gz=c0abaeca6731e2c0b2d6437bc3d7c0c240cabc6373773a5c53614c6170c82d91 -dist/2026-01-21/rustc-nightly-sparcv9-sun-solaris.tar.xz=6dbfcee73e53c92e7f80e81bae9c86da392ff46667867bec5a5eb771b734c52a -dist/2026-01-21/rustc-nightly-x86_64-apple-darwin.tar.gz=0a5c541c832312723e6d1ca0dca655f380118c57d29d1629310bdfd116628a98 -dist/2026-01-21/rustc-nightly-x86_64-apple-darwin.tar.xz=78f631995200a5a48156321ad3abb8d97983080134c2404681a48b5007dda2c3 -dist/2026-01-21/rustc-nightly-x86_64-pc-solaris.tar.gz=fe0d0240075ba8545845323c1966cac7c45d14f34f5fdee547e1b1c3847e3453 -dist/2026-01-21/rustc-nightly-x86_64-pc-solaris.tar.xz=09a3e0be0f2bc5ab6af1726e479150be43b7dd9e3bd459998d7bdb30a4f16b73 -dist/2026-01-21/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=7ca059132ba0615d17b6c32f5f74b86bdceac13940198cb6b9b6bb0b821db118 -dist/2026-01-21/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=93aad27c0870881af1f58e91e4015fd3ef6c023fcf9877b34c301e5c0a23b4cb -dist/2026-01-21/rustc-nightly-x86_64-pc-windows-gnullvm.tar.gz=a731ea06a52953dd1741be00ec0d9c651080c1725c9298f22afcce2500bb74e4 -dist/2026-01-21/rustc-nightly-x86_64-pc-windows-gnullvm.tar.xz=81cb2094a2aa287cf940bc637990771b3243434f54523dfe5133ad476967cf30 -dist/2026-01-21/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=cfb755273c592a5bce52728454eb4b880022e2ee6cecec9f9f4206599ea0ee5d -dist/2026-01-21/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=2d3ded1d4a048146a56182d106fe82a6357fdf688921701fa2d1a93733a05eb9 -dist/2026-01-21/rustc-nightly-x86_64-unknown-freebsd.tar.gz=49355564096356792020a1fef64a3e413f35790e40b572e36e26307c38fad144 -dist/2026-01-21/rustc-nightly-x86_64-unknown-freebsd.tar.xz=2178ff7a29f514adc3cbd93940956fa397611e90ab51da87b5087071512bb15f -dist/2026-01-21/rustc-nightly-x86_64-unknown-illumos.tar.gz=1851080c6461ab09568fa537f604dfba79bc55e06b434029d69445d8963b1f27 -dist/2026-01-21/rustc-nightly-x86_64-unknown-illumos.tar.xz=a6d4ddfd1985471c05bd70d18a56da3afae4039c2680813338e1189b1240d28b -dist/2026-01-21/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=40b6c623ded8ea06fbb97303cdf8a8c1fbf3bf16ca38bfda8b7e118480c6fb1f -dist/2026-01-21/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=8ac14c7783ca3e8b4fa8fb7df22f3950418c7a37ddb1f327d336de72e25e92b9 -dist/2026-01-21/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=2bd8b16b8632b290516dd1638ce088905d06784fb638eb765ab0b7ed6a9d6528 -dist/2026-01-21/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=5e7fd38662525448b8d223be07894bf600686c2bc28d926074967d9717b14063 -dist/2026-01-21/rustc-nightly-x86_64-unknown-netbsd.tar.gz=e5d06dce9f4659a7ceb9ad5f88226e447c5e095a2944d0727b475d9990d7d7b6 -dist/2026-01-21/rustc-nightly-x86_64-unknown-netbsd.tar.xz=48f8198fe59877d31cdb01d6560fda88e3a194a75eb4c664b5397061ca877f3d -dist/2026-01-21/rust-nightly-aarch64-pc-windows-gnullvm.msi=003330d02b7ccdf87a54aef9afe5d5d164f433ceec98d7f8e22ed8349f5c07a6 -dist/2026-01-21/rust-nightly-aarch64-pc-windows-msvc.msi=ab2d63a49e1d9c3c9fb2e5b59fb456b022680adf6b0707ee2223a4fb4eb66aaf -dist/2026-01-21/rust-nightly-i686-pc-windows-gnu.msi=fff10ea8984cee03280c4b10d2f6c740a88d26216aea661ee67580c64f69e8f2 -dist/2026-01-21/rust-nightly-i686-pc-windows-msvc.msi=f78b7b22d7505e361201ac502596e9cddf04a625af287801c89b8e46908f82fe -dist/2026-01-21/rust-nightly-x86_64-pc-windows-gnu.msi=f26775794a4079aa09bdd69706c6d3b8b5c67d4264a1859302939f2a647b6d88 -dist/2026-01-21/rust-nightly-x86_64-pc-windows-gnullvm.msi=114a595e1091daa04a3652a0c8b2dd21992848118bdacba4b076ea28f8e7dbd9 -dist/2026-01-21/rust-nightly-x86_64-pc-windows-msvc.msi=2813a6cf26e45fff004e2f531d61d1cb08187ebf0f3c6395d017a4c5e3ce5032 -dist/2026-01-21/rust-nightly-aarch64-apple-darwin.pkg=17093b3b31ec58273f7bbd52014436ed001b14ff76dabd1df4018d9a8e95e0ca -dist/2026-01-21/rust-nightly-x86_64-apple-darwin.pkg=e758c7a74f3599f2bd10a58c78b8d0a60bcfe356b87d65c8430081556f0b4cdd -dist/2026-01-21/rustc-nightly-src.tar.gz=0af498197bb5c995f078770a208c609b60baf20b27fa54c5888d9e38c66bcf8e -dist/2026-01-21/rustc-nightly-src.tar.xz=2e44bad1f973ebe58ae463559752d7e2a93511927e847957867ee781140b15bc +dist/2026-03-05/rustc-beta-aarch64-apple-darwin.tar.gz=650b56e03e7154b0c186e0da55c9f041aa4cb3647df11463442dc537370ada85 +dist/2026-03-05/rustc-beta-aarch64-apple-darwin.tar.xz=381a0ef50b903bd25eab7e77527ccbe1587529402c97ce88622631494b242ffd +dist/2026-03-05/rustc-beta-aarch64-pc-windows-gnullvm.tar.gz=ff3174cdc011b123932c36dc8b8aa4d621a335eb581b46e62f14c6fb006f22bc +dist/2026-03-05/rustc-beta-aarch64-pc-windows-gnullvm.tar.xz=905581d7fddf56543fa1acda7fe4db6227de8ce042385c9bdd4c364fb84aa2c8 +dist/2026-03-05/rustc-beta-aarch64-pc-windows-msvc.tar.gz=00c91e988eab57c55190dc86ed999f702d829edd4710fd4e5e3634fbbbdbfeaf +dist/2026-03-05/rustc-beta-aarch64-pc-windows-msvc.tar.xz=7d336f56d300b843a849da814c53a2beee7b691f30acb36593f6ec58360dc7d5 +dist/2026-03-05/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=81d3fbc4892b4258bcee80e80623cca3415884981a9917daada0dda12b367dde +dist/2026-03-05/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=290438ff8ba8568c4d73934feaa306af9e7c4caa0c8661882ece05b939c163fa +dist/2026-03-05/rustc-beta-aarch64-unknown-linux-musl.tar.gz=d6503532a5ab7b503f82fa9d351c01e9817d1cab6c25d46e314009d0dc75cf40 +dist/2026-03-05/rustc-beta-aarch64-unknown-linux-musl.tar.xz=083b52bf221707eacc293d71c9a5c28976564318af3c97ae6f58cd8773d855e6 +dist/2026-03-05/rustc-beta-aarch64-unknown-linux-ohos.tar.gz=60bc7c0341e6d5134e6ad906fd752ebbc882d06542a35af226a3b56eb59282cf +dist/2026-03-05/rustc-beta-aarch64-unknown-linux-ohos.tar.xz=1332a554b15f83874c57ad6121ed4883cda66df8b44070491eec13ce39834b4e +dist/2026-03-05/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=a01016314c662a96c03df4cf5380903796f13ca54848827381d99f4a7159957b +dist/2026-03-05/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=325a6f5872936c2302823fd905d22eed59a78d2cbdf208f5b6017808e2263ad9 +dist/2026-03-05/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=7a46a409c4058189d22d1e48b3bd629343ac4ed5a3dfd618b689200b58ef95dc +dist/2026-03-05/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=f7fb4b0d5f10b590ea2e4beaf75b6c6f4361ead7901d8fb897133632c9698856 +dist/2026-03-05/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=280725ea12cf056a4f03ff83e2d81f919346c2903e5791a126fa6d16efbcab16 +dist/2026-03-05/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=52835b250cc4727f8084f192e9344014cfad9bfb5522dc453da2746c15c4eb99 +dist/2026-03-05/rustc-beta-i686-pc-windows-gnu.tar.gz=7961a9dc51530f9e1adf5a59f53490d3dc2ca157f8b211d0ec5ec0877988bfb9 +dist/2026-03-05/rustc-beta-i686-pc-windows-gnu.tar.xz=ca668b08973f71f589e9d4f77de9f76f2ee4a82b7e5bb183f377769aa9fc0084 +dist/2026-03-05/rustc-beta-i686-pc-windows-msvc.tar.gz=b2ad97cd15048533ed9dc6f75b8ff57aedc2af81fda6f0ed6be34af36464fb93 +dist/2026-03-05/rustc-beta-i686-pc-windows-msvc.tar.xz=6523e6786f296d3ea28f4262a1c6ca04b516c2b4503e49c3c7d3ea327082071b +dist/2026-03-05/rustc-beta-i686-unknown-linux-gnu.tar.gz=67e9bf000831d12df5c142ba00d3b8e7ecdbd83a743ef980268463e5afa4387e +dist/2026-03-05/rustc-beta-i686-unknown-linux-gnu.tar.xz=62f9750fd2a2105d0fc13942c3641b5f6f134321a464380d2a723bc0fe52e738 +dist/2026-03-05/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=0b27d682292f44196fce7f6bf1eaa071a9a7601c3203afa3181510793294c0a1 +dist/2026-03-05/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=b749c18e1d118e4a8ecffb6686930afba2768df66dd34ff3fa6cb64ca7e6673e +dist/2026-03-05/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=723aed06f88df2141444364b2edcb4813a12604742d4b96c919490e9010ddec7 +dist/2026-03-05/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=9cbe23a8790f009da41e84e2dfb6c435deca18a25f35a852c36decdc78a9dcbd +dist/2026-03-05/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=a5789c1b290db71f2966dcbbaffc792c1c2980d2e86a53fcebb5328de2e8f73d +dist/2026-03-05/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=5e8a86b1c8e8be1d48d965a289d5329a5c7127c518f8ba440622cd5312d2d442 +dist/2026-03-05/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=0c2a4a56dcdf62b0ccf4fa828730bd1ec5295eebace379609d678480d84980da +dist/2026-03-05/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=190b49c000102e56bb9318edaf8e0fed179345ccc3cf78fb57989d57f3974cef +dist/2026-03-05/rustc-beta-powerpc64-unknown-linux-musl.tar.gz=738add7773970131fd43f080410a8fae703ae6eb5dbc356a1a427ad173489e92 +dist/2026-03-05/rustc-beta-powerpc64-unknown-linux-musl.tar.xz=0faaed4c732ae9b771644d84739492fdc80bdcb350fed185c8736166e8ad9c20 +dist/2026-03-05/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=0a8f9660dc8a14bf2ff9b8c7a2df8fc23a45c14fc6b085fbd50a13877f042c04 +dist/2026-03-05/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=51429bf6500b7d09da100dd959cca5e88aa4e8545cc5beb09a7300b5eaee1412 +dist/2026-03-05/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=3ff09553efaf5d230dd4a0b3c8e5a0e6ae62d00fe815e9a0229dfff34e7e87ee +dist/2026-03-05/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=b620eed0bd1ebda01b4119e15b5178b816fe96e366afd8814c786e006541c785 +dist/2026-03-05/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=6acf5d8fdeb2e7ecdc91da9294aa4a588b5791bf99cf4ad7765f3ed388a5dd80 +dist/2026-03-05/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=aa9769b54f698eb24acdfa5085130558a3571569d50c009dedd2ce9eb72345af +dist/2026-03-05/rustc-beta-s390x-unknown-linux-gnu.tar.gz=4ebfdac050645cf9afc0e2607f62995455a0353d7653a66d2bb08f7fad804a48 +dist/2026-03-05/rustc-beta-s390x-unknown-linux-gnu.tar.xz=610496d4b784e3fdb2fc7a32668fe58b14268792bf6b4894d8fa0e833112a8c7 +dist/2026-03-05/rustc-beta-sparcv9-sun-solaris.tar.gz=030105e01ae63f958ecfa42987c256b07e5a585002ac1fbb6dcbc4c7df18c038 +dist/2026-03-05/rustc-beta-sparcv9-sun-solaris.tar.xz=0ccf23b879e50b3bf769cfcb7951434bc2ba59265139a0ffe00706dfa44dd5ee +dist/2026-03-05/rustc-beta-x86_64-apple-darwin.tar.gz=83280a373b737caa5b6f216a3a556f8d869f0b54243cc53dd604d5cf0b5e07a1 +dist/2026-03-05/rustc-beta-x86_64-apple-darwin.tar.xz=2881512dfdf291dfa8cc690a765afeee80a742e25fed8b4639fd342d3b57dcf3 +dist/2026-03-05/rustc-beta-x86_64-pc-solaris.tar.gz=91e0e499456fd72682eda9e1110c3092d96c8a0b833e6c9e2ae4be8a2061d067 +dist/2026-03-05/rustc-beta-x86_64-pc-solaris.tar.xz=4c80df90c8894e10b8906ea822443313fd5081f2fbf4f18977357773f918e424 +dist/2026-03-05/rustc-beta-x86_64-pc-windows-gnu.tar.gz=1b64212003ff090df8de09c03c91f99a26c72c83aa36ccf8b2a1196a516fbaee +dist/2026-03-05/rustc-beta-x86_64-pc-windows-gnu.tar.xz=554e356a2962b389f3dc45ac21ffca256e1ed21ee29f567d3d67b8a4b5ef085e +dist/2026-03-05/rustc-beta-x86_64-pc-windows-gnullvm.tar.gz=fa4bec1bb19c64363eef9d8311aae96e44d06f10c5996d5f7a33ceae01372738 +dist/2026-03-05/rustc-beta-x86_64-pc-windows-gnullvm.tar.xz=1e38ff8b07ef3b70a5e334884346011fdcc1c3c30651d7099a66633d656e6e23 +dist/2026-03-05/rustc-beta-x86_64-pc-windows-msvc.tar.gz=8bb72e21316da6ab8e062dc70fce7f4f7f6768cd172c243c1057891aa358c9ff +dist/2026-03-05/rustc-beta-x86_64-pc-windows-msvc.tar.xz=a88f497378e937eb37c5eceff9ee7de18153be3542c8aaa086644c1664832838 +dist/2026-03-05/rustc-beta-x86_64-unknown-freebsd.tar.gz=371760bc3d699b172f84b11eacffe8f2ca327ea3855a455252ad397f0fa2c8ca +dist/2026-03-05/rustc-beta-x86_64-unknown-freebsd.tar.xz=c773c2e91b829ef50736baf135e34aaca6f85a6c3ba338a955b22934f0a0e007 +dist/2026-03-05/rustc-beta-x86_64-unknown-illumos.tar.gz=60bb8ae417384961616050d0f0b1a64e1f0a3e3a70454358f7e86ff57a2d088c +dist/2026-03-05/rustc-beta-x86_64-unknown-illumos.tar.xz=85df1a6cae9cef7c4b1c937f00a1a3040e30e2607ab1432b962c2f078be98642 +dist/2026-03-05/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=7227d0c3367084f40b65adc970a5062b6b432eac74927859e5b561d5b205585b +dist/2026-03-05/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=09f6ad1eac75fdd1fb9fec65896a89230af299764be7035610dbb257ff7db092 +dist/2026-03-05/rustc-beta-x86_64-unknown-linux-musl.tar.gz=4c4514a5809af869d8517f5eb0ce4c985203c8f4f29fca70f8acdce7d6b1a7ad +dist/2026-03-05/rustc-beta-x86_64-unknown-linux-musl.tar.xz=cb915951bba6f4a0b6aeb8d15c262ac6f0bc0fe8449715ef856d7c31985a2961 +dist/2026-03-05/rustc-beta-x86_64-unknown-netbsd.tar.gz=af520fc931de40fdd053f04bda845c043fa03ea3816cf2543232b04c37faddbe +dist/2026-03-05/rustc-beta-x86_64-unknown-netbsd.tar.xz=c7070141983633d890d41dfca8652b7c7fa84a965ac3e98d7e9c19237c54e5c7 +dist/2026-03-05/rust-std-beta-aarch64-apple-darwin.tar.gz=cafe606fb1d62c7181a65c44bab0fc74f4173a9e8fa832d2b3114a601e41ed50 +dist/2026-03-05/rust-std-beta-aarch64-apple-darwin.tar.xz=72ee3b3f8e51dbb05d543016fc58ca549eeabcc30d824717dff0252da4735d82 +dist/2026-03-05/rust-std-beta-aarch64-apple-ios.tar.gz=0730eff2b7e19e90cba3273702d16389f71a04f5bcf49116a2ad3d17a6aa75ac +dist/2026-03-05/rust-std-beta-aarch64-apple-ios.tar.xz=74616504a19fe9c517bfa28eeb54b037092f12f44b85030bb52a5b9ac6d5faab +dist/2026-03-05/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=1f03b91804fb2acb2c763d09fa8d8a7acc899c9d3c5294567bd8cadb6e9165bf +dist/2026-03-05/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=268834b76808fa8727540d1e7932fd3e473821bdd099f8b86e79f7bba724f504 +dist/2026-03-05/rust-std-beta-aarch64-apple-ios-sim.tar.gz=7d850e86133f7bbacecd9148ada1c7ed30a733c9cd532e1abfd0f55d52087910 +dist/2026-03-05/rust-std-beta-aarch64-apple-ios-sim.tar.xz=b90fc4c1b4c09fc9d4c0caa74e5033809f3ee966f602dffdd91364dc83bd6cfe +dist/2026-03-05/rust-std-beta-aarch64-apple-tvos.tar.gz=a7563c0d120528bfb2dc4080446ffcc3446374c0ca63c0ddddfc07c32c94c08b +dist/2026-03-05/rust-std-beta-aarch64-apple-tvos.tar.xz=cca30889ac56c8c0896ec7c420d02192bc6d7a74cbf6721625c7a710e46abcb6 +dist/2026-03-05/rust-std-beta-aarch64-apple-tvos-sim.tar.gz=5a14b4402c1ec7aea207d22ca697b090b251210ba0694039fe4ea3a4c2e72715 +dist/2026-03-05/rust-std-beta-aarch64-apple-tvos-sim.tar.xz=a21c3eb2f3b5cc2ad646764022e1f88ba2c24a300ea10aa5aceb3e6b959e4d1a +dist/2026-03-05/rust-std-beta-aarch64-apple-visionos.tar.gz=dc0a255f8e87880e91989c862328277d530f3a42a48f3efe33f90b5f862081e2 +dist/2026-03-05/rust-std-beta-aarch64-apple-visionos.tar.xz=86788e6c2f85a8423b7605927aea132623591247eef2e45aa84fb47f3101c1ff +dist/2026-03-05/rust-std-beta-aarch64-apple-visionos-sim.tar.gz=074c5d2669def265508f3be284c518b0eedc81644052c001bf15dc3857557abd +dist/2026-03-05/rust-std-beta-aarch64-apple-visionos-sim.tar.xz=80f3809c79778f83944bc35293cbf77031ca7734af119476ebf4dc38fff569dc +dist/2026-03-05/rust-std-beta-aarch64-apple-watchos.tar.gz=906b636421c268f6d44ef80d9d1de8fd996edd3fcc157a19401126825a7c3cd6 +dist/2026-03-05/rust-std-beta-aarch64-apple-watchos.tar.xz=10b38ff3e2eae457831e22ed5970f4cd5b2e0f01275a733fa3eb27ec27015dbb +dist/2026-03-05/rust-std-beta-aarch64-apple-watchos-sim.tar.gz=bd54cf3033bd31ee06e9991dd1a95c0cdb6a7c99164eea3fa9a5b0eca7faefa4 +dist/2026-03-05/rust-std-beta-aarch64-apple-watchos-sim.tar.xz=1e7c5ca89565862d74c7116f54e8f79c60ba73d84323dd0683f6790ba1de9bff +dist/2026-03-05/rust-std-beta-aarch64-linux-android.tar.gz=c74d4fae56c3b2d8b3246e4162aac942d8572a55b998b7c817624f486afd99e3 +dist/2026-03-05/rust-std-beta-aarch64-linux-android.tar.xz=ce86ee64dd869f2d28d5acbc97c055e74b78c185ea6f78941e3d45b8138e52c3 +dist/2026-03-05/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=a93b191e685c3da24f76c6144b17cb112e124e1f8138de262bbbf548defd7f8d +dist/2026-03-05/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=126d68941a10d43392ff6fe7814cf41512d2eb8142136b3ae044e554aafae662 +dist/2026-03-05/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=1a29244fa330079a71c2249c385b1b83f1bd5d10649ef5d13b707a13beeb3ea9 +dist/2026-03-05/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=2728145b39da6d8b2a2e29b3ce0f362dbc12a9e31911240a8c20a4dc26802f94 +dist/2026-03-05/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=76cbf59626d054792dff027a2316f8a797416b8e739bb5266541a37cbddf141d +dist/2026-03-05/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=1747e64a0b09934cc79704be3ff9b10f505c159c0f7933e3181946adc4f8dd01 +dist/2026-03-05/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=fe62c8b9c1bab4f864ebd011f6b2a40ae6a9f7d6e060117972ea751eabaa6156 +dist/2026-03-05/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=a7c611051bc24de59e738d52b25f812d6faad70c2e0ee57af93828b19b596855 +dist/2026-03-05/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=b98fe0cc6e91e3734ffde23e49bebf303c6311ed6d8a213e653d21ed7239d8af +dist/2026-03-05/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=bd6e61c0c69266fa5230ef9a29beb540d619c5cd426295139a4de504535aa76b +dist/2026-03-05/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=d9a2e69d9db54f3a53f178fabb069764d1eee274e5b1c6b940ad652eb2d100ed +dist/2026-03-05/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=bf9515a722bd9db2c7b481341fb624ebe2205a23ef2d31967a07d36ab65cf96e +dist/2026-03-05/rust-std-beta-aarch64-unknown-none.tar.gz=ac686397718764da9b5c05302ac240aca033d2ea79f41f5678b793188d372566 +dist/2026-03-05/rust-std-beta-aarch64-unknown-none.tar.xz=f173adaf5053b9a552b7dd4eace60116a9fbb539acfb2de5409328ca5614adb2 +dist/2026-03-05/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=3f8e38efd4d5a688dfa543b600f54bfb1231203d3ed48d9971be6bdaddcb85d9 +dist/2026-03-05/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=3dae31453f6b3c16cfc753b066fe10e27dd93131411afc3c781997d5358bca7d +dist/2026-03-05/rust-std-beta-aarch64-unknown-uefi.tar.gz=930dfd3a113977cdcc58b41e6174ed6c5c09046f022a95eccca503775359f093 +dist/2026-03-05/rust-std-beta-aarch64-unknown-uefi.tar.xz=55211bd94f6837282f6c7ca94ac10ab94090252eede5f48d508d82efb0d0f505 +dist/2026-03-05/rust-std-beta-arm-linux-androideabi.tar.gz=c53336c95ccab792659681c9ad1234bc100e30248c80aab6cb68943625d74529 +dist/2026-03-05/rust-std-beta-arm-linux-androideabi.tar.xz=ee221c73b41a4735cd1304aaccb877180f2caf7f539209d948c01b6d00f8b6c6 +dist/2026-03-05/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=745555f9758b4d40f2ebcc9ac7610ea509a2dbc0db1ee47c878c7d630af1309f +dist/2026-03-05/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=8d8e5904ad91585b66caed20f715b345002202087ad0347179f4f093dc67a26a +dist/2026-03-05/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=d245629790c3d9cdef797754caa2c0239df7222f583b4b2ebf1ca7126c2e17cb +dist/2026-03-05/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=cc0fe3213fed72ca7e21807d4e063a815597d3152d209d45c23eff508d9adeab +dist/2026-03-05/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=3471b6e04ebcc9b9fb768efdca01c7c0bddb5a52152941fc95ca576506307a66 +dist/2026-03-05/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=d59e7e19afc0b4f9a893fa94d41837710858d3fd0b61be28f79ec64445a83d75 +dist/2026-03-05/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=b3cfd42b65d7728b7448970d70e5db8ef33a7c0106be94a37b1c77d0a2d98f15 +dist/2026-03-05/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=a89ff710cce70389e619c880f38a8eee7b6e46b3ec72cc812909f8707bb22418 +dist/2026-03-05/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=dcdf512d4063007732b80ff57f9f3aaa834560a5ddbdfc536c68092f44a3ef5c +dist/2026-03-05/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=7cb83f36f9eb618fa0e3e5b06b4180b2b28782f86e6ed0eca7f3370daa07537f +dist/2026-03-05/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=fd5bf879cb8097aa315c24afb4349ca2d72d10399db97cbf8c3028c897e0046c +dist/2026-03-05/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=fe9d351f6326d74068e97b5a04a63a8d5ad3506ccb29a0795d3bfba8b9218e62 +dist/2026-03-05/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=28d8fd235126127fb4079d877d512f119e12ca5c100986e9105bd2824c07017b +dist/2026-03-05/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=5e951c5635ff4ecba02217bcd43c20960cc1eea2e900072c946731315e8afdd0 +dist/2026-03-05/rust-std-beta-armv7-linux-androideabi.tar.gz=966a7bc22853ccd129ebc7e4b53bbe46fd41685dc1fe55b0e98ead75642b9ad3 +dist/2026-03-05/rust-std-beta-armv7-linux-androideabi.tar.xz=806d1c02e46ef5133867cba9b60c3458bf020d1a5663a9686b6bb34f3054d376 +dist/2026-03-05/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=59213be7a4cf1745cb285e93761ec905a6f2bec6e061c48069be628415630657 +dist/2026-03-05/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=ef4b990defa339048b9c192064f23c3d89d11e3899a99c5c7312d76dc0abd605 +dist/2026-03-05/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=d396e64ec5ac1c3f059b17714175031c63a019a01f397ee0664d7ca5ec5ab9c1 +dist/2026-03-05/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=f496630dddab81fcedb318b65d1863f8f172c5c6f11a0e70ba14c0d64c764db7 +dist/2026-03-05/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=4c6a244e898e2f02e480743b7e26d2f39c399678223d36b1a62fb3d5fe3c7bc4 +dist/2026-03-05/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=23067c41381cec9ab7daab8f03698405cc23665a1ec75993d8dd9c37c7fcd21a +dist/2026-03-05/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=bc88e9d6c2a8260028563e453a03cf247204ac94a5d0b3705d7f85ca07ec82b0 +dist/2026-03-05/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=7fe689bafeef2d918821741d8bb5d9ba73712a5636cf6d1ef0e8783ebd73fc15 +dist/2026-03-05/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=e7d2973ece8536dd2c552881cb1d7af5c8ab9155a263e4a87261ed8a3420341f +dist/2026-03-05/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=18cf4eb6f76c1a685aac70d43464344ecb92f0a44d8ed9186826db737ac7763f +dist/2026-03-05/rust-std-beta-armv7a-none-eabi.tar.gz=5621e2c815e801c43f9541018cf86f4083e322c5b9d03457461a2d657d9b8f18 +dist/2026-03-05/rust-std-beta-armv7a-none-eabi.tar.xz=55875228a72dc62a4bba1c4696dd254ed5b9728da49df7bf24155a4401ae67d4 +dist/2026-03-05/rust-std-beta-armv7a-none-eabihf.tar.gz=37c60688dde6b2ffe7661984643f7bc853424ce50272265fe0dfd5baf89cad33 +dist/2026-03-05/rust-std-beta-armv7a-none-eabihf.tar.xz=72397894682178a725e8e3876f62bc77cfdaedd8180fb17da52b042acfb28994 +dist/2026-03-05/rust-std-beta-armv7r-none-eabi.tar.gz=b8da86b3c3c635fed5e5a7a83ae922d651dafcc2d74873a335cb669e7e773a25 +dist/2026-03-05/rust-std-beta-armv7r-none-eabi.tar.xz=577a8318914714b870ce3045cc5e68eb98b193d54b109ef24ed9af1caeeee727 +dist/2026-03-05/rust-std-beta-armv7r-none-eabihf.tar.gz=07d8e3c7182cc3f8b985ccaa6980ae2e1ed855d4e3d3fd7119ae77fca2feb593 +dist/2026-03-05/rust-std-beta-armv7r-none-eabihf.tar.xz=47cc02692e03192420a6994897051aa0e33d9940a6fc780c3de270e4b49deda3 +dist/2026-03-05/rust-std-beta-armv8r-none-eabihf.tar.gz=382aa1b05857096d321d5df98b683c99c10e866bada218190bdcf6a223d15cba +dist/2026-03-05/rust-std-beta-armv8r-none-eabihf.tar.xz=17010738cb68f5e545a45e899a64c2a29716d679eb897a40356d5aafa3327228 +dist/2026-03-05/rust-std-beta-i586-unknown-linux-gnu.tar.gz=e56d2535e50e6fe644d350d7d98ca0afd7a0a4ee417b9aa18f4807c2ace433f9 +dist/2026-03-05/rust-std-beta-i586-unknown-linux-gnu.tar.xz=3643be85aab61baef1f841199d624b6a74d8eafa395125731d0b39d4b62acf18 +dist/2026-03-05/rust-std-beta-i586-unknown-linux-musl.tar.gz=b8c36eae5032af0cce2dbee4b97dda5790849f31ae03daef3729aaf52f1a5080 +dist/2026-03-05/rust-std-beta-i586-unknown-linux-musl.tar.xz=4fb8a1acefc27c554b7f9223ae56a267e732c7616e4932b9f9785c490495f49e +dist/2026-03-05/rust-std-beta-i686-linux-android.tar.gz=f9d5f6d20f0615afc0bfc93798fa28afdcbdf97174686961fa82486001037b4e +dist/2026-03-05/rust-std-beta-i686-linux-android.tar.xz=75b0977f263a1f6157798343ce94f2450ef6959f830e1088e9db4e17aba57090 +dist/2026-03-05/rust-std-beta-i686-pc-windows-gnu.tar.gz=58e534f8eea22babdf368655bb279fc9e31d06e1155d65a18912d1c993bc0c2a +dist/2026-03-05/rust-std-beta-i686-pc-windows-gnu.tar.xz=7165c674d0f01a288ed0a8f555dd860b54c4b943765a74ab6ff25f60234ff91a +dist/2026-03-05/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=19c5cc06bf4114967c025a83e786d70496b2f4c04b6476eda1cece711342e290 +dist/2026-03-05/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=699969c886699a54f1129928ece1620cd9e5d5a851836ba791c25ea6a7ded1ea +dist/2026-03-05/rust-std-beta-i686-pc-windows-msvc.tar.gz=de2f6107d3e160f7c764fefd5845d6e39af3efddc82a0454ddb240b92e296867 +dist/2026-03-05/rust-std-beta-i686-pc-windows-msvc.tar.xz=3076bc9d5159f2ae64531eab5e4586d8b45a6621fc0ffde36dd89c6a0993d7d6 +dist/2026-03-05/rust-std-beta-i686-unknown-freebsd.tar.gz=920711cafd7d9474230a74e1432e9d3bcf10f34dacf506bc7c0e0cbc52f2490a +dist/2026-03-05/rust-std-beta-i686-unknown-freebsd.tar.xz=b246d3036b85ec9c0633ec464e6009eeab80d0ebb913ed298e5feb5b5d5e5f51 +dist/2026-03-05/rust-std-beta-i686-unknown-linux-gnu.tar.gz=bce452a29f1be4a042979de40c4aa3c7d3a1f4220d15aa86828fb690c7ec09f6 +dist/2026-03-05/rust-std-beta-i686-unknown-linux-gnu.tar.xz=5477b47f5b02951ede4977d2e1e632fdd78ff62b7d247879a87a2ad870d453bc +dist/2026-03-05/rust-std-beta-i686-unknown-linux-musl.tar.gz=c96f7dbd9413ab90e4392eec66431a11137961d12e4e0f3e28418ac8aa1f8966 +dist/2026-03-05/rust-std-beta-i686-unknown-linux-musl.tar.xz=46be6fee5b16eed761730038f4e4b0c6d25327882143f56ea2f409bc6c966cc3 +dist/2026-03-05/rust-std-beta-i686-unknown-uefi.tar.gz=f34bece05809ffecb6f452b8311116d6badd9500ee2232df20d02908e5c1370c +dist/2026-03-05/rust-std-beta-i686-unknown-uefi.tar.xz=a66afe0de861f7604448855296268cdaac51f299b443f625503195d0bc32774c +dist/2026-03-05/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=46f67b6383a7c95dab4c72485b62f2609bde64631190efc20368034029782e75 +dist/2026-03-05/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=f557985f9131c9f73c88f6b5825e104ec7b9288f34d072a29a4be9e9a9e2fe79 +dist/2026-03-05/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=e42191719d3362f473b9f59d067787e3b2483d8abc55358b56473a93d812db8b +dist/2026-03-05/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=8556ff0160fec5905d1329aeef6ccc66eb757077840782f4171c5cfc7dc05e0e +dist/2026-03-05/rust-std-beta-loongarch64-unknown-none.tar.gz=d9fd31dc58820a27a0e9b3b46ff010eb4b22ca69e95eacaa5563a41389817627 +dist/2026-03-05/rust-std-beta-loongarch64-unknown-none.tar.xz=672227214c74df4805879204fb97ac4f1cc7f1ea3e6afd63a238265311b09b39 +dist/2026-03-05/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=aca39f4f45d7c15bb3e3ced00d76e5897ba11825dd755b1ef3655ff1a972ff3a +dist/2026-03-05/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=3ed71bf5a7595ee98ea2b3ab4cab5d8a0a35c69888517daabb1156104f631d3d +dist/2026-03-05/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=45ba5e51950abe8b84a4187b7af01a48580d84ce4c27fd77ab93f193daf64013 +dist/2026-03-05/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=58d6e3c0159564c96767fe703661251f63b0fbb2696d8e508b1bb0bfa0d72614 +dist/2026-03-05/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=2cd713940e33a0aefbcc4d15c480b80b9595e8742144d5c08fa90ee233facc55 +dist/2026-03-05/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=cf3b4c722305630d4c3334c35ca862e349b88707e80a394a26acb43eb4408893 +dist/2026-03-05/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=14b1686a0ecf61e22f0993ddeba23209d01195ddbccab05972a6caa7edd2e2dc +dist/2026-03-05/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=7931dbf858d47b3b0dbf5c5b5d35325fc95316c75fa760526de64ceeec8cba5d +dist/2026-03-05/rust-std-beta-powerpc64-unknown-linux-musl.tar.gz=7c1ed0f1b20baf2d6b5647ac8813d046db03af9bc2f99e602665af84f28703b0 +dist/2026-03-05/rust-std-beta-powerpc64-unknown-linux-musl.tar.xz=0fa036221f2ff65621c5b862f739553e750bff0b0afd02544c30f3a781908b8d +dist/2026-03-05/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=e3cda0ea3bdbe09a05df7e9f7a7e87dc96bc92f5559286679d1cf793ab9ecfd4 +dist/2026-03-05/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=1cd6d88de0723a3cbf9fa2644aa849d75e13e6a20e9b5aa59a974bd5c0e523a0 +dist/2026-03-05/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=745111404f52bda5dc4a5b9494fb68c050f82d9f0863d91da9578291b881d780 +dist/2026-03-05/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=6cd441a90549ac68ec0b4882fda352726391cf14f772b9daa5bc08056447d49b +dist/2026-03-05/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=4a510bdbfdd74781f92c65a87146efc75d48f79fe0e7a9988b8ae82d74916de5 +dist/2026-03-05/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=2dcfd1be33ebce0d677af0cde3d04ca4d27dee380a334cc7fa5fd86c751d39d2 +dist/2026-03-05/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=a247a3dfc51bf8d13a27b443a1ff6731c45f5cccd905341fd1d69834bfa8403e +dist/2026-03-05/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=990243746c631e1e5896edc278cbca5ca4a1680100495e5d6f8e05823ac26249 +dist/2026-03-05/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=3043d783c97867628079247d16ec9368bdef4e526643b4de7b89dd274d615c96 +dist/2026-03-05/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=2e2ae7d6e73ce1ef4638ac3f637bd71ca7e382ea4af9a71de0a6b1d406f55970 +dist/2026-03-05/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=600fe7eff97e1f351140c006652f42345c296580781d15e5cbb183647f2ac81e +dist/2026-03-05/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=38317b15436642e33c4f7e88338896ed863df8eda59af0c917df4461a57dd655 +dist/2026-03-05/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=0090d73f5817a87c05406046fd5bd1f9c8a74f5af97acba0af5118fbb255be85 +dist/2026-03-05/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=44f5371edf183585e138520e7bb14993654f177d519446a30fc6c3dc0e8dd407 +dist/2026-03-05/rust-std-beta-riscv64a23-unknown-linux-gnu.tar.gz=138dec05006d15b2def37de03894966c02f80806153050fdd618c7c6803e7dfb +dist/2026-03-05/rust-std-beta-riscv64a23-unknown-linux-gnu.tar.xz=b938e120861f65f23d1857dfcd4f04a8476f37a55ce7ee5fb35ed160faf5f66c +dist/2026-03-05/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=7be51115ae17b46a0b5f5df77dd73d507d1b5731df268a7f308e87cba5f11743 +dist/2026-03-05/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=9077843e3719c10a126b6a74bcc94272d9c0439fc3b74e356c585cb57d8ce1b9 +dist/2026-03-05/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=db8e9eed4e00bf763f9fb406578395b73b8f81aa7c096537a8a5ea933de1afab +dist/2026-03-05/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=88ab1dd67b4beb703d06ea02678e90903f1f7c20c9ba2b11bf3b4fb833f2111c +dist/2026-03-05/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=e20c786852a0c0d33e39c145ddd3bd35ac7fe3c445d2e632f172b58459a2cf4f +dist/2026-03-05/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=46c1a56d2eed59964262c04c724e8e299c1d7e835dfd73d5b7b48c96300fa9fd +dist/2026-03-05/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=fbef02ac4da131b76ff6bf32e6ad174e3f5bfa7701380b6fc778a2a457a8266b +dist/2026-03-05/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=d129fa880bd7667b03f6526354d33b25949ea0a426f8d27e4c62bd839c947b29 +dist/2026-03-05/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=a379f60908528d81c6d9c974ee51815ec9c784d9a0f7911936c9e8482fb1bf85 +dist/2026-03-05/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=39bc843baca7c9a0fa6d0474211707f250c1367bd118ad625ce17831a8811e36 +dist/2026-03-05/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=5f352b2d16abd3d1f907803c2e778452827a150c4e957f3883164333f37af51d +dist/2026-03-05/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=99339ef8b8bfadbc8a5be2328afa80e562c445bfb8617f0f76ac382f9f1fa5ce +dist/2026-03-05/rust-std-beta-sparcv9-sun-solaris.tar.gz=85264c7227613c2da8ce02e5a1fd83822b29c7958afd0e43525f5d5117552ff1 +dist/2026-03-05/rust-std-beta-sparcv9-sun-solaris.tar.xz=a0dde20f6796d574cc856bd25f83b953dba0e6d349e88c69d93ea170ee622e32 +dist/2026-03-05/rust-std-beta-thumbv6m-none-eabi.tar.gz=8d9933d10602cbb923a3ac4ec1f8fde22eb68a5ed9ac4f44cb8253ac90d0ab3d +dist/2026-03-05/rust-std-beta-thumbv6m-none-eabi.tar.xz=d150465828f4e14e75b5c94cb8aa246367f6a77f9c9d7c3e6646dbf0427c49a3 +dist/2026-03-05/rust-std-beta-thumbv7em-none-eabi.tar.gz=69806cf3c03236b2db21dcdbab9b3f259fb9dd0aacc183082579333deb0469e1 +dist/2026-03-05/rust-std-beta-thumbv7em-none-eabi.tar.xz=19c2348369984cde9af45767e7c928e483bf3e1d6867dc1f544bc6dd0493c03d +dist/2026-03-05/rust-std-beta-thumbv7em-none-eabihf.tar.gz=076f320b0e2b97c686771551b663adc27bcc3157a93f4cdff685865ca0194ef4 +dist/2026-03-05/rust-std-beta-thumbv7em-none-eabihf.tar.xz=878e4be6803cc6859511b9934c1402598727f1a303460f81aeb0d46087b15759 +dist/2026-03-05/rust-std-beta-thumbv7m-none-eabi.tar.gz=6bedf4e9200e569de8b7e5bcf02dace47ebf7f0b8a085c1e7fda62d9668538ad +dist/2026-03-05/rust-std-beta-thumbv7m-none-eabi.tar.xz=a24920a8099a9fa06610b1882382a4119e7b122b853de975fceaa9ef84c89fbf +dist/2026-03-05/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=f3bc9fc0b2a21d8d978620204eac013c2391433131f1883713b88f000ab20cd7 +dist/2026-03-05/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=d8617595c8cc3863bbe85b0dbd6ead1f27d7330032d2940d4c56617641a919d9 +dist/2026-03-05/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=28461e5f6a6de05d4f713c650a9a6410222821daf8b8f6063460746dda673bbd +dist/2026-03-05/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=f34f81b3b83b28f4556e115d54885f0bf78a8cb99091651114d1a092486ee3b6 +dist/2026-03-05/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=f091d4713aefe32ff57514d9604e690cd76e294e497c8e3d62a8d16a1b01c483 +dist/2026-03-05/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=0be6ef7df0c74611cc424a3123be7af7457e42d4b0372fe61a781e5021a156ec +dist/2026-03-05/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=4176487e2eee68463a2014173640c92dea7a3ea14765c46dff29004b1368cbb2 +dist/2026-03-05/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=b5c6a05971185a875fce49b4261b642ad5f68a4234f435d036baa33b2f343345 +dist/2026-03-05/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=1cc3d65237cdf408d230e0a4e14dac778fb1a4a17c14873b2b7247defbc3f26d +dist/2026-03-05/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=9d22dcba260431b746eeb5bee5d3e3f056261106fedac3ce2c324a79a036e69f +dist/2026-03-05/rust-std-beta-wasm32-unknown-emscripten.tar.gz=34a0f151d8df4a1fdc0fdfb736322fa8f73490f53c9ef707f75676a6777b11b4 +dist/2026-03-05/rust-std-beta-wasm32-unknown-emscripten.tar.xz=39de807d96bf396e504dfc158ece3f181393b6665815626ed80454876096de8e +dist/2026-03-05/rust-std-beta-wasm32-unknown-unknown.tar.gz=9a81193806c6a612bee692d438cb2c309c0668d13ef37093a36509268229e73d +dist/2026-03-05/rust-std-beta-wasm32-unknown-unknown.tar.xz=db8fc69516f290d40247c912ff1e1a5cd9171fd0dcdcfc129e13f1af41ea486f +dist/2026-03-05/rust-std-beta-wasm32-wasip1.tar.gz=2c2534ac71433b9a34f10e19678b96b8058c92a86f071d53bedea9b0bc489fd7 +dist/2026-03-05/rust-std-beta-wasm32-wasip1.tar.xz=7a15fde2513490d977d2dea9bfdb80bb55f30caddb0c0b06334b5758b26eff42 +dist/2026-03-05/rust-std-beta-wasm32-wasip1-threads.tar.gz=bff299121c3a8cda52f3f43a5529d041b2bc995365e6eaf3b986926672eeffd4 +dist/2026-03-05/rust-std-beta-wasm32-wasip1-threads.tar.xz=5195e3672661961670f44b0d69396fa1695f1a6a2127f9f56ea31df5846f4397 +dist/2026-03-05/rust-std-beta-wasm32-wasip2.tar.gz=659bad1ef10f3c5b82e9894a306acfde6553c44217a74dde57b25e29dcfaf5c9 +dist/2026-03-05/rust-std-beta-wasm32-wasip2.tar.xz=05e263489efbb721bb66709a02881458a402fb4377267b7381caaa652e6306b3 +dist/2026-03-05/rust-std-beta-wasm32v1-none.tar.gz=b0cfc9a70fceae519bab310245abbdee0fd2f48048860a2598cc1e0a207059fd +dist/2026-03-05/rust-std-beta-wasm32v1-none.tar.xz=33f52cf94437b72cc73255b40fa7631cac2fb29bed9d35f68c2e9335ccc13733 +dist/2026-03-05/rust-std-beta-x86_64-apple-darwin.tar.gz=27ba2cf7756d8564604a76cee8e3da28956cd3742a7644baa94edc5acdb67270 +dist/2026-03-05/rust-std-beta-x86_64-apple-darwin.tar.xz=038ea8ccb752f7ee3a0b2373db7a474624a13685c66149f1998aff0d4c66cd40 +dist/2026-03-05/rust-std-beta-x86_64-apple-ios.tar.gz=24034142c44ca1fea1c8198f49bc4516b92b529e2f608fe93d0cfeedf69c1999 +dist/2026-03-05/rust-std-beta-x86_64-apple-ios.tar.xz=0d4fa173a7424f09128dcb8acd1f2cace73f0e6935be5b1f018282853986285a +dist/2026-03-05/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=e2a5e41d8ce11f82e1e243f043a606bacc978e9847d38b042b34b9957540ae9b +dist/2026-03-05/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=1d37957f9d8bd35a5395e348a6d6d8304c4f6b4b3154e540ede580533b6da992 +dist/2026-03-05/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=1c5c3ecfc5a5d5e0e7b186e89a871cbe836b0817b7aa049ad67ba792a42b18c3 +dist/2026-03-05/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=5aca860c6f93dcb9fc148a4dbccabc6cb9680cad686c42d8253ad6328e26cd7a +dist/2026-03-05/rust-std-beta-x86_64-linux-android.tar.gz=3ff05c6013c8313b67544006b7d8eda924f3b607bcfbf52753b6f2ddd2e57778 +dist/2026-03-05/rust-std-beta-x86_64-linux-android.tar.xz=6c3941c8b3f75f90c49d3ce68c1325b6bf2a2fe300ecbc927193f9fc732beab1 +dist/2026-03-05/rust-std-beta-x86_64-pc-solaris.tar.gz=d6a69b5ea4ec7f78c27b4507fa9e5d2f10364e707de76facaaeb9c2d151bc9fb +dist/2026-03-05/rust-std-beta-x86_64-pc-solaris.tar.xz=266796034403f625824277f0e0b375b32c0d00b121449abfa7a1f40cf9eb37e0 +dist/2026-03-05/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=34911f2eebe9fca1264f72cf8d8ee6c030fbe53680cb12cb712cf15ff03a1616 +dist/2026-03-05/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=50bf5599e34e355ecd813481755f749b78761d061a49d99644bf2bbed0688254 +dist/2026-03-05/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=25ba06cc262257e71301cc0e165f60028fa16aecf65229ccab64f72a2720e603 +dist/2026-03-05/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=fab550da3f00d57eceab58704078b56efd501e068ddf32ea48b9dea117f6414d +dist/2026-03-05/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=98d3000a287fb7affc240f85f4ce0a9ce679ce245b5ca98acc7f91bd17b5d522 +dist/2026-03-05/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=7a8d2216231450594439863e2aae5fbacc35777d33ba7138a5ac6a9f748a6090 +dist/2026-03-05/rust-std-beta-x86_64-unknown-freebsd.tar.gz=c9ec7f794ad24b4a7f6de7c4721438925a895f0a151dd6dfd8730bd43fcbfabe +dist/2026-03-05/rust-std-beta-x86_64-unknown-freebsd.tar.xz=bf727d7b87bafcc2cfdd45a61e9b728a73024cd141fdc37dcfe9731545645e2d +dist/2026-03-05/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=bedcfc98f1bfe9c190e2b62d6b64fbbe13dcf14a7bf0b0295dc0e8f4b78148be +dist/2026-03-05/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=2a4b98d9f573199cd3fbf1bcc0b1c1f1a575bea95a452b225e153c20041a1dd9 +dist/2026-03-05/rust-std-beta-x86_64-unknown-illumos.tar.gz=ec8fe5df05705fed6426921eeeb170a6e2d02c0f6a0edab14a27c8a2a2e94baa +dist/2026-03-05/rust-std-beta-x86_64-unknown-illumos.tar.xz=c44b9d327d7800bd72a28c5f27aa5e91caffc497e3e23bbb46e1264bd9920d7c +dist/2026-03-05/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=775c2706672c106587f3f23e5b8871f8e1952be852f07a6bbd9c3db06eb9bea3 +dist/2026-03-05/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=bc2ffcdf3a4eae93774ac99397bde4d31a1aa0379b86836d01967e300aad58a7 +dist/2026-03-05/rust-std-beta-x86_64-unknown-linux-gnuasan.tar.gz=5ec7c93e79db0166f4dd9c32ab5c3efdf85e49a38315754ed1836a3b817926e4 +dist/2026-03-05/rust-std-beta-x86_64-unknown-linux-gnuasan.tar.xz=1f946d69a66fa343e4b4be30ce6e65ff09b74ab1fd6f0742977b49e1950585b8 +dist/2026-03-05/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=8fcb20971f563fcc2b935da6f2c93bc533f48dcdf413cf1319ff9cc857d041cb +dist/2026-03-05/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=7b4172c848472dac3a7197b2ae9556929723849da36dbb9318f50dfd6faf85c2 +dist/2026-03-05/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=0208ebe70f3328e386a80c76b8208109a44aae91201313f58d794478540e3cb0 +dist/2026-03-05/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=cc0d53793d75abd53be4fcec42f07dd2c70ebc5ac3570df23cd51cdd216279eb +dist/2026-03-05/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=5d777d1abdac2788b656d1eef75cad1be06fbd36c11c0451581ea4e0450a2b01 +dist/2026-03-05/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=2309970fd19d7aaf3eef71791f372ddad7b4bbe9180735e3294905ad5362b035 +dist/2026-03-05/rust-std-beta-x86_64-unknown-netbsd.tar.gz=f5a6a65f5bb5188aa04a60a41a61fe393342bf16c653bf0000a30195d3ee63da +dist/2026-03-05/rust-std-beta-x86_64-unknown-netbsd.tar.xz=b12f4589d40c99a0b226a2783d8fc3e1e65e9cd53d9cbd25833e7381e05aceaf +dist/2026-03-05/rust-std-beta-x86_64-unknown-none.tar.gz=acb373525161a1a4a4ea598fecf17cbe6bbbb05e7fa8e4caad55fe5a8307f772 +dist/2026-03-05/rust-std-beta-x86_64-unknown-none.tar.xz=6442f6794054a5b0aead34aeb0ca128490cd779a1884622710ebbb6270d1f206 +dist/2026-03-05/rust-std-beta-x86_64-unknown-redox.tar.gz=b5e739c2ecd4dae331d66c36dec8af0314afb08db7685ad5033f895660ba5a36 +dist/2026-03-05/rust-std-beta-x86_64-unknown-redox.tar.xz=87e8ac22d6be02fee1dc399ab8481e6e060c663e28262f9ec22140b2bb16a71b +dist/2026-03-05/rust-std-beta-x86_64-unknown-uefi.tar.gz=5806c818b6fbd642fd781c12d41118fc26a188508537d7f682cc37451c33a8a1 +dist/2026-03-05/rust-std-beta-x86_64-unknown-uefi.tar.xz=62b76fd0a9d2f24eaec6f56dd0f27035b216092baf83e148570da33c6cd52d8a +dist/2026-03-05/cargo-beta-aarch64-apple-darwin.tar.gz=57a6b7b1e222528ab2ad8b66d6daf4a5990a95ee8ad2cd5f4c83bac4339f4846 +dist/2026-03-05/cargo-beta-aarch64-apple-darwin.tar.xz=449cdd485bcb58cdac7503020ba4b2ef732f3e7278815344fe2d777903bdf48b +dist/2026-03-05/cargo-beta-aarch64-pc-windows-gnullvm.tar.gz=d5beb91b8342731bac0361a9d15231b33608e73a2d28bc6b127e5625e4bffe77 +dist/2026-03-05/cargo-beta-aarch64-pc-windows-gnullvm.tar.xz=a3430d8099bb2c143bad2975cf49e06fa033cea11efed2fa43fed40b84757672 +dist/2026-03-05/cargo-beta-aarch64-pc-windows-msvc.tar.gz=8a82b5a203d3237e2e30165f03528128d3bb41283d678e57ad9de3334630b619 +dist/2026-03-05/cargo-beta-aarch64-pc-windows-msvc.tar.xz=c8a83afb8c110453301ff7f601019740c23d584cb421c940704ab185d548bc1d +dist/2026-03-05/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=860889eae0f10c3143c2dcfba5b35d1f7ded8604469e98042999a7bb78a5d6e4 +dist/2026-03-05/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=3270668bfca382dec633acee3712db180dd7c3b630bb5cc9bc286b2244ae906e +dist/2026-03-05/cargo-beta-aarch64-unknown-linux-musl.tar.gz=38d13b7cda22ef1ddd3e79c1df45eb77948849c7c68c91ea37930f3c13f310d6 +dist/2026-03-05/cargo-beta-aarch64-unknown-linux-musl.tar.xz=dbdefc6c3dce5ce89a8e0c19f827f83a0210a7f1a201452df1bc0dbf3d426c54 +dist/2026-03-05/cargo-beta-aarch64-unknown-linux-ohos.tar.gz=b76846af95a5ba516658530a929f82ef19549291c8cd7eab4e34ed1cb2aa19b4 +dist/2026-03-05/cargo-beta-aarch64-unknown-linux-ohos.tar.xz=c4ee178a645c51710612a363486c38581b04cc956bd5c1d1f966d1a66ba2ba59 +dist/2026-03-05/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=13e6504ac6059030b3e20c7a2a60902113625e7f1b313abd7a90751e44fccce2 +dist/2026-03-05/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=d232746defa606f9a37968da702988c81554183abb7703703dcc20cd5211b51e +dist/2026-03-05/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=b19d4e616e98901ac089cd023beffa7606815edfc7e5a6abb94a38823eaae3a1 +dist/2026-03-05/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=814634baf493641407e0ed443938283159ebeec3530304e3c05163e85e96ac9c +dist/2026-03-05/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=5cd5bfe43ef31e7dfad23beb4510e311c098057a46457ef8d123cb155e2370d4 +dist/2026-03-05/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=337087164aa614eeb8272e06a5414ab7d15682f7f4d7d4e12f5536ae704adcc3 +dist/2026-03-05/cargo-beta-i686-pc-windows-gnu.tar.gz=74ccaa0f31de099e43be9dfe0a7a0f00202b371954adaf4a20e2ff64a6f95788 +dist/2026-03-05/cargo-beta-i686-pc-windows-gnu.tar.xz=d3bbe5570c9740adfb0409fadac59a8bbcbbb52465d292e35244a8a0bd9e0121 +dist/2026-03-05/cargo-beta-i686-pc-windows-msvc.tar.gz=6087ac3a8896e7b75b8f1636a2cb8620e0d1ce486e0714e5644acc4e49e478e4 +dist/2026-03-05/cargo-beta-i686-pc-windows-msvc.tar.xz=a5c2a704fed42d167b6a5e7ca950812aefae0d9348a00870dbaf10cc0f85752d +dist/2026-03-05/cargo-beta-i686-unknown-linux-gnu.tar.gz=4ac578cd52e61e3952d64d6b68e19bd9646c61cf5b809e225eecb49dffa21879 +dist/2026-03-05/cargo-beta-i686-unknown-linux-gnu.tar.xz=09e4af4d94c6b1625f4e747773f9a18cb13d1e457b90193647f9ba13f67d4c5d +dist/2026-03-05/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=11771e0ed31948bc3d5ca53f71390913b778cab0a315b3a3e5a4610d3ee6541a +dist/2026-03-05/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=57ccc21558e8da88dab76215ab8b16b167c3206a8ec33ae17c8a918e1f13d062 +dist/2026-03-05/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=421d40079b804c4653da7b6ec9d54151deaeb80426054a17c030687b581b275c +dist/2026-03-05/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=d93be442c7f8dfea9ddc2d9e39bfcd5dd4a53987050acecd9582e012f427cc66 +dist/2026-03-05/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=a0fc2ced0638aff22501acd64064fb9afaa2c86771161dd3e0120d67a3124062 +dist/2026-03-05/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=6f5499e146e8ee4b70c27e28c98a7c33cf5848fa65a820ce1e0031142abc7198 +dist/2026-03-05/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=7b9ab95407e7b434382db442bbbc5702f68bb906098d67a065e02cdc2e063032 +dist/2026-03-05/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=93adc6abc694c59b07794839e6a39576c7250da4c4e20cf79f328217e960bd33 +dist/2026-03-05/cargo-beta-powerpc64-unknown-linux-musl.tar.gz=1bb8e52c3ea595ef87576cdff3fd95fbec0d2b780de6ffcbfca7c1c375f91e94 +dist/2026-03-05/cargo-beta-powerpc64-unknown-linux-musl.tar.xz=cc286dd918dccc2116c9b9e884cb8c439ace42a81fc183d6070d6efb728e7acf +dist/2026-03-05/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=194a1542da15f0ea8a55f2b010a4f575a64c48233534b6e534e15fcbefea76bf +dist/2026-03-05/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=1370d4178aeba0877508b2fd5578577c651df9e6b5bdccb613a3ecbb0110d4c3 +dist/2026-03-05/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=3f2c01f54dfd209e9a2c67efab0dde8027316c3f8a89a6a62324a02962d076e8 +dist/2026-03-05/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=d86ee2b482cd251e9a0dd31bb3a444fb73f23bcb829802e5f490864de15c16ee +dist/2026-03-05/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=56c22f5dcfd7c248d99f5333cfb8cdc1c09f918dee31eb699b510778d447e86d +dist/2026-03-05/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=2ef47d8a112bdd00be5e40a1a96dc53ab455254b3d9d38d67053aed7761d0b42 +dist/2026-03-05/cargo-beta-s390x-unknown-linux-gnu.tar.gz=9556a3bfa4204454be0ceb75fd552ef833501a2244557d3a77267cb6b4a9dd95 +dist/2026-03-05/cargo-beta-s390x-unknown-linux-gnu.tar.xz=c1ba7e3f8fc09f014bd6cd7248cbe01a1bab6648cb21352109d75d7e6393f341 +dist/2026-03-05/cargo-beta-sparcv9-sun-solaris.tar.gz=6f14015a29be4f56aafc8246038abf7547e2fc703fdc49045360dbde27d7ef19 +dist/2026-03-05/cargo-beta-sparcv9-sun-solaris.tar.xz=6cbc9b545e39f3e6485576557bcb80a28a888fd70ef5a64a66e3185e9c8202b9 +dist/2026-03-05/cargo-beta-x86_64-apple-darwin.tar.gz=c77c0ad2a353f6b0240b85cdaab3a9f568d0f998a25276c2d8d819ad55699f18 +dist/2026-03-05/cargo-beta-x86_64-apple-darwin.tar.xz=59ac9372a168a88e2b1bd6cac129c469957e2bd0d5bed93a4c6f0528106bf4cb +dist/2026-03-05/cargo-beta-x86_64-pc-solaris.tar.gz=4eb565eb6dab8823cb5c61596fab232d24d7c841bce34ccb6b35eed1a0dfc9e6 +dist/2026-03-05/cargo-beta-x86_64-pc-solaris.tar.xz=e8a97246c632bd492cabc27a9ff09edc058b20db964db378eac93846d6d10920 +dist/2026-03-05/cargo-beta-x86_64-pc-windows-gnu.tar.gz=e2703b785aa0a3091a06b4273aadcff174f5e6c4b4117d10cd9e7f14539409f2 +dist/2026-03-05/cargo-beta-x86_64-pc-windows-gnu.tar.xz=f10b1254e240a3864137c03257ee674a6e8fb4b0d9dd50de234db001034ff705 +dist/2026-03-05/cargo-beta-x86_64-pc-windows-gnullvm.tar.gz=41f7d406df80d4c1a0abf610498a8d224174ab2706e1946b6a15663d6eef65b0 +dist/2026-03-05/cargo-beta-x86_64-pc-windows-gnullvm.tar.xz=dcf721bcc2ae8d3c08124317e45f51b0a41925b6e28a2046f2111c7aa983d4ce +dist/2026-03-05/cargo-beta-x86_64-pc-windows-msvc.tar.gz=aa55c73d9e1f90378fbb61c6d1a487cd983eb5776f6f2e4fd8120b8341543420 +dist/2026-03-05/cargo-beta-x86_64-pc-windows-msvc.tar.xz=18062197a5422213c8aaf1cdac396145e3773f298789bf364f9665d6ef254f2f +dist/2026-03-05/cargo-beta-x86_64-unknown-freebsd.tar.gz=ae7bbbbbe966ea68e13b1976e745c782ea5b649a7c541596bcb44afbe4b367dd +dist/2026-03-05/cargo-beta-x86_64-unknown-freebsd.tar.xz=203a9b205262c90cfa04870d8e5233ab65bf6ad8840579194455917e50e51736 +dist/2026-03-05/cargo-beta-x86_64-unknown-illumos.tar.gz=ea24f15a151c3f9d5d0ae9d5b21c14c6570ad0ae1bc749c4367e65f96d010c09 +dist/2026-03-05/cargo-beta-x86_64-unknown-illumos.tar.xz=49ea2b342ed92263ff57b56eeb3b9762ad48ae06ea5cc50f60fd4643adbb0afb +dist/2026-03-05/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=2ace7395bea67d5f8e336f1a0c96a3d0b6deea580600a5e0f55bc95253d46496 +dist/2026-03-05/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=3330ec521c6d283934f756775be28b827ed0e04ad9f584245d14641ccde72dfc +dist/2026-03-05/cargo-beta-x86_64-unknown-linux-musl.tar.gz=5b212696aa5b7dcf7845b85145e2ed012eee2bcec25f7cb4224d665b84c2ffe8 +dist/2026-03-05/cargo-beta-x86_64-unknown-linux-musl.tar.xz=f8398a2a545b8e795c412954b8f18ae773c12a3542b20cac92aa1e1556a38094 +dist/2026-03-05/cargo-beta-x86_64-unknown-netbsd.tar.gz=2839e430b2c7b98c80eede1219160104462d2b46d6eb8b79967e43dde397c482 +dist/2026-03-05/cargo-beta-x86_64-unknown-netbsd.tar.xz=2e34688fbba817738fb3c5a34601c83a55ac563d4bf9e8dc58b1256e6aa8db3a +dist/2026-03-05/clippy-beta-aarch64-apple-darwin.tar.gz=32c4d827c0aa7af3c1ce66e5689418e9accb1578b6c8843c7f5cd00adf7fcf38 +dist/2026-03-05/clippy-beta-aarch64-apple-darwin.tar.xz=b95cae5d7a22da58fef7e9e58820adf7b5e16ebce72b600379b907a57e3ed9d0 +dist/2026-03-05/clippy-beta-aarch64-pc-windows-gnullvm.tar.gz=c38f756f294133f65835c84fa57bff9c7aa9614ccf3a722ef8984b8b70ea5957 +dist/2026-03-05/clippy-beta-aarch64-pc-windows-gnullvm.tar.xz=96f38c6ef8c3b7459c45c4900ece7c9a32ae024eb88ea623dfaca6a4d0591b7b +dist/2026-03-05/clippy-beta-aarch64-pc-windows-msvc.tar.gz=532fa0e4ae7d44501dd8b4503b84d55582ef8c4badd505aede9145329d73a77a +dist/2026-03-05/clippy-beta-aarch64-pc-windows-msvc.tar.xz=19606687be0915392385ca118f9931955887e4e15e7427e631b132e42dfad13a +dist/2026-03-05/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=36218bed1dbaeba27b450190862218c2e87af7b366575d4ce3a6688eefa70aa7 +dist/2026-03-05/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=04d10dfa623df3be0e9a2d8f8d03ba7b094fed0585d9cde34014d5b65c72e981 +dist/2026-03-05/clippy-beta-aarch64-unknown-linux-musl.tar.gz=c9286a0b79141c973950c3ad28b82c3c9becb15504dc632c8875181d206b5c96 +dist/2026-03-05/clippy-beta-aarch64-unknown-linux-musl.tar.xz=5fd10f23a7a6c652d26e53f7f4715d72fe97465ecc844a2e24bedf76df56230e +dist/2026-03-05/clippy-beta-aarch64-unknown-linux-ohos.tar.gz=de3a8e799292a9bde717fdf1ac41bf1f6d773641f6d9f07ed435440f91d8abdb +dist/2026-03-05/clippy-beta-aarch64-unknown-linux-ohos.tar.xz=9ff3c0639362d398c8c6b259094bc2e8ebf58318666a4fe35e38bee3e54d02e3 +dist/2026-03-05/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=c38ba3fb8e40b7a543d759b70f66b0000a61f418713b20a50e0070f8f360b799 +dist/2026-03-05/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=aeea0f32d03becd509df70bc83ea74590f935517979460c05886a08c2715a013 +dist/2026-03-05/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=6e23bf9dc2c5d4b17c42d90015772ec0136402e47d36b49ecfa11c79c246a54e +dist/2026-03-05/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=7f0945519df5a8f4ebb17ee36fc582af022942e6a08e30d53f7aba449f7736d8 +dist/2026-03-05/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=cd020f9fccf3b99a2128ea4a2225d82eb1a4266a5cc0e7014d44c5d43a909712 +dist/2026-03-05/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=de4559a9437eb2db35dc44f9a0c60d847e5fc46b857d164c055a583904c3683a +dist/2026-03-05/clippy-beta-i686-pc-windows-gnu.tar.gz=35bcc1f3d81eac70e8f3d1ccb085add8fab6b7f4e46b002e5b87eb4553326ccc +dist/2026-03-05/clippy-beta-i686-pc-windows-gnu.tar.xz=03e68c1bcf261c8cfa4caf707f1a64164ef4b65cebd27e6aa977494c4915fdfc +dist/2026-03-05/clippy-beta-i686-pc-windows-msvc.tar.gz=0be0ce91b176b60d2c5f372bf795d1b2a8c67254013b37d35cda10c358947881 +dist/2026-03-05/clippy-beta-i686-pc-windows-msvc.tar.xz=2856ecfe58311108e53d3228f979a7fbeaf686f9e6ae5c410442eeef3965a07a +dist/2026-03-05/clippy-beta-i686-unknown-linux-gnu.tar.gz=af2c7821f78ff6cba5778fd127502c885bc1eb3890d5a700f31ea28cd3cd9e30 +dist/2026-03-05/clippy-beta-i686-unknown-linux-gnu.tar.xz=d33cdb0e15e8df422d34ca77267c765a6694dd342f6072aa1f495fa937126638 +dist/2026-03-05/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=6325829f43a04cf485b4d6293289d466003fa5bf55f98bca104d377385f6c5cc +dist/2026-03-05/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=35a0e71198ed24f8d8258d343647c472c915bca007ae8f23122c636d2ecd3820 +dist/2026-03-05/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=1fddbbf7398a38013db3809eb29b62f828a8f8ca52ae183f5620fcd00d6ec4d2 +dist/2026-03-05/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=66fbbfc003a30ea6a9a4fb389a0e2d88b16bb347802e8e718cf4250af24ca3a9 +dist/2026-03-05/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=42a7be84be5beebeaadbbe7bb184a5a0679b63d566e44522c930d78c77306645 +dist/2026-03-05/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=b27c6822586427072c973425a917b249d29b9284467c2c28e8dc7a5a804b74df +dist/2026-03-05/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=4c9b7ee30c127add6ff3ba6cf443546a30955e30ca974f06abcce13ca19a321d +dist/2026-03-05/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=b17c9b434cf1b171b307a3de511527bfc08edc3164714981679d709dd3f81306 +dist/2026-03-05/clippy-beta-powerpc64-unknown-linux-musl.tar.gz=9f0eba1790ea3d3193bf1f10af114baea823266a2164bd7109204c87026e6121 +dist/2026-03-05/clippy-beta-powerpc64-unknown-linux-musl.tar.xz=8db967689337ce7019e0c5a2e5bea2e5eda896b477ee6606db69ceed1d478fd4 +dist/2026-03-05/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=4d65345aec8590ca95a5929a544ef165fd9fae3a513ec986198af29f3082ccff +dist/2026-03-05/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=3ef1c6ee769fa586f2f9ec78abc66a2ed24695e9bf2fbf85290316127c46026c +dist/2026-03-05/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=17dadf88ba335fb69a8a393580444e45a04719bf43ef6e893f3c2bdc7880960b +dist/2026-03-05/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=eeba6019f7788bda6e353bc277d8ab7ae9b91ca2512ce81185e6d3977520d3ad +dist/2026-03-05/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=524db9643bea13f5787adba2b1f0bde5dee45f414a4f0ae54f23083dcd4a1a47 +dist/2026-03-05/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=c2f5a81757cf04ae2f6298b79007673a69e044a45775b362b1ca98dd2bf96d6f +dist/2026-03-05/clippy-beta-s390x-unknown-linux-gnu.tar.gz=a3441cacd2ccf3afa5c82fa5f2b552c864ecbd2abcd393fadeb96b4d6e5c3883 +dist/2026-03-05/clippy-beta-s390x-unknown-linux-gnu.tar.xz=ebb5d7b5098a004be056c6f5ee111c02cad679db9d8c4616e19d6f027506b43c +dist/2026-03-05/clippy-beta-sparcv9-sun-solaris.tar.gz=a230e3330c2fc952c58d52b9aa118511b3ce4f441862770a4136a6aaa85e72fe +dist/2026-03-05/clippy-beta-sparcv9-sun-solaris.tar.xz=88496d17922c9cc19c70cdceb35b81423047644ceaa2295e92effcc6829f5c7a +dist/2026-03-05/clippy-beta-x86_64-apple-darwin.tar.gz=94a9b2fc9f16587cf41230fd56ba71bd66fbfba60c2c2b914f9a667791de1c4d +dist/2026-03-05/clippy-beta-x86_64-apple-darwin.tar.xz=5cf79b63ecbbf627d164b24123db5595e5c9e82708c6d7d672a1ff9bb1463b9b +dist/2026-03-05/clippy-beta-x86_64-pc-solaris.tar.gz=cb69b7d5743d43722e6eaa963272fd58f41b326f7724b701606a9409941a105d +dist/2026-03-05/clippy-beta-x86_64-pc-solaris.tar.xz=ff191a1112e48bc07d377fe8f85fab2791b0ad2c74c22caef2f9770a459b706a +dist/2026-03-05/clippy-beta-x86_64-pc-windows-gnu.tar.gz=59088e23e324ec220581e5e163106989268c6897552f2284c435782d6161c8b5 +dist/2026-03-05/clippy-beta-x86_64-pc-windows-gnu.tar.xz=36ee7120604df50e8e2568e7c048bdbf1180d4ef903330520175606be0ae531e +dist/2026-03-05/clippy-beta-x86_64-pc-windows-gnullvm.tar.gz=973540bc6b85bb7dc972ceddbd859e86aa2fb2de92e0e418a2390b1135243f50 +dist/2026-03-05/clippy-beta-x86_64-pc-windows-gnullvm.tar.xz=ce07888ae00182bcd2fdc0a40c1b8d6a66a5513afda14ca2dd13b7954020b606 +dist/2026-03-05/clippy-beta-x86_64-pc-windows-msvc.tar.gz=c51dc3262c9db4f1d394ca39a7f28780258b8718c32d1ddef68c64f256ef3682 +dist/2026-03-05/clippy-beta-x86_64-pc-windows-msvc.tar.xz=067098b54ce15773ca62cfd75a2696a95e961edc5027afa6605d99d2d5a3ae84 +dist/2026-03-05/clippy-beta-x86_64-unknown-freebsd.tar.gz=54a7297c31dfd6e11a0f127a9d51f4e4fc8878c317e8802a21f454140fe9aee5 +dist/2026-03-05/clippy-beta-x86_64-unknown-freebsd.tar.xz=7eb5bb64c781be9ba6ca4da2576019c0aeaf06f03c0dd420b9b6a2ca0091c57f +dist/2026-03-05/clippy-beta-x86_64-unknown-illumos.tar.gz=7abdf2a821441ea77118f21f4741d6841529e552398f88bd8f1d26ca277c3611 +dist/2026-03-05/clippy-beta-x86_64-unknown-illumos.tar.xz=8962472a92f36dffe4dec76f8a8bf7d5eaf27a0ee39eac1bcd05e40f6b12b9ce +dist/2026-03-05/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=e517ba027f5a06456f20ba2229c7538bc4d1cb2f5f6a601189a08053d5d55701 +dist/2026-03-05/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=8c4f7f679154ea734bd73cda9ac8c79cd12cc273c1c9dfdc0611ab34197d4d27 +dist/2026-03-05/clippy-beta-x86_64-unknown-linux-musl.tar.gz=a52b54b33b5ea3f4d43be459b952344fbc812e79e6d9e2fb4d41f3741c4db75b +dist/2026-03-05/clippy-beta-x86_64-unknown-linux-musl.tar.xz=ef778799a516bbc9b2947afe5660a6435ce2a8e011e9ae74c7927e9b485d1048 +dist/2026-03-05/clippy-beta-x86_64-unknown-netbsd.tar.gz=19b24bb8f9df0c52461f692c365500a0f2b9c3f988d2ddfab017f152c766eac7 +dist/2026-03-05/clippy-beta-x86_64-unknown-netbsd.tar.xz=96d2797ef4d847a849a5dea25114c769d2671cd7ff5e2c0cc06902529d5ab7ea +dist/2026-03-05/rust-beta-aarch64-pc-windows-gnullvm.msi=08afe58fc6635c05f6f62cbb2b6fa0ff0c747c456cd86780376236bace4d6a54 +dist/2026-03-05/rust-beta-aarch64-pc-windows-msvc.msi=8e6a83a419d809ac8621e266f72bd263a63d75dacdf38d7a7f1ddd3402a53893 +dist/2026-03-05/rust-beta-i686-pc-windows-gnu.msi=7989892eecd5378bcfe40609fd27592410fc658334cbd8142cd452f7bac00057 +dist/2026-03-05/rust-beta-i686-pc-windows-msvc.msi=4b7e475e8e52391b439c4c081facbce68ac369d7b09f6080762c6679894bfa48 +dist/2026-03-05/rust-beta-x86_64-pc-windows-gnu.msi=3f4714f30c3ef169bdf5942856fbe5599d7c63d16cc95be6d86e0d2fd837a596 +dist/2026-03-05/rust-beta-x86_64-pc-windows-gnullvm.msi=a2e1941f860be18fb26ef403f3baea3619827dd268e705e895f6d6d9186d2658 +dist/2026-03-05/rust-beta-x86_64-pc-windows-msvc.msi=e03da5bcaa71b11bc8d8d748da947af0a4d6b21ec7991b6c3e383556cc00198c +dist/2026-03-05/rust-beta-aarch64-apple-darwin.pkg=809c3facc61f153caca8210b59340627b263b66fe6889456e43b9d080431174e +dist/2026-03-05/rust-beta-x86_64-apple-darwin.pkg=0146d25460ee2dedd98718b25d6f394db8b4c7f0d447c58fb34c120a8bb8369f +dist/2026-03-05/rustc-beta-src.tar.gz=7c1b2a4a591ba994cfb25b22675a749914eeec0d5ffd4e5b026d14067fab09b5 +dist/2026-03-05/rustc-beta-src.tar.xz=9f5053396fe6dced53ad64a463fb727ac7ea9294544db7408f5dc2005878c008 +dist/2026-03-05/rustfmt-nightly-aarch64-apple-darwin.tar.gz=e68b2b292526919eba5dd9f5ba441ae9b8f4ac823dd10a4d7b47bb872d3a21c2 +dist/2026-03-05/rustfmt-nightly-aarch64-apple-darwin.tar.xz=cb676f18bf6c90292e04effaa4d8401ea459ce93b66b5b737100aa103cefa402 +dist/2026-03-05/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.gz=92e2a069130fa5b13170123e0d499800c0cea8e38fb4e9cc6b59ad1225894dba +dist/2026-03-05/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.xz=798f752757db5312f2d78699be4f031cc6a5695c791360e1bb1d422b19d69f7d +dist/2026-03-05/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=fda191f803e26ec5eddea71c74e81763aeb3cdcbb8ff964ea8a85808e8da797b +dist/2026-03-05/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=3fee26f428f04b1f5d0208bd3bc6f14c0cbd6d909505e77cbed642871efa606c +dist/2026-03-05/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=1bb0c5ebfa96ed19cdb6bafd275ba770540b1c150c2dd92a34d810914d64c8ac +dist/2026-03-05/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=82b607f58f9e87e9095b05266ed573ce58a0192201151c31d15a0e38f59bfabc +dist/2026-03-05/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=6748cabbdec4552318a15069ba3cfc15d4c799b48996d95640a62e37a272d272 +dist/2026-03-05/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=54cc148fb9df3d76b2291e9492308cbd4c529c20240b43110fd0ab6d4cef4c35 +dist/2026-03-05/rustfmt-nightly-aarch64-unknown-linux-ohos.tar.gz=962c11a988df29b7c7222b36a79aa4cf3ed3e7f9e5080bfc11e2bfce4bc914b8 +dist/2026-03-05/rustfmt-nightly-aarch64-unknown-linux-ohos.tar.xz=2d051b5a08c069af69ee278ad0a773b5f3ef1ff25b13d124388ec4dd0c9302fd +dist/2026-03-05/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=f9e96db6e45ed1044910213944457f8c1339e3486f0019ec89d646ab7d1889f8 +dist/2026-03-05/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=ae2467f98c25e7cb8f2848ff407f5d9906ba18b14ff170dac44cc525f9e4037b +dist/2026-03-05/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=307d16c5eb1badd7d345233eb585fcba1e69a4060eb97bd9dbb6595541475fd9 +dist/2026-03-05/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=cf7ec96a6391a0d5bec32163f505ad13e629a9bbf73b4825a435048fec6f789f +dist/2026-03-05/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=5a8a2ffe2d164655fdf5074a1f51da65b6ceccc9796324a90666677e7cef81cc +dist/2026-03-05/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=6b9790ee4854caf73d4e230447f51ddfcccf74f9730d33d25b9929fd9d683309 +dist/2026-03-05/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=112ab32d3983596bc64040965c3e04cf850122ca81b20a1153ff53c28457b2b9 +dist/2026-03-05/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=40b5a446bf57d045306663e77aa03cab150d6f17e711f11175a095df5248a981 +dist/2026-03-05/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=ceadd70ba7ff5c3d91ee7774dbacf6017a16bdea5ee8870a7090d56824d983ab +dist/2026-03-05/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=cc0b1b3a42888ebd54df2ac6b1f715bb348879b65a7436612c7e6975a9a6f2b8 +dist/2026-03-05/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=d7fe2972190a304deb4ac3824e8c2164b216c68c5b7c9b94610abe8e2bd0f3a1 +dist/2026-03-05/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=07cedc3db3af1cf5912f35b7703762bcbdcb936639b2007ffd83ea9f897e977c +dist/2026-03-05/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=75411629c837bd6920d4c41caca3ef43c89e1f60fab3fbf603d073c379437e68 +dist/2026-03-05/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=ff352912831a559a6c5a089b3b6f172963be1653c21e47d65546f07be71bd89e +dist/2026-03-05/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=ec2f4a5afc9e72217c3dff1c96587f06572b46c5dbb7d32b7dfb7105d0a2eb7d +dist/2026-03-05/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=63085f7704f7c272d8923428fe8186cb28d683d94312fe42d9f36b548d1e8ad7 +dist/2026-03-05/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=199ab80531cf416a398954eca5b31a1206ecebdd7b09fbfe7c532d842388b7b2 +dist/2026-03-05/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=7b3f8936dde840abd4519a3a26de92fc4d1250f9611137bd0ff8528e2ab3b064 +dist/2026-03-05/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=7e684003264eb2392e920cd955af7fc263062d4fbc428ba0dea7179d6d47b83d +dist/2026-03-05/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=905caff6ef52c86124edfe5f7c52821badbc89f358816443f52cbb7d1497d93a +dist/2026-03-05/rustfmt-nightly-powerpc64-unknown-linux-musl.tar.gz=7f03bb50fccc0a8aa1d8c1e092f6b06fd7dcf885d4d66ebcc90daf69d98c470c +dist/2026-03-05/rustfmt-nightly-powerpc64-unknown-linux-musl.tar.xz=ca3159c24af1a93ef3b3ec47902a0aca81b288b39f105acb6d337e7d88eb1c2d +dist/2026-03-05/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=6affa4a366183dc49b687fd5c2867975e6b6f74f9a1cc6999392a46211a0e85f +dist/2026-03-05/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=1053fa8fe7ee40eef933945c885ed6e3a7ced2beb1fd0549bc475cf30ab66113 +dist/2026-03-05/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=ade7c0d35f1da32d010e3e1787aa1d505b675bb72208c2141149cc82cb53b3ff +dist/2026-03-05/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=535c441f349e3b2d14924bb308bf528c16d2e83f5a76755e2747b23ff3a94721 +dist/2026-03-05/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=bb784c051a0472dc1f89128beb012d1ffdeda4a213fd7cb4b3f3485e4ad9fd7f +dist/2026-03-05/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=99fdbdee5bfa2c1071347727448cffbbe3a37fffcfe3f58fd5f21585ced2280b +dist/2026-03-05/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=074564c8ee6f4e01adc5d5be5ad569147e6c14f075066e3739a23a3b1516bd9d +dist/2026-03-05/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=420107a978280b0a1855d3b6b7c2bf52feaf806bc3c027e5948755ad31962ee2 +dist/2026-03-05/rustfmt-nightly-sparcv9-sun-solaris.tar.gz=d391a08020fdb9e5a9dad91d48015185e376340d96265e82c3ff1dfa9fc50464 +dist/2026-03-05/rustfmt-nightly-sparcv9-sun-solaris.tar.xz=6730256f48b4ef1e2f955bb40adf9c4107d413d41b5c96296cec9bdc4e464fce +dist/2026-03-05/rustfmt-nightly-x86_64-apple-darwin.tar.gz=8a68468f5bb8e65f351239307d7f965a2c5824a60535a399b36786c2bb1419b5 +dist/2026-03-05/rustfmt-nightly-x86_64-apple-darwin.tar.xz=939077ad91115420b116a4b14e8d5b3c06ecee86a42b8ecd9451e551b19c8d2d +dist/2026-03-05/rustfmt-nightly-x86_64-pc-solaris.tar.gz=a9938bc124c52b114a9b7c4a90d731a17c30bed1078b42e87e5bb1a62d49deeb +dist/2026-03-05/rustfmt-nightly-x86_64-pc-solaris.tar.xz=8588faf0a7948b555cb585ecc547d69dbf7a03e51f520165ad052c0d74996e45 +dist/2026-03-05/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=a914ae0e5304dd6a1a3fa72b86e0de7c564c61470082c7e25f457335022c2517 +dist/2026-03-05/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=6492952e004f9ed6a35eb7454a84f4c4d595e197d8b31c832a2f96e808a7241c +dist/2026-03-05/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.gz=d611d10c86712ed57dc7e7db1c0cec3b0660b7d95a60ab6dfe7253dc7e88c379 +dist/2026-03-05/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.xz=1f00cc4640dfec1694647f3ab7a18e3c0b15cccaf45eaa4fc10992044ce556cc +dist/2026-03-05/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=48b36ad2d3df621ba56f0c092e014d8dc03abfb9af256169a0e309f11aa393a4 +dist/2026-03-05/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=46b61c73550605e0eccc9da9bb26141a56fa981f01432f1f1fcfcc8c755ae1f7 +dist/2026-03-05/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=9ba9bfa29fb6f5a553180046cfd1b348fd2abce3997130eb063c7707d6e440f7 +dist/2026-03-05/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=ef9e51529aba4607b55144c98d97f830f660addfef28aa51d0e1afae5212ab09 +dist/2026-03-05/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=399f26b755ac7773d2bf5be27fe1e7fc8f74ce1af48250fc2ad8278de457b212 +dist/2026-03-05/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=ff912b75d3f9042631c6536f037d68fa645714fe05b94eac6541f67d2e56d47c +dist/2026-03-05/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=fee5b8e5619b5ab9e1b53771e775b0456254e50c08cbeb032184491b77bd28e0 +dist/2026-03-05/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=bde94b7cf7038ce8e8cf9f0215abe66624a22aa64437c02c72331ea86951c685 +dist/2026-03-05/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=c13174e759b8546ddd7a998228737714ca58e559069f560bc89235dc6ca0386c +dist/2026-03-05/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=b90a54783e5514d739828100022beb88a721586b579f1e868d98d223497f3b1e +dist/2026-03-05/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=64487e30e104a34c89de7dfbd2f55a0c8c04750520c69c5076adcc870b1bd8bb +dist/2026-03-05/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=c48d3dbf294d4bf9a7f7d775d57342fe2f4b912ac366beec43aa6d7aa861afe7 +dist/2026-03-05/rustc-nightly-aarch64-apple-darwin.tar.gz=1cc140ed9ade9e0797c91ae9c3e8796a5a097c90044d11fb7f6b7472e5198076 +dist/2026-03-05/rustc-nightly-aarch64-apple-darwin.tar.xz=d52a4f380fe5fe17c361f39f913062cf9d8498cb8a8f3d9f21a4280564b87123 +dist/2026-03-05/rustc-nightly-aarch64-pc-windows-gnullvm.tar.gz=90e41e28ff254abf0eba0daf6c1a514125c5069bffde1edec390f1149cf7d1b0 +dist/2026-03-05/rustc-nightly-aarch64-pc-windows-gnullvm.tar.xz=86bf30f294809011b2e2c40f5246405b230d2b5cbce21e28e5b582a6832a7cea +dist/2026-03-05/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=590a16846d0fa0e4ea8c9c130f5653bc27669114527d2cbdb1ad407a48f5f60d +dist/2026-03-05/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=4e7a34abffe1eb6e7119c43d4a79893abcac38c4090b2175d24c3d9156f71dc1 +dist/2026-03-05/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=ebb0d66ec5c6f5ad70663110e976ce7d43ebb3920eb0a4e1b1201994c03b9bfa +dist/2026-03-05/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=88bcbec30877561aecb88ad475e96783c99cc525dbcaaecbb321c158c32cfc62 +dist/2026-03-05/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=e6aeb296405e0e94b7e966a590397085ecb8a31fbf162be2d568dac9c8b324dd +dist/2026-03-05/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=a74f82126979cb77099e4f9499822c11a39507d71302dd51e483011ff88d28c4 +dist/2026-03-05/rustc-nightly-aarch64-unknown-linux-ohos.tar.gz=19a50ab74e4c1dad4c40402a6047730fd4529808a9c8eda4992517178631e2e4 +dist/2026-03-05/rustc-nightly-aarch64-unknown-linux-ohos.tar.xz=ffadce5f79c003218be6eb87208fbf641ccf69b93bce0356e1355d62e09ae656 +dist/2026-03-05/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=aaac22afb9b1ef407f588404a65462b406d98d444ce8a5f5db1f2bfd7e7defbd +dist/2026-03-05/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=2a0b146e55528a5b47c5ff4ccc6b221e721e1a3a704a49618d0219bb85fe1a1b +dist/2026-03-05/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=beab02beed24871b275814d214e6a35ce6ad20b1f23eba11f2ff1f0999b81f0b +dist/2026-03-05/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=b3b2f5ff0a0363f5da8f75d8dcfc3032bb56feff6f85fe02151ebb5c6934b9bb +dist/2026-03-05/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=d226f8ec83d863c1de1c8a869c0ed39def5c37ff15e8c5c3321fe938eb2dfe2e +dist/2026-03-05/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=1c31fec36badcb08942c285918b49ae2eb83c84233ed62dfabeeab483954bece +dist/2026-03-05/rustc-nightly-i686-pc-windows-gnu.tar.gz=530a9826095bdb19cd08f7574a442aff430f288eafcfe3af7d67a1c05f7893cc +dist/2026-03-05/rustc-nightly-i686-pc-windows-gnu.tar.xz=75c12c03203a15528f8bee21a9a91b68fb42586a47d578940363624cada99b03 +dist/2026-03-05/rustc-nightly-i686-pc-windows-msvc.tar.gz=70c9a0389074801195ecef5ea2c54c146f1e44a560cdc28cb0e0708951c83c27 +dist/2026-03-05/rustc-nightly-i686-pc-windows-msvc.tar.xz=3c275d6fb85bb4062d8ef74906e03e030a391639782292b27b8f3cf75782ada6 +dist/2026-03-05/rustc-nightly-i686-unknown-linux-gnu.tar.gz=c6e2daa18250b1b54f6354405ff337e06611a87a65f8171e9aca9eb073523460 +dist/2026-03-05/rustc-nightly-i686-unknown-linux-gnu.tar.xz=313a837f74f788a8ba0406deea4362a406d1caafab33e215cd7f527cecf76849 +dist/2026-03-05/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=8978ea4c8c8f3fcdcd069a1e0df9ac457cddc1ca7ac01c59083e47ff1c957c8d +dist/2026-03-05/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=a3be5275e2523d7cfb1d08e46db414c3c351743b87bc6579f666d124ab8bdeee +dist/2026-03-05/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=7249ed32ade44739209f9a28cfbeb9e4d1242b431528567cfbe8f56b80653deb +dist/2026-03-05/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=7e395037d4d0695701a74c758fc48606b553a42bfd48534f6f2b7bbf480e2e0b +dist/2026-03-05/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=6e25c8fa0c20855c3bab253c95e882b56ab4e25b81400f60a577d55faef2483b +dist/2026-03-05/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=92ce678053ad13dbec2d1fb8bc88c524326b9a00fce5820d41bf4409e69d4d90 +dist/2026-03-05/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=756472d5ddc4f6fe82364538b528a058c68c87367fb7f3df63efbc46949ef2c0 +dist/2026-03-05/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=9edbf2a91630a7a4e9af1ba70a353b8b5a828864bbcdeb5660f0db3ab35a189c +dist/2026-03-05/rustc-nightly-powerpc64-unknown-linux-musl.tar.gz=05c2e2ae8fc194e21f4d6add34f6275484a4fed79634088b8e98a57f6b356bb8 +dist/2026-03-05/rustc-nightly-powerpc64-unknown-linux-musl.tar.xz=ac7ab3865ad8ad80d984dc25fa43ca6c393a863eff2ca6d6875e48d06ecaa3d4 +dist/2026-03-05/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=ddfbe0ab2e5ce9feb07ad38e5d1d3306caab7984d93062c1e8516f6c13d5ecca +dist/2026-03-05/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=f3db3420bcab694e426fd66aaf7343f52429877ea4b0949977aeb72ecdaccdc7 +dist/2026-03-05/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=bf29889b4ce6e0e9ed505db3892e5e44001c2d35a655f81e42d804eb8923df32 +dist/2026-03-05/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=1da514153649cc32a98a96c669a3010743ca799a979e4d4af926f778f77f1d93 +dist/2026-03-05/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=126224ede47d98027afdd16eea4548f48883675b773e43ae6379ed8b6e2f2140 +dist/2026-03-05/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=8727382f858f4cf043f7fbcaf38857422c1eb18cfec72b8a2bd5399928983aa4 +dist/2026-03-05/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=27e7c9c68936945b8d4008aaabf1abcf0ca6c2a969384e448798f0f901360e46 +dist/2026-03-05/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=6a4bbc5a65648bff0cf0512a9e72f88d5e9eeeca4a4580c2f28f5d95e6152221 +dist/2026-03-05/rustc-nightly-sparcv9-sun-solaris.tar.gz=fa3c1c7c1e3eaf8be18a816e95c0f343b004065360e9d376695e56a3972b3bf4 +dist/2026-03-05/rustc-nightly-sparcv9-sun-solaris.tar.xz=e4bf12cc38be7431f19c7e291ccbfd410a0555314fc0e4099073cc879458efe4 +dist/2026-03-05/rustc-nightly-x86_64-apple-darwin.tar.gz=808e4839f09c0252c10aeb54276695f1e881dde4d1fe9d8583743fdb438f7a08 +dist/2026-03-05/rustc-nightly-x86_64-apple-darwin.tar.xz=7ac7a15dfbacf5a79ff0f1b596828f178327df77883d7a4af76b7abdcde4cd9d +dist/2026-03-05/rustc-nightly-x86_64-pc-solaris.tar.gz=d4731e54154d2fd0c93ffddc5e3aa4ad74280b6978f7f3f58afc274cd1c410d1 +dist/2026-03-05/rustc-nightly-x86_64-pc-solaris.tar.xz=a5f5be4a5819e433dfbafb2ed4edae7f44b3d0de04610e7543279a3d0f88546c +dist/2026-03-05/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=5ff330a7d68a8ddbd2438686d74ac8ae9e88d5a638f61e8f6e49c3669322f18d +dist/2026-03-05/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=58c255026ed3088685aa2716828b21ac1e0537aea099176da9a55511e7e6e1c9 +dist/2026-03-05/rustc-nightly-x86_64-pc-windows-gnullvm.tar.gz=75bab5e659f7d88441390b02a5f51a7768ebde0ec0c642a669da88367b2c9ed9 +dist/2026-03-05/rustc-nightly-x86_64-pc-windows-gnullvm.tar.xz=2d60e6f303301b74627d7f222a8a5b946c07e0011a060ce86a1df0807d127b80 +dist/2026-03-05/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=cdea2da52b50aee1c460eebb28fe69a2c50b23540aa4e0471c30b15e3670492f +dist/2026-03-05/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=f314218a65ce441498b988c29ab8f8f79d65d3c4184d4d99fb483b6f887f1505 +dist/2026-03-05/rustc-nightly-x86_64-unknown-freebsd.tar.gz=741e4cf53e080c3d063e7af230894842a4d3306af0984ce189c1b9da1b632883 +dist/2026-03-05/rustc-nightly-x86_64-unknown-freebsd.tar.xz=1f5b1a0f0c51da513009d6040b16b9e3cc40ec753a921143280d2fd6ee9db0b2 +dist/2026-03-05/rustc-nightly-x86_64-unknown-illumos.tar.gz=a55975f8a4547fd0bfbb0fdaa67dbfafba606d3016b0038ea6a6e7a013e10fcd +dist/2026-03-05/rustc-nightly-x86_64-unknown-illumos.tar.xz=eb3e9776311af9c99de8f9a041247e94168797f1d6dee99f6ecaacbbc181381b +dist/2026-03-05/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=fb153dccf3ca91404d502c7cbac9eb7374cde3a6c0838a46c8308a52ef67ed4f +dist/2026-03-05/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=cda6cb941296a57229610ad32fa48c3df8408e8dddafad621f9f3e663e3b9868 +dist/2026-03-05/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=170926fba93656f78318cb26e0cdd8cce63a51b45ac876a0126e2f3a8fba6e61 +dist/2026-03-05/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=b1a7eee40476f172263c75c12c80797e982534fa85f704aaea762fb58235e06d +dist/2026-03-05/rustc-nightly-x86_64-unknown-netbsd.tar.gz=b3ca488ee4a2b94e774e41018c11177f8cf212c10711fbccfd547711269ddbbe +dist/2026-03-05/rustc-nightly-x86_64-unknown-netbsd.tar.xz=05bb7747133d824768473ec6619acbfa673d7996c697314e12b7861ebcbae157 +dist/2026-03-05/rust-nightly-aarch64-pc-windows-gnullvm.msi=aa745e99ed2fff3b3e9971cb877b50eefb7ec7b583341e56d15ff731471e09e3 +dist/2026-03-05/rust-nightly-aarch64-pc-windows-msvc.msi=e4f745b5e034bfa3f3b131ba161e51a4461c98968e54d0180883e2b60c227027 +dist/2026-03-05/rust-nightly-i686-pc-windows-gnu.msi=0ed6c3fdc7e64c629867ca746ba2581ff7441c3fa38ea5492f483e4166c78e1d +dist/2026-03-05/rust-nightly-i686-pc-windows-msvc.msi=f766902a3563476dff0effb43b94c87d6630ee3c6a98de71fe73c9c6633d85cb +dist/2026-03-05/rust-nightly-x86_64-pc-windows-gnu.msi=22cdd435bed4b20ebe4685c17ee4536f723f0f6b742fc744cb89a01cec66c9b3 +dist/2026-03-05/rust-nightly-x86_64-pc-windows-gnullvm.msi=dd0940ed9dd2de730dabc6298fd6d85a2c0ce46679863ac7d2b6e06c24dfa564 +dist/2026-03-05/rust-nightly-x86_64-pc-windows-msvc.msi=8c82372d71b9bfe9e6e70b391633409cf38f3409ef1d7f7851623ceaed721597 +dist/2026-03-05/rust-nightly-aarch64-apple-darwin.pkg=37504436eddf5f9ac1c493caed773eb7bef0839d0aef5bef770a392a042a4545 +dist/2026-03-05/rust-nightly-x86_64-apple-darwin.pkg=5b00f8e416966e84a5b8513b1f9990f790e921056e4bac8b898fdb00efcf371d +dist/2026-03-05/rustc-nightly-src.tar.gz=75518893121190cd6716780637373d437c2d09b0dc49fa49fd90b69c1e5f315e +dist/2026-03-05/rustc-nightly-src.tar.xz=74bae911bb195af3de63dd0d9f5c8c2848093bfbc6bd9acde4aee4122be0a5c7 diff --git a/src/tools/cargo b/src/tools/cargo index ce69df6f72a3..90ed291a50ef 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit ce69df6f72a3b6a2b5c722ba68ddef255344b31c +Subproject commit 90ed291a50efc459e0c380d7b455777ed41c6799 diff --git a/src/tools/clippy/.github/workflows/clippy_changelog.yml b/src/tools/clippy/.github/workflows/clippy_changelog.yml index 4d84d6b6dae4..121e1b84543f 100644 --- a/src/tools/clippy/.github/workflows/clippy_changelog.yml +++ b/src/tools/clippy/.github/workflows/clippy_changelog.yml @@ -20,7 +20,8 @@ jobs: - name: Check Changelog if: ${{ github.event_name == 'pull_request' }} run: | - if [[ -z $(grep -oP 'changelog: *\K\S+' <<< "$PR_BODY") ]]; then + # Checks that the PR body contains a changelog entry, ignoring the placeholder from the PR template. + if [[ -z $(grep -oP 'changelog: *\K(?!\[`lint_name`\])\S+' <<< "$PR_BODY") ]]; then echo "::error::Pull request message must contain 'changelog: ...' with your changelog. Please add it." exit 1 fi diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 795eba1dfeaf..7c798a3c2e5a 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -8,6 +8,11 @@ document. [92b4b68...master](https://github.com/rust-lang/rust-clippy/compare/92b4b68...master) +### New Lints + +* Added [`unnecessary_trailing_comma`] to `style` (single-line format-like macros only) + [#13965](https://github.com/rust-lang/rust-clippy/issues/13965) + ## Rust 1.93 Current stable, released 2026-01-22 @@ -6433,6 +6438,7 @@ Released 2018-09-13 [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq [`derived_hash_with_manual_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derived_hash_with_manual_eq +[`disallowed_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_fields [`disallowed_macros`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods @@ -7136,6 +7142,7 @@ Released 2018-09-13 [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_struct_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_struct_initialization [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned +[`unnecessary_trailing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_trailing_comma [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern @@ -7233,6 +7240,7 @@ Released 2018-09-13 [`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for [`allow-unwrap-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-consts [`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests +[`allow-unwrap-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-types [`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles [`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates @@ -7252,6 +7260,7 @@ Released 2018-09-13 [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold [`const-literal-digits-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#const-literal-digits-threshold +[`disallowed-fields`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-fields [`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros [`disallowed-methods`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-methods [`disallowed-names`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-names diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 8f5ef9ca2f8f..bcd800930c51 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.95" +version = "0.1.96" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/book/src/configuration.md b/src/tools/clippy/book/src/configuration.md index b13054431898..b270c11ab397 100644 --- a/src/tools/clippy/book/src/configuration.md +++ b/src/tools/clippy/book/src/configuration.md @@ -2,13 +2,17 @@ > **Note:** The configuration file is unstable and may be deprecated in the future. -Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`, which is searched for in: +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`, which is searched for starting in the +first defined directory according to the following priority order: 1. The directory specified by the `CLIPPY_CONF_DIR` environment variable, or 2. The directory specified by the [CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html) environment variable, or 3. The current directory. +If the chosen directory does not contain a configuration file, Clippy will walk up the directory tree, searching each +parent directory until it finds one or reaches the filesystem root. + It contains a basic `variable = value` mapping e.g. ```toml 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 9eb7e7cc9c9e..4652d71a9ea3 100644 --- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md +++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md @@ -28,8 +28,8 @@ bullet points might be helpful: check out the Clippy commit of the current Rust `beta` branch. [Link][rust_beta_tools] * When writing the release notes for the **upcoming beta release**, you need to - check out the Clippy commit of the current Rust `master`. - [Link][rust_master_tools] + check out the Clippy commit of the current Rust `main`. + [Link][rust_main_tools] * When writing the (forgotten) release notes for a **past stable release**, you need to check out the Rust release tag of the stable release. [Link][rust_stable_tools] @@ -112,7 +112,7 @@ written for. If not, update the version to the changelog version. [changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md [forge]: https://forge.rust-lang.org/ -[rust_master_tools]: https://github.com/rust-lang/rust/tree/HEAD/src/tools/clippy +[rust_main_tools]: https://github.com/rust-lang/rust/tree/HEAD/src/tools/clippy [rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy [rust_stable_tools]: https://github.com/rust-lang/rust/releases [`beta-accepted`]: https://github.com/rust-lang/rust-clippy/issues?q=label%3Abeta-accepted+ diff --git a/src/tools/clippy/book/src/development/the_team.md b/src/tools/clippy/book/src/development/the_team.md index d22123231868..a663158c949b 100644 --- a/src/tools/clippy/book/src/development/the_team.md +++ b/src/tools/clippy/book/src/development/the_team.md @@ -51,7 +51,7 @@ this group to help with triaging, which can include: busy or wants to abandon it. If the reviewer is busy, the PR can be reassigned to someone else. - Checkout: https://triage.rust-lang.org/triage/rust-lang/rust-clippy to + Checkout: to monitor PRs. While not part of their duties, contributors are encouraged to review PRs diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 57ac01828e59..c87f8e9a68de 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -214,6 +214,23 @@ Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` * [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) +## `allow-unwrap-types` +List of types to allow `unwrap()` and `expect()` on. + +#### Example + +```toml +allow-unwrap-types = [ "std::sync::LockResult" ] +``` + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`expect_used`](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used) +* [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) + + ## `allow-useless-vec-in-tests` Whether `useless_vec` should ignore test functions or `#[cfg(test)]` @@ -505,6 +522,23 @@ The minimum digits a const float literal must have to supress the `excessive_pre * [`excessive_precision`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision) +## `disallowed-fields` +The list of disallowed fields, written as fully qualified paths. + +**Fields:** +- `path` (required): the fully qualified path to the field that should be disallowed +- `reason` (optional): explanation why this field is disallowed +- `replacement` (optional): suggested alternative method +- `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry + if the path doesn't exist, instead of emitting an error + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`disallowed_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_fields) + + ## `disallowed-macros` The list of disallowed macros, written as fully qualified paths. diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index da5166392b4e..366c776b8f1a 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.95" +version = "0.1.96" 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 3f4997a395a8..41099f94b044 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -413,6 +413,15 @@ fn span_from_toml_range(file: &SourceFile, span: Range) -> Span { /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` #[lints(unwrap_used)] allow_unwrap_in_tests: bool = false, + /// List of types to allow `unwrap()` and `expect()` on. + /// + /// #### Example + /// + /// ```toml + /// allow-unwrap-types = [ "std::sync::LockResult" ] + /// ``` + #[lints(expect_used, unwrap_used)] + allow_unwrap_types: Vec = Vec::new(), /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]` #[lints(useless_vec)] allow_useless_vec_in_tests: bool = false, @@ -581,6 +590,17 @@ fn span_from_toml_range(file: &SourceFile, span: Range) -> Span { /// Use the Cognitive Complexity lint instead. #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)] cyclomatic_complexity_threshold: u64 = 25, + /// The list of disallowed fields, written as fully qualified paths. + /// + /// **Fields:** + /// - `path` (required): the fully qualified path to the field that should be disallowed + /// - `reason` (optional): explanation why this field is disallowed + /// - `replacement` (optional): suggested alternative method + /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry + /// if the path doesn't exist, instead of emitting an error + #[disallowed_paths_allow_replacements = true] + #[lints(disallowed_fields)] + disallowed_fields: Vec = Vec::new(), /// The list of disallowed macros, written as fully qualified paths. /// /// **Fields:** diff --git a/src/tools/clippy/clippy_dev/src/edit_lints.rs b/src/tools/clippy/clippy_dev/src/edit_lints.rs index fb1c1458c50c..70c096783af8 100644 --- a/src/tools/clippy/clippy_dev/src/edit_lints.rs +++ b/src/tools/clippy/clippy_dev/src/edit_lints.rs @@ -1,11 +1,12 @@ use crate::parse::cursor::{self, Capture, Cursor}; -use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint}; -use crate::update_lints::generate_lint_files; +use crate::parse::{ActiveLint, DeprecatedLint, Lint, LintData, LintName, ParseCx, RenamedLint}; use crate::utils::{ ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target, }; +use core::mem; use rustc_lexer::TokenKind; +use std::collections::hash_map::Entry; use std::ffi::OsString; use std::fs; use std::path::Path; @@ -20,74 +21,51 @@ /// /// If a file path could not read from or written to pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'env str, reason: &'env str) { - let mut lints = cx.find_lint_decls(); - let (mut deprecated_lints, renamed_lints) = cx.read_deprecated_lints(); + let mut data = cx.parse_lint_decls(); - let Some(lint_idx) = lints.iter().position(|l| l.name == name) else { + let Entry::Occupied(mut lint) = data.lints.entry(name) else { eprintln!("error: failed to find lint `{name}`"); return; }; + let Lint::Active(prev_lint) = mem::replace( + lint.get_mut(), + Lint::Deprecated(DeprecatedLint { + reason, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }), + ) else { + eprintln!("error: `{name}` is already deprecated"); + return; + }; - let prefixed_name = cx.str_buf.with(|buf| { - buf.extend(["clippy::", name]); - cx.arena.alloc_str(buf) - }); - match deprecated_lints.binary_search_by(|x| x.name.cmp(prefixed_name)) { - Ok(_) => { - println!("`{name}` is already deprecated"); - return; - }, - Err(idx) => deprecated_lints.insert( - idx, - DeprecatedLint { - name: prefixed_name, - reason, - version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), - }, - ), - } - - remove_lint_declaration(lint_idx, &mut lints, &mut FileUpdater::default()); - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + remove_lint_declaration(name, &prev_lint, &data, &mut FileUpdater::default()); + data.gen_decls(UpdateMode::Change); println!("info: `{name}` has successfully been deprecated"); println!("note: you must run `cargo uitest` to update the test results"); } pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) { - let mut lints = cx.find_lint_decls(); - let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); + let mut data = cx.parse_lint_decls(); - let Some(lint_idx) = lints.iter().position(|l| l.name == old_name) else { + update_rename_targets(&mut data, old_name, LintName::new_rustc(new_name)); + + let Entry::Occupied(mut lint) = data.lints.entry(old_name) else { eprintln!("error: failed to find lint `{old_name}`"); return; }; - - let old_name_prefixed = cx.str_buf.with(|buf| { - buf.extend(["clippy::", old_name]); - cx.arena.alloc_str(buf) - }); - for lint in &mut renamed_lints { - if lint.new_name == old_name_prefixed { - lint.new_name = new_name; - } - } - match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) { - Ok(_) => { - println!("`{old_name}` is already deprecated"); - return; - }, - Err(idx) => renamed_lints.insert( - idx, - RenamedLint { - old_name: old_name_prefixed, - new_name, - version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), - }, - ), - } + let Lint::Active(prev_lint) = mem::replace( + lint.get_mut(), + Lint::Renamed(RenamedLint { + new_name: LintName::new_rustc(new_name), + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }), + ) else { + eprintln!("error: `{old_name}` is already deprecated"); + return; + }; let mut updater = FileUpdater::default(); - let remove_mod = remove_lint_declaration(lint_idx, &mut lints, &mut updater); + let remove_mod = remove_lint_declaration(old_name, &prev_lint, &data, &mut updater); let mut update_fn = uplift_update_fn(old_name, new_name, remove_mod); for e in walk_dir_no_dot_or_target(".") { let e = expect_action(e, ErrAction::Read, "."); @@ -95,7 +73,7 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam updater.update_file(e.path(), &mut update_fn); } } - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + data.gen_decls(UpdateMode::Change); println!("info: `{old_name}` has successfully been uplifted as `{new_name}`"); println!("note: you must run `cargo uitest` to update the test results"); } @@ -117,79 +95,50 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam /// * If `old_name` names a deprecated or renamed lint. pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) { let mut updater = FileUpdater::default(); - let mut lints = cx.find_lint_decls(); - let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); + let mut data = cx.parse_lint_decls(); - let Ok(lint_idx) = lints.binary_search_by(|x| x.name.cmp(old_name)) else { - panic!("could not find lint `{old_name}`"); + update_rename_targets(&mut data, old_name, LintName::new_clippy(new_name)); + + let Entry::Occupied(mut lint) = data.lints.entry(old_name) else { + eprintln!("error: failed to find lint `{old_name}`"); + return; + }; + let Lint::Active(mut prev_lint) = mem::replace( + lint.get_mut(), + Lint::Renamed(RenamedLint { + new_name: LintName::new_clippy(new_name), + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }), + ) else { + eprintln!("error: `{old_name}` is already deprecated"); + return; }; - let old_name_prefixed = cx.str_buf.with(|buf| { - buf.extend(["clippy::", old_name]); - cx.arena.alloc_str(buf) - }); - let new_name_prefixed = cx.str_buf.with(|buf| { - buf.extend(["clippy::", new_name]); - cx.arena.alloc_str(buf) - }); - - for lint in &mut renamed_lints { - if lint.new_name == old_name_prefixed { - lint.new_name = new_name_prefixed; - } - } - match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) { - Ok(_) => { - println!("`{old_name}` already has a rename registered"); - return; - }, - Err(idx) => { - renamed_lints.insert( - idx, - RenamedLint { - old_name: old_name_prefixed, - new_name: new_name_prefixed, - version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), - }, - ); - }, - } - let mut rename_mod = false; - if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() { - let lint = &mut lints[lint_idx]; - if lint.module.ends_with(old_name) - && lint + if let Entry::Vacant(e) = data.lints.entry(new_name) { + if prev_lint.module.ends_with(old_name) + && prev_lint .path .file_stem() .is_some_and(|x| x.as_encoded_bytes() == old_name.as_bytes()) { - let mut new_path = lint.path.with_file_name(new_name).into_os_string(); + let mut new_path = prev_lint.path.with_file_name(new_name).into_os_string(); new_path.push(".rs"); - if try_rename_file(lint.path.as_ref(), new_path.as_ref()) { + if try_rename_file(prev_lint.path.as_ref(), new_path.as_ref()) { rename_mod = true; } - lint.module = cx.str_buf.with(|buf| { - buf.push_str(&lint.module[..lint.module.len() - old_name.len()]); + prev_lint.module = cx.str_buf.with(|buf| { + buf.push_str(&prev_lint.module[..prev_lint.module.len() - old_name.len()]); buf.push_str(new_name); cx.arena.alloc_str(buf) }); } + e.insert(Lint::Active(prev_lint)); - rename_test_files( - old_name, - new_name, - &lints[lint_idx + 1..] - .iter() - .map(|l| l.name) - .take_while(|&n| n.starts_with(old_name)) - .collect::>(), - ); - lints[lint_idx].name = new_name; - lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); + rename_test_files(old_name, new_name, &create_ignored_prefixes(old_name, &data)); } else { - println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("Renamed `{old_name}` to `{new_name}`"); println!("Since `{new_name}` already exists the existing code has not been changed"); return; } @@ -201,9 +150,9 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam updater.update_file(e.path(), &mut update_fn); } } - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + data.gen_decls(UpdateMode::Change); - println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("Renamed `{old_name}` to `{new_name}`"); println!("All code referencing the old name has been updated"); println!("Make sure to inspect the results as some things may have been missed"); println!("note: `cargo uibless` still needs to be run to update the test results"); @@ -211,17 +160,22 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam /// Removes a lint's declaration and test files. Returns whether the module containing the /// lint was deleted. -fn remove_lint_declaration(lint_idx: usize, lints: &mut Vec>, updater: &mut FileUpdater) -> bool { - let lint = lints.remove(lint_idx); - let delete_mod = if lints.iter().all(|l| l.module != lint.module) { +fn remove_lint_declaration(name: &str, lint: &ActiveLint<'_>, data: &LintData<'_>, updater: &mut FileUpdater) -> bool { + let delete_mod = if data.lints.iter().all(|(_, l)| { + if let Lint::Active(l) = l { + l.module != lint.module + } else { + true + } + }) { delete_file_if_exists(lint.path.as_ref()) } else { updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus { - let mut start = &src[..lint.declaration_range.start]; + let mut start = &src[..lint.declaration_range.start as usize]; if start.ends_with("\n\n") { start = &start[..start.len() - 1]; } - let mut end = &src[lint.declaration_range.end..]; + let mut end = &src[lint.declaration_range.end as usize..]; if end.starts_with("\n\n") { end = &end[1..]; } @@ -231,18 +185,35 @@ fn remove_lint_declaration(lint_idx: usize, lints: &mut Vec>, updater: }); false }; - delete_test_files( - lint.name, - &lints[lint_idx..] - .iter() - .map(|l| l.name) - .take_while(|&n| n.starts_with(lint.name)) - .collect::>(), - ); + delete_test_files(name, &create_ignored_prefixes(name, data)); delete_mod } +/// Updates all renames to the old name to be renames to the new name. +/// +/// This is needed because rustc doesn't allow a lint to be renamed to a lint that has +/// also been renamed. +fn update_rename_targets<'cx>(data: &mut LintData<'cx>, old_name: &str, new_name: LintName<'cx>) { + let old_name = LintName::new_clippy(old_name); + for lint in data.lints.values_mut() { + if let Lint::Renamed(lint) = lint + && lint.new_name == old_name + { + lint.new_name = new_name; + } + } +} + +/// Creates a list of prefixes to ignore when +fn create_ignored_prefixes<'cx>(name: &str, data: &LintData<'cx>) -> Vec<&'cx str> { + data.lints + .keys() + .copied() + .filter(|&x| x.len() > name.len() && x.starts_with(name)) + .collect() +} + fn collect_ui_test_names(lint: &str, ignored_prefixes: &[&str], dst: &mut Vec<(OsString, bool)>) { for e in fs::read_dir("tests/ui").expect("error reading `tests/ui`") { let e = e.expect("error reading `tests/ui`"); diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index 781e37e6144e..d13f966b19c3 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -1,3 +1,6 @@ +use crate::generate::gen_sorted_lints_file; +use crate::new_parse_cx; +use crate::parse::VecBuf; use crate::utils::{ ErrAction, FileUpdater, UpdateMode, UpdateStatus, expect_action, run_with_output, split_args_for_threads, walk_dir_no_dot_or_target, @@ -326,10 +329,35 @@ fn run_rustfmt(update_mode: UpdateMode) { // the "main" function of cargo dev fmt pub fn run(update_mode: UpdateMode) { - run_rustfmt(update_mode); fmt_syms(update_mode); if let Err(e) = fmt_conf(update_mode.is_check()) { e.display(); process::exit(1); } + + new_parse_cx(|cx| { + let mut data = cx.parse_lint_decls(); + let (mut lints, passes) = data.split_by_lint_file(); + let mut updater = FileUpdater::default(); + let mut ranges = VecBuf::with_capacity(256); + + for passes in passes { + let path = passes[0].path.clone(); + let mut lints = lints.remove(&*path); + let lints = lints.as_deref_mut().unwrap_or_default(); + updater.update_file_checked("cargo dev fmt", update_mode, &path, &mut |_, src, dst| { + gen_sorted_lints_file(src, dst, lints, passes, &mut ranges); + UpdateStatus::from_changed(src != dst) + }); + } + + for (&path, lints) in &mut lints { + updater.update_file_checked("cargo dev fmt", update_mode, path, &mut |_, src, dst| { + gen_sorted_lints_file(src, dst, lints, &mut [], &mut ranges); + UpdateStatus::from_changed(src != dst) + }); + } + }); + + run_rustfmt(update_mode); } diff --git a/src/tools/clippy/clippy_dev/src/generate.rs b/src/tools/clippy/clippy_dev/src/generate.rs new file mode 100644 index 000000000000..24e4218716ab --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/generate.rs @@ -0,0 +1,316 @@ +use crate::parse::cursor::Cursor; +use crate::parse::{Lint, LintData, LintPass, VecBuf}; +use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; +use core::range::Range; +use itertools::Itertools; +use std::collections::HashSet; +use std::fmt::Write; +use std::path::{self, Path}; + +const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\ + // Use that command to update this file and do not edit by hand.\n\ + // Manual edits will be overwritten.\n\n"; + +const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; + +impl LintData<'_> { + #[expect(clippy::too_many_lines)] + pub fn gen_decls(&self, update_mode: UpdateMode) { + let mut updater = FileUpdater::default(); + + let mut lints: Vec<_> = self.lints.iter().map(|(&x, y)| (x, y)).collect(); + lints.sort_by_key(|&(x, _)| x); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "CHANGELOG.md", + &mut update_text_region_fn( + "\n", + "", + |dst| { + for &(lint, _) in &lints { + writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); + } + }, + ), + ); + + let mut active = Vec::with_capacity(lints.len()); + let mut deprecated = Vec::with_capacity(lints.len() / 8); + let mut renamed = Vec::with_capacity(lints.len() / 8); + for &(name, lint) in &lints { + match lint { + Lint::Active(lint) => active.push((name, lint)), + Lint::Deprecated(lint) => deprecated.push((name, lint)), + Lint::Renamed(lint) => renamed.push((name, lint)), + } + } + active.sort_by_key(|&(_, lint)| lint.module); + + // Round to avoid updating the readme every time a lint is added/deprecated. + let lint_count = active.len() / 50 * 50; + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{lint_count}").unwrap(); + }), + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "book/src/README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{lint_count}").unwrap(); + }), + ); + + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "clippy_lints/src/deprecated_lints.rs", + &mut |_, src, dst| { + let mut cursor = Cursor::new(src); + assert!( + cursor.find_ident("declare_with_version").is_some() + && cursor.find_ident("declare_with_version").is_some(), + "error reading deprecated lints" + ); + dst.push_str(&src[..cursor.pos() as usize]); + dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); + for &(name, data) in &deprecated { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n", + data.version, data.reason, + ) + .unwrap(); + } + dst.push_str( + "]}\n\n\ + #[rustfmt::skip]\n\ + declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\ + ", + ); + for &(name, data) in &renamed { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n", + data.version, data.new_name, + ) + .unwrap(); + } + dst.push_str("]}\n"); + UpdateStatus::from_changed(src != dst) + }, + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "tests/ui/deprecated.rs", + &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + for &(lint, _) in &deprecated { + writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }, + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "tests/ui/rename.rs", + &mut move |_, src, dst| { + let mut seen_lints = HashSet::new(); + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); + for &(_, lint) in &renamed { + if seen_lints.insert(lint.new_name) { + writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); + } + } + for &(lint, _) in &renamed { + writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }, + ); + for (crate_name, lints) in active.iter().copied().into_group_map_by(|&(_, lint)| { + let Some(path::Component::Normal(name)) = lint.path.components().next() else { + // All paths should start with `{crate_name}/src` when parsed from `find_lint_decls` + panic!( + "internal error: can't read crate name from path `{}`", + lint.path.display() + ); + }; + name + }) { + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + Path::new(crate_name).join("src/lib.rs"), + &mut update_text_region_fn( + "// begin lints modules, do not remove this comment, it's used in `update_lints`\n", + "// end lints modules, do not remove this comment, it's used in `update_lints`", + |dst| { + let mut prev = ""; + for &(_, lint) in &lints { + if lint.module != prev { + writeln!(dst, "mod {};", lint.module).unwrap(); + prev = lint.module; + } + } + }, + ), + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + Path::new(crate_name).join("src/declared_lints.rs"), + &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n"); + let mut buf = String::new(); + for &(name, lint) in &lints { + buf.clear(); + buf.push_str(name); + buf.make_ascii_uppercase(); + if lint.module.is_empty() { + writeln!(dst, " crate::{buf}_INFO,").unwrap(); + } else { + writeln!(dst, " crate::{}::{buf}_INFO,", lint.module).unwrap(); + } + } + dst.push_str("];\n"); + UpdateStatus::from_changed(src != dst) + }, + ); + } + } +} + +impl LintPass<'_> { + pub fn gen_mac(&self, dst: &mut String) { + let mut line_start = dst.len(); + dst.extend([self.mac.name(), "!("]); + let has_docs = write_comment_lines(self.docs, "\n ", dst); + let (list_indent, list_multi_end, end) = if has_docs { + dst.push_str("\n "); + line_start = dst.len() - 4; + (" ", "\n ", "]\n);") + } else { + (" ", "\n", "]);") + }; + dst.push_str(self.name); + if let Some(lt) = self.lt { + dst.extend(["<", lt, ">"]); + } + dst.push_str(" => ["); + let fmt = write_list( + self.lints.iter().copied(), + 80usize.saturating_sub(dst.len() - line_start), + list_indent, + dst, + ); + if matches!(fmt, ListFmt::MultiLine) { + dst.push_str(list_multi_end); + } + dst.push_str(end); + } +} + +fn write_comment_lines(s: &str, prefix: &str, dst: &mut String) -> bool { + let mut has_doc = false; + for line in s.split('\n') { + let line = line.trim_start(); + if !line.is_empty() { + has_doc = true; + dst.extend([prefix, line]); + } + } + has_doc +} + +#[derive(Clone, Copy)] +enum ListFmt { + SingleLine, + MultiLine, +} + +fn write_list<'a>( + items: impl Iterator + Clone, + single_line_limit: usize, + indent: &str, + dst: &mut String, +) -> ListFmt { + let len = items.clone().map(str::len).sum::(); + if len > single_line_limit { + for item in items { + dst.extend(["\n", indent, item, ","]); + } + ListFmt::MultiLine + } else { + let _ = write!(dst, "{}", items.format(", ")); + ListFmt::SingleLine + } +} + +/// Generates the contents of a lint's source file with all the lint and lint pass +/// declarations sorted. +pub fn gen_sorted_lints_file( + src: &str, + dst: &mut String, + lints: &mut [(&str, Range)], + passes: &mut [LintPass<'_>], + ranges: &mut VecBuf>, +) { + ranges.with(|ranges| { + ranges.extend(lints.iter().map(|&(_, x)| x)); + ranges.extend(passes.iter().map(|x| x.decl_range)); + ranges.sort_unstable_by_key(|x| x.start); + + lints.sort_unstable_by_key(|&(x, _)| x); + passes.sort_by_key(|x| x.name); + + let mut ranges = ranges.iter(); + let pos = if let Some(range) = ranges.next() { + dst.push_str(&src[..range.start as usize]); + for &(_, range) in &*lints { + dst.push_str(&src[range.start as usize..range.end as usize]); + dst.push_str("\n\n"); + } + for pass in passes { + pass.gen_mac(dst); + dst.push_str("\n\n"); + } + range.end + } else { + dst.push_str(src); + return; + }; + + let pos = ranges.fold(pos, |start, range| { + let s = &src[start as usize..range.start as usize]; + dst.push_str(if s.trim_start().is_empty() { + // Only whitespace between this and the previous item. No need to keep that. + "" + } else if src[..pos as usize].ends_with("\n\n") + && let Some(s) = s.strip_prefix("\n\n") + { + // Empty line before and after. Remove one of them. + s + } else { + // Remove only full lines unless something is in the way. + s.strip_prefix('\n').unwrap_or(s) + }); + range.end + }); + + // Since we always generate an empty line at the end, make sure to always skip it. + let s = &src[pos as usize..]; + dst.push_str(s.strip_prefix('\n').map_or(s, |s| s.strip_prefix('\n').unwrap_or(s))); + }); +} diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 69309403c8d0..359bf17c68c5 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,6 +1,5 @@ #![feature( exit_status_error, - if_let_guard, new_range, new_range_api, os_str_slice, @@ -19,6 +18,7 @@ #![allow(clippy::missing_panics_doc)] extern crate rustc_arena; +extern crate rustc_data_structures; #[expect(unused_extern_crates, reason = "required to link to rustc crates")] extern crate rustc_driver; extern crate rustc_lexer; @@ -32,8 +32,8 @@ pub mod serve; pub mod setup; pub mod sync; -pub mod update_lints; +mod generate; mod parse; mod utils; diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 8dc2290df8e4..5fe2295a1e21 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -5,7 +5,6 @@ use clap::{Args, Parser, Subcommand}; use clippy_dev::{ ClippyInfo, UpdateMode, dogfood, edit_lints, fmt, lint, new_lint, new_parse_cx, release, serve, setup, sync, - update_lints, }; use std::env; @@ -27,7 +26,9 @@ fn main() { allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), DevCommand::Fmt { check } => fmt::run(UpdateMode::from_check(check)), - DevCommand::UpdateLints { check } => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::from_check(check))), + DevCommand::UpdateLints { check } => { + new_parse_cx(|cx| cx.parse_lint_decls().gen_decls(UpdateMode::from_check(check))); + }, DevCommand::NewLint { pass, name, @@ -35,7 +36,7 @@ fn main() { r#type, msrv, } => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) { - Ok(()) => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::Change)), + Ok(()) => new_parse_cx(|cx| cx.parse_lint_decls().gen_decls(UpdateMode::Change)), Err(e) => eprintln!("Unable to create lint: {e}"), }, DevCommand::Setup(SetupCommand { subcommand }) => match subcommand { diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 72f281ca4d9d..2abe471bed2b 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -526,18 +526,14 @@ fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) { let mut captures = [Capture::EMPTY]; while let Some(name) = cursor.find_any_ident() { match cursor.get_text(name) { - "declare_clippy_lint" => { - if cursor.match_all(&[Bang, OpenBrace], &mut []) && cursor.find_pat(CloseBrace) { - decl_end = Some(cursor.pos()); - } + "declare_clippy_lint" if cursor.match_all(&[Bang, OpenBrace], &mut []) && cursor.find_pat(CloseBrace) => { + decl_end = Some(cursor.pos()); }, - "impl" => { - if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut captures) { - match cursor.get_text(captures[0]) { - "LateLintPass" => context = Some("LateContext"), - "EarlyLintPass" => context = Some("EarlyContext"), - _ => {}, - } + "impl" if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut captures) => { + match cursor.get_text(captures[0]) { + "LateLintPass" => context = Some("LateContext"), + "EarlyLintPass" => context = Some("EarlyContext"), + _ => {}, } }, _ => {}, diff --git a/src/tools/clippy/clippy_dev/src/parse.rs b/src/tools/clippy/clippy_dev/src/parse.rs index de5caf4e1ef6..d51fb25552f9 100644 --- a/src/tools/clippy/clippy_dev/src/parse.rs +++ b/src/tools/clippy/clippy_dev/src/parse.rs @@ -1,10 +1,11 @@ pub mod cursor; use self::cursor::{Capture, Cursor}; -use crate::utils::{ErrAction, File, Scoped, expect_action, walk_dir_no_dot_or_target}; -use core::fmt::{Display, Write as _}; +use crate::utils::{ErrAction, File, Scoped, expect_action, slice_groups_mut, walk_dir_no_dot_or_target}; +use core::fmt::{self, Display, Write as _}; use core::range::Range; use rustc_arena::DroplessArena; +use rustc_data_structures::fx::FxHashMap; use std::fs; use std::path::{self, Path, PathBuf}; use std::str::pattern::Pattern; @@ -12,6 +13,7 @@ pub struct ParseCxImpl<'cx> { pub arena: &'cx DroplessArena, pub str_buf: StrBuf, + pub str_list_buf: VecBuf<&'cx str>, } pub type ParseCx<'cx> = &'cx mut ParseCxImpl<'cx>; @@ -21,6 +23,7 @@ pub fn new_parse_cx<'env, T>(f: impl for<'cx> FnOnce(&'cx mut Scoped<'cx, 'env, f(&mut Scoped::new(ParseCxImpl { arena: &arena, str_buf: StrBuf::with_capacity(128), + str_list_buf: VecBuf::with_capacity(128), })) } @@ -81,31 +84,150 @@ pub fn with(&mut self, f: impl FnOnce(&mut String) -> T) -> T { } } -pub struct Lint<'cx> { +pub struct VecBuf(Vec); +impl VecBuf { + /// Creates a new buffer with the specified initial capacity. + pub fn with_capacity(cap: usize) -> Self { + Self(Vec::with_capacity(cap)) + } + + /// Performs an operation with the freshly cleared buffer. + pub fn with(&mut self, f: impl FnOnce(&mut Vec) -> R) -> R { + self.0.clear(); + f(&mut self.0) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub enum LintTool { + Rustc, + Clippy, +} +impl LintTool { + /// Gets the namespace prefix to use when naming a lint including the `::`. + pub fn prefix(self) -> &'static str { + match self { + Self::Rustc => "", + Self::Clippy => "clippy::", + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct LintName<'cx> { pub name: &'cx str, + pub tool: LintTool, +} +impl<'cx> LintName<'cx> { + pub fn new_rustc(name: &'cx str) -> Self { + Self { + name, + tool: LintTool::Rustc, + } + } + + pub fn new_clippy(name: &'cx str) -> Self { + Self { + name, + tool: LintTool::Clippy, + } + } +} +impl Display for LintName<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.tool.prefix())?; + f.write_str(self.name) + } +} + +pub struct ActiveLint<'cx> { pub group: &'cx str, pub module: &'cx str, pub path: PathBuf, - pub declaration_range: Range, + pub declaration_range: Range, } pub struct DeprecatedLint<'cx> { - pub name: &'cx str, pub reason: &'cx str, pub version: &'cx str, } pub struct RenamedLint<'cx> { - pub old_name: &'cx str, - pub new_name: &'cx str, + pub new_name: LintName<'cx>, pub version: &'cx str, } +pub enum Lint<'cx> { + Active(ActiveLint<'cx>), + Deprecated(DeprecatedLint<'cx>), + Renamed(RenamedLint<'cx>), +} + +#[derive(Clone, Copy)] +pub enum LintPassMac { + Declare, + Impl, +} +impl LintPassMac { + pub fn name(self) -> &'static str { + match self { + Self::Declare => "declare_lint_pass", + Self::Impl => "impl_lint_pass", + } + } +} + +pub struct LintPass<'cx> { + /// The raw text of the documentation comments. May include leading/trailing + /// whitespace and empty lines. + pub docs: &'cx str, + pub name: &'cx str, + pub lt: Option<&'cx str>, + pub mac: LintPassMac, + pub decl_range: Range, + pub lints: &'cx [&'cx str], + pub path: PathBuf, +} + +pub struct LintData<'cx> { + pub lints: FxHashMap<&'cx str, Lint<'cx>>, + pub lint_passes: Vec>, +} +impl<'cx> LintData<'cx> { + #[expect(clippy::type_complexity)] + pub fn split_by_lint_file<'s>( + &'s mut self, + ) -> ( + FxHashMap<&'s Path, Vec<(&'s str, Range)>>, + impl Iterator]>, + ) { + #[expect(clippy::default_trait_access)] + let mut lints = FxHashMap::with_capacity_and_hasher(500, Default::default()); + for (&name, lint) in &self.lints { + if let Lint::Active(lint) = lint { + lints + .entry(&*lint.path) + .or_insert_with(|| Vec::with_capacity(8)) + .push((name, lint.declaration_range)); + } + } + let passes = slice_groups_mut(&mut self.lint_passes, |head, tail| { + tail.iter().take_while(|&x| x.path == head.path).count() + }); + (lints, passes) + } +} + impl<'cx> ParseCxImpl<'cx> { - /// Finds all lint declarations (`declare_clippy_lint!`) + /// Finds and parses all lint declarations. #[must_use] - pub fn find_lint_decls(&mut self) -> Vec> { - let mut lints = Vec::with_capacity(1000); + pub fn parse_lint_decls(&mut self) -> LintData<'cx> { + let mut data = LintData { + #[expect(clippy::default_trait_access)] + lints: FxHashMap::with_capacity_and_hasher(1000, Default::default()), + lint_passes: Vec::with_capacity(400), + }; + let mut contents = String::new(); for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") { let e = expect_action(e, ErrAction::Read, "."); @@ -139,25 +261,26 @@ pub fn find_lint_decls(&mut self) -> Vec> { self.str_buf .alloc_replaced(self.arena, path, path::MAIN_SEPARATOR, "::") }; - self.parse_clippy_lint_decls( + self.parse_lint_src_file( e.path(), File::open_read_to_cleared_string(e.path(), &mut contents), module, - &mut lints, + &mut data, ); } } } - lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); - lints + + self.read_deprecated_lints(&mut data); + data } /// Parse a source file looking for `declare_clippy_lint` macro invocations. - fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx str, lints: &mut Vec>) { + fn parse_lint_src_file(&mut self, path: &Path, contents: &str, module: &'cx str, data: &mut LintData<'cx>) { #[allow(clippy::enum_glob_use)] use cursor::Pat::*; #[rustfmt::skip] - static DECL_TOKENS: &[cursor::Pat<'_>] = &[ + static LINT_DECL_TOKENS: &[cursor::Pat<'_>] = &[ // !{ /// docs Bang, OpenBrace, AnyComment, // #[clippy::version = "version"] @@ -165,24 +288,106 @@ fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx // pub NAME, GROUP, Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma, ]; + #[rustfmt::skip] + static PASS_DECL_TOKENS: &[cursor::Pat<'_>] = &[ + // !( NAME <'lt> => [ + Bang, OpenParen, CaptureDocLines, CaptureIdent, CaptureOptLifetimeArg, FatArrow, OpenBracket, + ]; let mut cursor = Cursor::new(contents); - let mut captures = [Capture::EMPTY; 2]; - while let Some(start) = cursor.find_ident("declare_clippy_lint") { - if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) { - lints.push(Lint { - name: self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])), - group: self.arena.alloc_str(cursor.get_text(captures[1])), - module, - path: path.into(), - declaration_range: start as usize..cursor.pos() as usize, - }); + let mut captures = [Capture::EMPTY; 3]; + while let Some(mac_name) = cursor.find_any_ident() { + match cursor.get_text(mac_name) { + "declare_clippy_lint" + if cursor.match_all(LINT_DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) => + { + assert!( + data.lints + .insert( + self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])), + Lint::Active(ActiveLint { + group: self.arena.alloc_str(cursor.get_text(captures[1])), + module, + path: path.into(), + declaration_range: mac_name.pos..cursor.pos(), + }), + ) + .is_none() + ); + }, + mac @ ("declare_lint_pass" | "impl_lint_pass") if cursor.match_all(PASS_DECL_TOKENS, &mut captures) => { + let mac = if matches!(mac, "declare_lint_pass") { + LintPassMac::Declare + } else { + LintPassMac::Impl + }; + let docs = match cursor.get_text(captures[0]) { + "" => "", + x => self.arena.alloc_str(x), + }; + let name = self.arena.alloc_str(cursor.get_text(captures[1])); + let lt = cursor.get_text(captures[2]); + let lt = if lt.is_empty() { + None + } else { + Some(self.arena.alloc_str(lt)) + }; + + let lints = self.str_list_buf.with(|buf| { + // Parses a comma separated list of paths and converts each path + // to a string with whitespace removed. + while !cursor.match_pat(CloseBracket) { + buf.push(self.str_buf.with(|buf| { + if cursor.match_pat(DoubleColon) { + buf.push_str("::"); + } + let capture = cursor.capture_ident()?; + buf.push_str(cursor.get_text(capture)); + while cursor.match_pat(DoubleColon) { + buf.push_str("::"); + let capture = cursor.capture_ident()?; + buf.push_str(cursor.get_text(capture)); + } + Some(self.arena.alloc_str(buf)) + })?); + + if !cursor.match_pat(Comma) { + if !cursor.match_pat(CloseBracket) { + return None; + } + break; + } + } + + // The arena panics when allocating a size of zero. + Some(if buf.is_empty() { + &[] + } else { + buf.sort_unstable(); + &*self.arena.alloc_slice(buf) + }) + }); + + if let Some(lints) = lints + && cursor.match_all(&[CloseParen, Semi], &mut []) + { + data.lint_passes.push(LintPass { + docs, + name, + lt, + mac, + decl_range: mac_name.pos..cursor.pos(), + lints, + path: path.into(), + }); + } + }, + _ => {}, } } } - #[must_use] - pub fn read_deprecated_lints(&mut self) -> (Vec>, Vec>) { + fn read_deprecated_lints(&mut self, data: &mut LintData<'cx>) { #[allow(clippy::enum_glob_use)] use cursor::Pat::*; #[rustfmt::skip] @@ -204,8 +409,6 @@ pub fn read_deprecated_lints(&mut self) -> (Vec>, Vec (Vec>, Vec (Vec>, Vec &'cx str { ); value } + + fn parse_clippy_lint_name(&mut self, path: &Path, s: &str) -> &'cx str { + match self.parse_str_single_line(path, s).strip_prefix("clippy::") { + Some(x) => x, + None => panic!( + "error parsing `{}`: `{s}` should be a string starting with `clippy::`", + path.display() + ), + } + } + + fn parse_lint_name(&mut self, path: &Path, s: &str) -> LintName<'cx> { + let s = self.parse_str_single_line(path, s); + let (name, tool) = match s.strip_prefix("clippy::") { + Some(s) => (s, LintTool::Clippy), + None => (s, LintTool::Rustc), + }; + LintName { name, tool } + } } diff --git a/src/tools/clippy/clippy_dev/src/parse/cursor.rs b/src/tools/clippy/clippy_dev/src/parse/cursor.rs index 2c142af4883a..af840532c27b 100644 --- a/src/tools/clippy/clippy_dev/src/parse/cursor.rs +++ b/src/tools/clippy/clippy_dev/src/parse/cursor.rs @@ -12,6 +12,7 @@ pub enum Pat<'a> { /// Matches any number of comments and doc comments. AnyComment, Ident(&'a str), + CaptureDocLines, CaptureIdent, LitStr, CaptureLitStr, @@ -22,12 +23,14 @@ pub enum Pat<'a> { Comma, DoubleColon, Eq, + FatArrow, Lifetime, Lt, Gt, OpenBrace, OpenBracket, OpenParen, + CaptureOptLifetimeArg, Pound, Semi, } @@ -112,6 +115,7 @@ pub fn step(&mut self) { /// /// For each capture made by the pattern one item will be taken from the capture /// sequence with the result placed inside. + #[expect(clippy::too_many_lines)] fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture>) -> bool { loop { match (pat, self.next_token.kind) { @@ -121,7 +125,6 @@ fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture Pat::AnyComment, TokenKind::BlockComment { terminated: true, .. } | TokenKind::LineComment { .. }, ) => self.step(), - (Pat::AnyComment, _) => return true, (Pat::Bang, TokenKind::Bang) | (Pat::CloseBrace, TokenKind::CloseBrace) | (Pat::CloseBracket, TokenKind::CloseBracket) @@ -152,12 +155,48 @@ fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture }, (Pat::DoubleColon, TokenKind::Colon) => { self.step(); - if !self.at_end() && matches!(self.next_token.kind, TokenKind::Colon) { + if matches!(self.next_token.kind, TokenKind::Colon) { self.step(); return true; } return false; }, + (Pat::FatArrow, TokenKind::Eq) => { + self.step(); + if matches!(self.next_token.kind, TokenKind::Gt) { + self.step(); + return true; + } + return false; + }, + (Pat::CaptureOptLifetimeArg, TokenKind::Lt) => { + self.step(); + loop { + match self.next_token.kind { + TokenKind::Lifetime { .. } => break, + TokenKind::Whitespace => self.step(), + _ => return false, + } + } + *captures.next().unwrap() = Capture { + pos: self.pos, + len: self.next_token.len, + }; + self.step(); + loop { + match self.next_token.kind { + TokenKind::Gt => break, + TokenKind::Whitespace => self.step(), + _ => return false, + } + } + self.step(); + return true; + }, + (Pat::CaptureOptLifetimeArg, _) => { + *captures.next().unwrap() = Capture { pos: 0, len: 0 }; + return true; + }, #[rustfmt::skip] ( Pat::CaptureLitStr, @@ -173,6 +212,28 @@ fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture self.step(); return true; }, + (Pat::CaptureDocLines, TokenKind::LineComment { doc_style: Some(_) }) => { + let pos = self.pos; + loop { + self.step(); + if !matches!( + self.next_token.kind, + TokenKind::Whitespace | TokenKind::LineComment { doc_style: Some(_) } + ) { + break; + } + } + *captures.next().unwrap() = Capture { + pos, + len: self.pos - pos, + }; + return true; + }, + (Pat::CaptureDocLines, _) => { + *captures.next().unwrap() = Capture::EMPTY; + return true; + }, + (Pat::AnyComment, _) => return true, _ => return false, } } @@ -219,8 +280,8 @@ pub fn find_any_ident(&mut self) -> Option { } } - /// Consume the returns the position of the next non-whitespace token if it's an - /// identifier. Returns `None` otherwise. + /// Consume the returns the position of the next non-whitespace token if it's the + /// specified identifier. Returns `None` otherwise. pub fn match_ident(&mut self, s: &str) -> Option { loop { match self.next_token.kind { @@ -235,6 +296,23 @@ pub fn match_ident(&mut self, s: &str) -> Option { } } + /// Consumes and captures the next non-whitespace token if it's an identifier. Returns + /// `None` otherwise. + pub fn capture_ident(&mut self) -> Option { + loop { + match self.next_token.kind { + TokenKind::Ident => { + let pos = self.pos; + let len = self.next_token.len; + self.step(); + return Some(Capture { pos, len }); + }, + TokenKind::Whitespace => self.step(), + _ => return None, + } + } + } + /// Continually attempt to match the pattern on subsequent tokens until a match is /// found. Returns whether the pattern was successfully matched. /// diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs deleted file mode 100644 index 3d0da6846114..000000000000 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ /dev/null @@ -1,199 +0,0 @@ -use crate::parse::cursor::Cursor; -use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint}; -use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; -use itertools::Itertools; -use std::collections::HashSet; -use std::fmt::Write; -use std::path::{self, Path}; - -const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\ - // Use that command to update this file and do not edit by hand.\n\ - // Manual edits will be overwritten.\n\n"; - -const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; - -/// Runs the `update_lints` command. -/// -/// This updates various generated values from the lint source code. -/// -/// `update_mode` indicates if the files should be updated or if updates should be checked for. -/// -/// # Panics -/// -/// Panics if a file path could not read from or then written to -pub fn update(cx: ParseCx<'_>, update_mode: UpdateMode) { - let lints = cx.find_lint_decls(); - let (deprecated, renamed) = cx.read_deprecated_lints(); - generate_lint_files(update_mode, &lints, &deprecated, &renamed); -} - -#[expect(clippy::too_many_lines)] -pub fn generate_lint_files( - update_mode: UpdateMode, - lints: &[Lint<'_>], - deprecated: &[DeprecatedLint<'_>], - renamed: &[RenamedLint<'_>], -) { - let mut updater = FileUpdater::default(); - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - "README.md", - &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { - write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); - }), - ); - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - "book/src/README.md", - &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { - write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); - }), - ); - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - "CHANGELOG.md", - &mut update_text_region_fn( - "\n", - "", - |dst| { - for lint in lints - .iter() - .map(|l| l.name) - .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) - .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) - .sorted() - { - writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); - } - }, - ), - ); - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - "clippy_lints/src/deprecated_lints.rs", - &mut |_, src, dst| { - let mut cursor = Cursor::new(src); - assert!( - cursor.find_ident("declare_with_version").is_some() - && cursor.find_ident("declare_with_version").is_some(), - "error reading deprecated lints" - ); - dst.push_str(&src[..cursor.pos() as usize]); - dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); - for lint in deprecated { - write!( - dst, - " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", - lint.version, lint.name, lint.reason, - ) - .unwrap(); - } - dst.push_str( - "]}\n\n\ - #[rustfmt::skip]\n\ - declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\ - ", - ); - for lint in renamed { - write!( - dst, - " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", - lint.version, lint.old_name, lint.new_name, - ) - .unwrap(); - } - dst.push_str("]}\n"); - UpdateStatus::from_changed(src != dst) - }, - ); - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - "tests/ui/deprecated.rs", - &mut |_, src, dst| { - dst.push_str(GENERATED_FILE_COMMENT); - for lint in deprecated { - writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); - } - dst.push_str("\nfn main() {}\n"); - UpdateStatus::from_changed(src != dst) - }, - ); - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - "tests/ui/rename.rs", - &mut move |_, src, dst| { - let mut seen_lints = HashSet::new(); - dst.push_str(GENERATED_FILE_COMMENT); - dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); - for lint in renamed { - if seen_lints.insert(lint.new_name) { - writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); - } - } - seen_lints.clear(); - for lint in renamed { - if seen_lints.insert(lint.old_name) { - writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); - } - } - dst.push_str("\nfn main() {}\n"); - UpdateStatus::from_changed(src != dst) - }, - ); - for (crate_name, lints) in lints.iter().into_group_map_by(|&l| { - let Some(path::Component::Normal(name)) = l.path.components().next() else { - // All paths should start with `{crate_name}/src` when parsed from `find_lint_decls` - panic!("internal error: can't read crate name from path `{}`", l.path.display()); - }; - name - }) { - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - Path::new(crate_name).join("src/lib.rs"), - &mut update_text_region_fn( - "// begin lints modules, do not remove this comment, it's used in `update_lints`\n", - "// end lints modules, do not remove this comment, it's used in `update_lints`", - |dst| { - for lint_mod in lints - .iter() - .filter(|l| !l.module.is_empty()) - .map(|l| l.module.split_once("::").map_or(l.module, |x| x.0)) - .sorted() - .dedup() - { - writeln!(dst, "mod {lint_mod};").unwrap(); - } - }, - ), - ); - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - Path::new(crate_name).join("src/declared_lints.rs"), - &mut |_, src, dst| { - dst.push_str(GENERATED_FILE_COMMENT); - dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n"); - for (module_path, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() { - if module_path.is_empty() { - writeln!(dst, " crate::{lint_name}_INFO,").unwrap(); - } else { - writeln!(dst, " crate::{module_path}::{lint_name}_INFO,").unwrap(); - } - } - dst.push_str("];\n"); - UpdateStatus::from_changed(src != dst) - }, - ); - } -} - -fn round_to_fifty(count: usize) -> usize { - count / 50 * 50 -} diff --git a/src/tools/clippy/clippy_dev/src/utils.rs b/src/tools/clippy/clippy_dev/src/utils.rs index 52452dd86b49..1f931140467e 100644 --- a/src/tools/clippy/clippy_dev/src/utils.rs +++ b/src/tools/clippy/clippy_dev/src/utils.rs @@ -1,5 +1,6 @@ use core::fmt::{self, Display}; use core::marker::PhantomData; +use core::mem; use core::num::NonZero; use core::ops::{Deref, DerefMut}; use core::range::Range; @@ -600,3 +601,29 @@ pub fn walk_dir_no_dot_or_target(p: impl AsRef) -> impl Iterator( + slice: &mut [T], + split_idx: impl FnMut(&T, &[T]) -> usize, +) -> impl Iterator { + struct I<'a, T, F> { + slice: &'a mut [T], + split_idx: F, + } + impl<'a, T, F: FnMut(&T, &[T]) -> usize> Iterator for I<'a, T, F> { + type Item = &'a mut [T]; + fn next(&mut self) -> Option { + let (head, tail) = self.slice.split_first()?; + let idx = (self.split_idx)(head, tail) + 1; + // `mem::take` makes it so `self.slice` isn't reborrowed. + if let Some((head, tail)) = mem::take(&mut self.slice).split_at_mut_checked(idx) { + self.slice = tail; + Some(head) + } else { + self.slice = &mut []; + None + } + } + } + I { slice, split_idx } +} diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index c0804dbb0492..51e753efb52e 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.95" +version = "0.1.96" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/clippy_lints/src/absolute_paths.rs b/src/tools/clippy/clippy_lints/src/absolute_paths.rs index 1af6d448a93c..fd515939dfb8 100644 --- a/src/tools/clippy/clippy_lints/src/absolute_paths.rs +++ b/src/tools/clippy/clippy_lints/src/absolute_paths.rs @@ -52,6 +52,7 @@ restriction, "checks for usage of an item without a `use` statement" } + impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]); pub struct AbsolutePaths { diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs index 4f55968d5625..258970393023 100644 --- a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs +++ b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs @@ -28,6 +28,7 @@ suspicious, "almost complete range" } + impl_lint_pass!(AlmostCompleteRange => [ALMOST_COMPLETE_RANGE]); pub struct AlmostCompleteRange { diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index a3710ca51655..2ea921e5d461 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -39,6 +39,8 @@ "the approximate of a known float constant (in `std::fXX::consts`)" } +impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]); + // Tuples are of the form (constant, name, min_digits, msrv) const KNOWN_CONSTS: [(f64, &str, usize, Option); 19] = [ (f64::E, "E", 4, None), @@ -111,8 +113,6 @@ fn check_known_consts(&self, cx: &LateContext<'_>, span: Span, s: symbol::Symbol } } -impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]); - fn count_digits_after_dot(input: &str) -> usize { input .char_indices() diff --git a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs index acfdfa65baed..e449b06199d3 100644 --- a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs +++ b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs @@ -39,6 +39,7 @@ suspicious, "using `Arc` with a type that does not implement `Send` and `Sync`" } + declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]); impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { diff --git a/src/tools/clippy/clippy_lints/src/asm_syntax.rs b/src/tools/clippy/clippy_lints/src/asm_syntax.rs index 69a8eb7d94e7..3c5cf74d5a17 100644 --- a/src/tools/clippy/clippy_lints/src/asm_syntax.rs +++ b/src/tools/clippy/clippy_lints/src/asm_syntax.rs @@ -57,54 +57,6 @@ fn check_asm_syntax( } } -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of Intel x86 assembly syntax. - /// - /// ### Why restrict this? - /// To enforce consistent use of AT&T x86 assembly syntax. - /// - /// ### Example - /// - /// ```rust,no_run - /// # #![feature(asm)] - /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - /// # unsafe { let ptr = "".as_ptr(); - /// # use std::arch::asm; - /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); - /// # } - /// ``` - /// Use instead: - /// ```rust,no_run - /// # #![feature(asm)] - /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - /// # unsafe { let ptr = "".as_ptr(); - /// # use std::arch::asm; - /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); - /// # } - /// ``` - #[clippy::version = "1.49.0"] - pub INLINE_ASM_X86_INTEL_SYNTAX, - restriction, - "prefer AT&T x86 assembly syntax" -} - -declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]); - -impl EarlyLintPass for InlineAsmX86IntelSyntax { - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if let ExprKind::InlineAsm(inline_asm) = &expr.kind { - check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel); - } - } - - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if let ItemKind::GlobalAsm(inline_asm) = &item.kind { - check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel); - } - } -} - declare_clippy_lint! { /// ### What it does /// Checks for usage of AT&T x86 assembly syntax. @@ -137,8 +89,56 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { "prefer Intel x86 assembly syntax" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of Intel x86 assembly syntax. + /// + /// ### Why restrict this? + /// To enforce consistent use of AT&T x86 assembly syntax. + /// + /// ### Example + /// + /// ```rust,no_run + /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// # unsafe { let ptr = "".as_ptr(); + /// # use std::arch::asm; + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// # } + /// ``` + /// Use instead: + /// ```rust,no_run + /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// # unsafe { let ptr = "".as_ptr(); + /// # use std::arch::asm; + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// # } + /// ``` + #[clippy::version = "1.49.0"] + pub INLINE_ASM_X86_INTEL_SYNTAX, + restriction, + "prefer AT&T x86 assembly syntax" +} + declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]); +declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]); + +impl EarlyLintPass for InlineAsmX86IntelSyntax { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::InlineAsm(inline_asm) = &expr.kind { + check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel); + } + } + + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if let ItemKind::GlobalAsm(inline_asm) = &item.kind { + check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel); + } + } +} + impl EarlyLintPass for InlineAsmX86AttSyntax { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::InlineAsm(inline_asm) = &expr.kind { diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs index 4aa55e53445c..6e57d0608bed 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -33,6 +33,7 @@ } impl_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); + pub struct AssertionsOnConstants { msrv: Msrv, } diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs index cc62306b33b5..d48ef10c8cd2 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; +use clippy_utils::macros::{find_assert_args, root_macro_call_first_node}; use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; use clippy_utils::sym; @@ -52,7 +52,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let Some(macro_call) = root_macro_call_first_node(cx, e) && matches!(cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::assert_macro)) && let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) - && matches!(panic_expn, PanicExpn::Empty) + && panic_expn.is_default_message() && let ExprKind::MethodCall(method_segment, recv, [], _) = condition.kind && let result_type_with_refs = cx.typeck_results().expr_ty(recv) && let result_type = result_type_with_refs.peel_refs() diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index efce23d13a38..60bc9b2b5b85 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -53,6 +53,8 @@ "assigning the result of cloning may be inefficient" } +impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); + pub struct AssigningClones { msrv: Msrv, } @@ -63,8 +65,6 @@ pub fn new(conf: &'static Conf) -> Self { } } -impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); - impl<'tcx> LateLintPass<'tcx> for AssigningClones { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Assign(lhs, rhs, _) = e.kind 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 bd6459d6f9db..e3b1a05bda7d 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs @@ -2,13 +2,14 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::sym; use rustc_ast::{LitKind, MetaItemLit}; +use rustc_hir::VERSION_PLACEHOLDER; use rustc_lint::EarlyContext; use rustc_span::Span; use semver::Version; pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) { if let LitKind::Str(is, _) = lit.kind - && (is == sym::TBD || Version::parse(is.as_str()).is_ok()) + && (is == sym::TBD || is.as_str() == VERSION_PLACEHOLDER || Version::parse(is.as_str()).is_ok()) { return; } diff --git a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs index fb86ae8da9d6..c0b14c2a4b66 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs @@ -1,6 +1,6 @@ use super::INLINE_ALWAYS; use clippy_utils::diagnostics::span_lint; -use rustc_hir::attrs::{AttributeKind, InlineAttr}; +use rustc_hir::attrs::InlineAttr; use rustc_hir::{Attribute, find_attr}; use rustc_lint::LateContext; use rustc_span::Span; @@ -11,7 +11,7 @@ pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Att return; } - if let Some(span) = find_attr!(attrs, AttributeKind::Inline(InlineAttr::Always, span) => *span) { + if let Some(span) = find_attr!(attrs, Inline(InlineAttr::Always, span) => *span) { span_lint( cx, INLINE_ALWAYS, diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index fa2951d91934..c15a378053e3 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -25,106 +25,60 @@ declare_clippy_lint! { /// ### What it does - /// Checks for items annotated with `#[inline(always)]`, - /// unless the annotated function is empty or simply panics. + /// Checks for usage of the `#[allow]` attribute and suggests replacing it with + /// the `#[expect]` attribute (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) + /// + /// This lint only warns outer attributes (`#[allow]`), as inner attributes + /// (`#![allow]`) are usually used to enable or disable lints on a global scale. /// /// ### Why is this bad? - /// While there are valid uses of this annotation (and once - /// you know when to use it, by all means `allow` this lint), it's a common - /// newbie-mistake to pepper one's code with it. - /// - /// As a rule of thumb, before slapping `#[inline(always)]` on a function, - /// measure if that additional function call really affects your runtime profile - /// sufficiently to make up for the increase in compile time. - /// - /// ### Known problems - /// False positives, big time. This lint is meant to be - /// deactivated by everyone doing serious performance work. This means having - /// done the measurement. + /// `#[expect]` attributes suppress the lint emission, but emit a warning, if + /// the expectation is unfulfilled. This can be useful to be notified when the + /// lint is no longer triggered. /// /// ### Example - /// ```ignore - /// #[inline(always)] - /// fn not_quite_hot_code(..) { ... } + /// ```rust,ignore + /// #[allow(unused_mut)] + /// fn foo() -> usize { + /// let mut a = Vec::new(); + /// a.len() + /// } /// ``` - #[clippy::version = "pre 1.29.0"] - pub INLINE_ALWAYS, - pedantic, - "use of `#[inline(always)]`" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `extern crate` and `use` items annotated with - /// lint attributes. - /// - /// 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 - /// - /// For `extern crate` items these lints are: - /// * `unused_imports` on items with `#[macro_use]` - /// - /// ### Why is this bad? - /// Lint attributes have no effect on crate imports. Most - /// likely a `!` was forgotten. - /// - /// ### Example - /// ```ignore - /// #[deny(dead_code)] - /// extern crate foo; - /// #[forbid(dead_code)] - /// use foo::bar; - /// ``` - /// /// Use instead: /// ```rust,ignore - /// #[allow(unused_imports)] - /// use foo::baz; - /// #[allow(unused_imports)] - /// #[macro_use] - /// extern crate baz; + /// #[expect(unused_mut)] + /// fn foo() -> usize { + /// let mut a = Vec::new(); + /// a.len() + /// } /// ``` - #[clippy::version = "pre 1.29.0"] - pub USELESS_ATTRIBUTE, - correctness, - "use of lint attributes on `extern crate` items" + #[clippy::version = "1.70.0"] + pub ALLOW_ATTRIBUTES, + restriction, + "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings." } declare_clippy_lint! { /// ### What it does - /// Checks for `#[deprecated]` annotations with a `since` - /// field that is not a valid semantic version. Also allows "TBD" to signal - /// future deprecation. + /// Checks for attributes that allow lints without a reason. /// - /// ### Why is this bad? - /// For checking the version of the deprecation, it must be - /// a valid semver. Failing that, the contained information is useless. + /// ### Why restrict this? + /// Justifying each `allow` helps readers understand the reasoning, + /// and may allow removing `allow` attributes if their purpose is obsolete. /// /// ### Example /// ```no_run - /// #[deprecated(since = "forever")] - /// fn something_else() { /* ... */ } + /// #![allow(clippy::some_lint)] /// ``` - #[clippy::version = "pre 1.29.0"] - pub DEPRECATED_SEMVER, - correctness, - "use of `#[deprecated(since = \"x\")]` where x is not semver" + /// + /// Use instead: + /// ```no_run + /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")] + /// ``` + #[clippy::version = "1.61.0"] + pub ALLOW_ATTRIBUTES_WITHOUT_REASON, + restriction, + "ensures that all `allow` and `expect` attributes have a reason" } declare_clippy_lint! { @@ -181,161 +135,6 @@ "usage of `cfg_attr(rustfmt)` instead of tool attributes" } -declare_clippy_lint! { - /// ### What it does - /// Checks for attributes that allow lints without a reason. - /// - /// ### Why restrict this? - /// Justifying each `allow` helps readers understand the reasoning, - /// and may allow removing `allow` attributes if their purpose is obsolete. - /// - /// ### Example - /// ```no_run - /// #![allow(clippy::some_lint)] - /// ``` - /// - /// Use instead: - /// ```no_run - /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")] - /// ``` - #[clippy::version = "1.61.0"] - pub ALLOW_ATTRIBUTES_WITHOUT_REASON, - restriction, - "ensures that all `allow` and `expect` attributes have a reason" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of the `#[allow]` attribute and suggests replacing it with - /// the `#[expect]` attribute (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) - /// - /// This lint only warns outer attributes (`#[allow]`), as inner attributes - /// (`#![allow]`) are usually used to enable or disable lints on a global scale. - /// - /// ### Why is this bad? - /// `#[expect]` attributes suppress the lint emission, but emit a warning, if - /// the expectation is unfulfilled. This can be useful to be notified when the - /// lint is no longer triggered. - /// - /// ### Example - /// ```rust,ignore - /// #[allow(unused_mut)] - /// fn foo() -> usize { - /// let mut a = Vec::new(); - /// a.len() - /// } - /// ``` - /// Use instead: - /// ```rust,ignore - /// #[expect(unused_mut)] - /// fn foo() -> usize { - /// let mut a = Vec::new(); - /// a.len() - /// } - /// ``` - #[clippy::version = "1.70.0"] - pub ALLOW_ATTRIBUTES, - restriction, - "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings." -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `#[should_panic]` attributes without specifying the expected panic message. - /// - /// ### Why is this bad? - /// The expected panic message should be specified to ensure that the test is actually - /// panicking with the expected message, and not another unrelated panic. - /// - /// ### Example - /// ```no_run - /// fn random() -> i32 { 0 } - /// - /// #[should_panic] - /// #[test] - /// fn my_test() { - /// let _ = 1 / random(); - /// } - /// ``` - /// - /// Use instead: - /// ```no_run - /// fn random() -> i32 { 0 } - /// - /// #[should_panic = "attempt to divide by zero"] - /// #[test] - /// fn my_test() { - /// let _ = 1 / random(); - /// } - /// ``` - #[clippy::version = "1.74.0"] - pub SHOULD_PANIC_WITHOUT_EXPECT, - pedantic, - "ensures that all `should_panic` attributes specify its expected panic message" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for items with `#[repr(packed)]`-attribute without ABI qualification - /// - /// ### Why is this bad? - /// Without qualification, `repr(packed)` implies `repr(Rust)`. The Rust-ABI is inherently unstable. - /// While this is fine as long as the type is accessed correctly within Rust-code, most uses - /// of `#[repr(packed)]` involve FFI and/or data structures specified by network-protocols or - /// other external specifications. In such situations, the unstable Rust-ABI implied in - /// `#[repr(packed)]` may lead to future bugs should the Rust-ABI change. - /// - /// In case you are relying on a well defined and stable memory layout, qualify the type's - /// representation using the `C`-ABI. Otherwise, if the type in question is only ever - /// accessed from Rust-code according to Rust's rules, use the `Rust`-ABI explicitly. - /// - /// ### Example - /// ```no_run - /// #[repr(packed)] - /// struct NetworkPacketHeader { - /// header_length: u8, - /// header_version: u16 - /// } - /// ``` - /// - /// Use instead: - /// ```no_run - /// #[repr(C, packed)] - /// struct NetworkPacketHeader { - /// header_length: u8, - /// header_version: u16 - /// } - /// ``` - #[clippy::version = "1.85.0"] - pub REPR_PACKED_WITHOUT_ABI, - suspicious, - "ensures that `repr(packed)` always comes with a qualified ABI" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `any` and `all` combinators in `cfg` with only one condition. - /// - /// ### Why is this bad? - /// If there is only one condition, no need to wrap it into `any` or `all` combinators. - /// - /// ### Example - /// ```no_run - /// #[cfg(any(unix))] - /// pub struct Bar; - /// ``` - /// - /// Use instead: - /// ```no_run - /// #[cfg(unix)] - /// pub struct Bar; - /// ``` - #[clippy::version = "1.71.0"] - pub NON_MINIMAL_CFG, - style, - "ensure that all `cfg(any())` and `cfg(all())` have more than one condition" -} - declare_clippy_lint! { /// ### What it does /// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for @@ -364,63 +163,23 @@ declare_clippy_lint! { /// ### What it does - /// Checks for `#[cfg_attr(clippy, allow(clippy::lint))]` - /// and suggests to replace it with `#[allow(clippy::lint)]`. + /// Checks for `#[deprecated]` annotations with a `since` + /// field that is not a valid semantic version. Also allows "TBD" to signal + /// future deprecation. /// /// ### Why is this bad? - /// There is no reason to put clippy attributes behind a clippy `cfg` as they are not - /// run by anything else than clippy. + /// For checking the version of the deprecation, it must be + /// a valid semver. Failing that, the contained information is useless. /// /// ### Example /// ```no_run - /// #![cfg_attr(clippy, allow(clippy::deprecated_cfg_attr))] + /// #[deprecated(since = "forever")] + /// fn something_else() { /* ... */ } /// ``` - /// - /// Use instead: - /// ```no_run - /// #![allow(clippy::deprecated_cfg_attr)] - /// ``` - #[clippy::version = "1.78.0"] - pub UNNECESSARY_CLIPPY_CFG, - suspicious, - "usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for items that have the same kind of attributes with mixed styles (inner/outer). - /// - /// ### Why is this bad? - /// Having both style of said attributes makes it more complicated to read code. - /// - /// ### Known problems - /// This lint currently has false-negatives when mixing same attributes - /// but they have different path symbols, for example: - /// ```ignore - /// #[custom_attribute] - /// pub fn foo() { - /// #![my_crate::custom_attribute] - /// } - /// ``` - /// - /// ### Example - /// ```no_run - /// #[cfg(linux)] - /// pub fn foo() { - /// #![cfg(windows)] - /// } - /// ``` - /// Use instead: - /// ```no_run - /// #[cfg(linux)] - /// #[cfg(windows)] - /// pub fn foo() { - /// } - /// ``` - #[clippy::version = "1.78.0"] - pub MIXED_ATTRIBUTES_STYLE, - style, - "item has both inner and outer attributes" + #[clippy::version = "pre 1.29.0"] + pub DEPRECATED_SEMVER, + correctness, + "use of `#[deprecated(since = \"x\")]` where x is not semver" } declare_clippy_lint! { @@ -478,15 +237,272 @@ "ignored tests without messages" } +declare_clippy_lint! { + /// ### What it does + /// Checks for items annotated with `#[inline(always)]`, + /// unless the annotated function is empty or simply panics. + /// + /// ### Why is this bad? + /// While there are valid uses of this annotation (and once + /// you know when to use it, by all means `allow` this lint), it's a common + /// newbie-mistake to pepper one's code with it. + /// + /// As a rule of thumb, before slapping `#[inline(always)]` on a function, + /// measure if that additional function call really affects your runtime profile + /// sufficiently to make up for the increase in compile time. + /// + /// ### Known problems + /// False positives, big time. This lint is meant to be + /// deactivated by everyone doing serious performance work. This means having + /// done the measurement. + /// + /// ### Example + /// ```ignore + /// #[inline(always)] + /// fn not_quite_hot_code(..) { ... } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub INLINE_ALWAYS, + pedantic, + "use of `#[inline(always)]`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for items that have the same kind of attributes with mixed styles (inner/outer). + /// + /// ### Why is this bad? + /// Having both style of said attributes makes it more complicated to read code. + /// + /// ### Known problems + /// This lint currently has false-negatives when mixing same attributes + /// but they have different path symbols, for example: + /// ```ignore + /// #[custom_attribute] + /// pub fn foo() { + /// #![my_crate::custom_attribute] + /// } + /// ``` + /// + /// ### Example + /// ```no_run + /// #[cfg(linux)] + /// pub fn foo() { + /// #![cfg(windows)] + /// } + /// ``` + /// Use instead: + /// ```no_run + /// #[cfg(linux)] + /// #[cfg(windows)] + /// pub fn foo() { + /// } + /// ``` + #[clippy::version = "1.78.0"] + pub MIXED_ATTRIBUTES_STYLE, + style, + "item has both inner and outer attributes" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `any` and `all` combinators in `cfg` with only one condition. + /// + /// ### Why is this bad? + /// If there is only one condition, no need to wrap it into `any` or `all` combinators. + /// + /// ### Example + /// ```no_run + /// #[cfg(any(unix))] + /// pub struct Bar; + /// ``` + /// + /// Use instead: + /// ```no_run + /// #[cfg(unix)] + /// pub struct Bar; + /// ``` + #[clippy::version = "1.71.0"] + pub NON_MINIMAL_CFG, + style, + "ensure that all `cfg(any())` and `cfg(all())` have more than one condition" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for items with `#[repr(packed)]`-attribute without ABI qualification + /// + /// ### Why is this bad? + /// Without qualification, `repr(packed)` implies `repr(Rust)`. The Rust-ABI is inherently unstable. + /// While this is fine as long as the type is accessed correctly within Rust-code, most uses + /// of `#[repr(packed)]` involve FFI and/or data structures specified by network-protocols or + /// other external specifications. In such situations, the unstable Rust-ABI implied in + /// `#[repr(packed)]` may lead to future bugs should the Rust-ABI change. + /// + /// In case you are relying on a well defined and stable memory layout, qualify the type's + /// representation using the `C`-ABI. Otherwise, if the type in question is only ever + /// accessed from Rust-code according to Rust's rules, use the `Rust`-ABI explicitly. + /// + /// ### Example + /// ```no_run + /// #[repr(packed)] + /// struct NetworkPacketHeader { + /// header_length: u8, + /// header_version: u16 + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// #[repr(C, packed)] + /// struct NetworkPacketHeader { + /// header_length: u8, + /// header_version: u16 + /// } + /// ``` + #[clippy::version = "1.85.0"] + pub REPR_PACKED_WITHOUT_ABI, + suspicious, + "ensures that `repr(packed)` always comes with a qualified ABI" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `#[should_panic]` attributes without specifying the expected panic message. + /// + /// ### Why is this bad? + /// The expected panic message should be specified to ensure that the test is actually + /// panicking with the expected message, and not another unrelated panic. + /// + /// ### Example + /// ```no_run + /// fn random() -> i32 { 0 } + /// + /// #[should_panic] + /// #[test] + /// fn my_test() { + /// let _ = 1 / random(); + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// fn random() -> i32 { 0 } + /// + /// #[should_panic = "attempt to divide by zero"] + /// #[test] + /// fn my_test() { + /// let _ = 1 / random(); + /// } + /// ``` + #[clippy::version = "1.74.0"] + pub SHOULD_PANIC_WITHOUT_EXPECT, + pedantic, + "ensures that all `should_panic` attributes specify its expected panic message" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `#[cfg_attr(clippy, allow(clippy::lint))]` + /// and suggests to replace it with `#[allow(clippy::lint)]`. + /// + /// ### Why is this bad? + /// There is no reason to put clippy attributes behind a clippy `cfg` as they are not + /// run by anything else than clippy. + /// + /// ### Example + /// ```no_run + /// #![cfg_attr(clippy, allow(clippy::deprecated_cfg_attr))] + /// ``` + /// + /// Use instead: + /// ```no_run + /// #![allow(clippy::deprecated_cfg_attr)] + /// ``` + #[clippy::version = "1.78.0"] + pub UNNECESSARY_CLIPPY_CFG, + suspicious, + "usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `extern crate` and `use` items annotated with + /// lint attributes. + /// + /// 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 + /// + /// For `extern crate` items these lints are: + /// * `unused_imports` on items with `#[macro_use]` + /// + /// ### Why is this bad? + /// Lint attributes have no effect on crate imports. Most + /// likely a `!` was forgotten. + /// + /// ### Example + /// ```ignore + /// #[deny(dead_code)] + /// extern crate foo; + /// #[forbid(dead_code)] + /// use foo::bar; + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// #[allow(unused_imports)] + /// use foo::baz; + /// #[allow(unused_imports)] + /// #[macro_use] + /// extern crate baz; + /// ``` + #[clippy::version = "pre 1.29.0"] + pub USELESS_ATTRIBUTE, + correctness, + "use of lint attributes on `extern crate` items" +} + +impl_lint_pass!(Attributes => [INLINE_ALWAYS, REPR_PACKED_WITHOUT_ABI]); + +impl_lint_pass!(EarlyAttributes => [ + DEPRECATED_CFG_ATTR, + DEPRECATED_CLIPPY_CFG_ATTR, + NON_MINIMAL_CFG, + UNNECESSARY_CLIPPY_CFG, +]); + +impl_lint_pass!(PostExpansionEarlyAttributes => [ + ALLOW_ATTRIBUTES, + ALLOW_ATTRIBUTES_WITHOUT_REASON, + BLANKET_CLIPPY_RESTRICTION_LINTS, + DEPRECATED_SEMVER, + DUPLICATED_ATTRIBUTES, + IGNORE_WITHOUT_REASON, + MIXED_ATTRIBUTES_STYLE, + SHOULD_PANIC_WITHOUT_EXPECT, + USELESS_ATTRIBUTE, +]); + pub struct Attributes { msrv: Msrv, } -impl_lint_pass!(Attributes => [ - INLINE_ALWAYS, - REPR_PACKED_WITHOUT_ABI, -]); - impl Attributes { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv } @@ -529,13 +545,6 @@ pub fn new(conf: &'static Conf) -> Self { } } -impl_lint_pass!(EarlyAttributes => [ - DEPRECATED_CFG_ATTR, - NON_MINIMAL_CFG, - DEPRECATED_CLIPPY_CFG_ATTR, - UNNECESSARY_CLIPPY_CFG, -]); - impl EarlyLintPass for EarlyAttributes { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { deprecated_cfg_attr::check(cx, attr, &self.msrv); @@ -558,18 +567,6 @@ pub fn new(conf: &'static Conf) -> Self { } } -impl_lint_pass!(PostExpansionEarlyAttributes => [ - ALLOW_ATTRIBUTES, - ALLOW_ATTRIBUTES_WITHOUT_REASON, - DEPRECATED_SEMVER, - IGNORE_WITHOUT_REASON, - USELESS_ATTRIBUTE, - BLANKET_CLIPPY_RESTRICTION_LINTS, - SHOULD_PANIC_WITHOUT_EXPECT, - MIXED_ATTRIBUTES_STYLE, - DUPLICATED_ATTRIBUTES, -]); - impl EarlyLintPass for PostExpansionEarlyAttributes { fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) { blanket_clippy_restriction_lints::check_command_line(cx); diff --git a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs index 8a530e8cff2e..2a3e4b96b1cd 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::{AttributeKind, ReprAttr}; +use rustc_hir::attrs::ReprAttr; use rustc_hir::{Attribute, find_attr}; use rustc_lint::LateContext; use rustc_span::Span; @@ -9,7 +9,7 @@ use super::REPR_PACKED_WITHOUT_ABI; pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], msrv: Msrv) { - if let Some(reprs) = find_attr!(attrs, AttributeKind::Repr { reprs, .. } => reprs) { + if let Some(reprs) = find_attr!(attrs, Repr { reprs, .. } => reprs) { let packed_span = reprs .iter() .find(|(r, _)| matches!(r, ReprAttr::ReprPacked(..))) diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs index 31cc004f6855..9e5b57a05580 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -2,13 +2,51 @@ use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::{self, PathNS}; +use clippy_utils::sym; use rustc_hir as hir; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::CoroutineLayout; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Allows users to configure types which should not be held across await + /// suspension points. + /// + /// ### Why is this bad? + /// There are some types which are perfectly safe to use concurrently from + /// a memory access perspective, but that will cause bugs at runtime if + /// they are held in such a way. + /// + /// ### Example + /// + /// ```toml + /// await-holding-invalid-types = [ + /// # You can specify a type name + /// "CustomLockType", + /// # You can (optionally) specify a reason + /// { path = "OtherCustomLockType", reason = "Relies on a thread local" } + /// ] + /// ``` + /// + /// ```no_run + /// # async fn baz() {} + /// struct CustomLockType; + /// struct OtherCustomLockType; + /// async fn foo() { + /// let _x = CustomLockType; + /// let _y = OtherCustomLockType; + /// baz().await; // Lint violation + /// } + /// ``` + #[clippy::version = "1.62.0"] + pub AWAIT_HOLDING_INVALID_TYPE, + suspicious, + "holding a type across an await point which is not allowed to be held as per the configuration" +} declare_clippy_lint! { /// ### What it does @@ -134,44 +172,11 @@ "inside an async function, holding a `RefCell` ref while calling `await`" } -declare_clippy_lint! { - /// ### What it does - /// Allows users to configure types which should not be held across await - /// suspension points. - /// - /// ### Why is this bad? - /// There are some types which are perfectly safe to use concurrently from - /// a memory access perspective, but that will cause bugs at runtime if - /// they are held in such a way. - /// - /// ### Example - /// - /// ```toml - /// await-holding-invalid-types = [ - /// # You can specify a type name - /// "CustomLockType", - /// # You can (optionally) specify a reason - /// { path = "OtherCustomLockType", reason = "Relies on a thread local" } - /// ] - /// ``` - /// - /// ```no_run - /// # async fn baz() {} - /// struct CustomLockType; - /// struct OtherCustomLockType; - /// async fn foo() { - /// let _x = CustomLockType; - /// let _y = OtherCustomLockType; - /// baz().await; // Lint violation - /// } - /// ``` - #[clippy::version = "1.62.0"] - pub AWAIT_HOLDING_INVALID_TYPE, - suspicious, - "holding a type across an await point which is not allowed to be held as per the configuration" -} - -impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); +impl_lint_pass!(AwaitHolding => [ + AWAIT_HOLDING_INVALID_TYPE, + AWAIT_HOLDING_LOCK, + AWAIT_HOLDING_REFCELL_REF, +]); pub struct AwaitHolding { def_ids: DefIdMap<(&'static str, &'static DisallowedPathWithoutReplacement)>, 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 129e77478406..b98a20a90ccb 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 @@ -43,6 +43,7 @@ pedantic, "using if to convert bool to int" } + declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 0bd459d8b021..8b7619d11a83 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -74,6 +74,8 @@ "boolean expressions that contain terminals which can be eliminated" } +impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]); + // For each pairs, both orders are considered. const METHODS_WITH_NEGATION: [(Option, Symbol, Symbol); 3] = [ (None, sym::is_some, sym::is_none), @@ -91,8 +93,6 @@ pub fn new(conf: &'static Conf) -> Self { } } -impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]); - impl<'tcx> LateLintPass<'tcx> for NonminimalBool { fn check_fn( &mut self, diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs index 70c9c45a60c8..947b99696bb1 100644 --- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs +++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs @@ -2,7 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, is_mutable}; +use clippy_utils::{ + get_enclosing_closure, get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, is_mutable, + is_upvar_in_closure, path_to_local_with_projections, +}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -81,6 +84,16 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) { && (is_expr_temporary_value(cx, deref_target) || !potentially_bound_to_mutable_ref(cx, e)) && let Some(deref_text) = deref_target.span.get_source_text(cx) { + // `&*x` can be needed to shorten the borrow of `x`. Replacing it with `x` can be + // incorrect when `x` is a closure-captured upvar (e.g. a closure returning another + // closure that captures `x`). + if let Some(closure) = get_enclosing_closure(cx, e.hir_id) + && let Some(local_id) = path_to_local_with_projections(deref_target) + && is_upvar_in_closure(cx, closure, local_id) + { + return; + } + span_lint_and_then( cx, BORROW_DEREF_REF, diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs index 6f51f2343ab5..34b96b1d9d7a 100644 --- a/src/tools/clippy/clippy_lints/src/box_default.rs +++ b/src/tools/clippy/clippy_lints/src/box_default.rs @@ -1,15 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_default_equivalent; use clippy_utils::macros::macro_backtrace; use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::expr_sig; +use clippy_utils::{is_default_equivalent, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LangItem, LetStmt, Node, QPath, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs index fc9931439e93..6c023189f69e 100644 --- a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs +++ b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs @@ -27,6 +27,7 @@ style, "hard to read byte char slice" } + declare_lint_pass!(ByteCharSlice => [BYTE_CHAR_SLICES]); impl EarlyLintPass for ByteCharSlice { diff --git a/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs b/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs index 80514cb52e6e..e92e3d115b17 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs @@ -9,7 +9,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: b for package in &metadata.packages { // only run the lint if publish is `None` (`publish = true` or skipped entirely) // or if the vector isn't empty (`publish = ["something"]`) - if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || ignore_publish { + if package.publish.as_ref().is_none_or(|publish| !publish.is_empty()) || ignore_publish { if is_empty_str(package.description.as_ref()) { missing_warning(cx, package, "package.description"); } diff --git a/src/tools/clippy/clippy_lints/src/cargo/mod.rs b/src/tools/clippy/clippy_lints/src/cargo/mod.rs index 08d92adbacef..0aa6e4c3e8e2 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/mod.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/mod.rs @@ -56,126 +56,6 @@ "common metadata is defined in `Cargo.toml`" } -declare_clippy_lint! { - /// ### What it does - /// Checks for feature names with prefix `use-`, `with-` or suffix `-support` - /// - /// ### Why is this bad? - /// These prefixes and suffixes have no significant meaning. - /// - /// ### Example - /// ```toml - /// # The `Cargo.toml` with feature name redundancy - /// [features] - /// default = ["use-abc", "with-def", "ghi-support"] - /// use-abc = [] // redundant - /// with-def = [] // redundant - /// ghi-support = [] // redundant - /// ``` - /// - /// Use instead: - /// ```toml - /// [features] - /// default = ["abc", "def", "ghi"] - /// abc = [] - /// def = [] - /// ghi = [] - /// ``` - /// - #[clippy::version = "1.57.0"] - pub REDUNDANT_FEATURE_NAMES, - cargo, - "usage of a redundant feature name" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for negative feature names with prefix `no-` or `not-` - /// - /// ### Why is this bad? - /// Features are supposed to be additive, and negatively-named features violate it. - /// - /// ### Example - /// ```toml - /// # The `Cargo.toml` with negative feature names - /// [features] - /// default = [] - /// no-abc = [] - /// not-def = [] - /// - /// ``` - /// Use instead: - /// ```toml - /// [features] - /// default = ["abc", "def"] - /// abc = [] - /// def = [] - /// - /// ``` - #[clippy::version = "1.57.0"] - pub NEGATIVE_FEATURE_NAMES, - cargo, - "usage of a negative feature name" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks to see if multiple versions of a crate are being - /// used. - /// - /// ### Why is this bad? - /// This bloats the size of targets, and can lead to - /// confusing error messages when structs or traits are used interchangeably - /// between different versions of a crate. - /// - /// ### Known problems - /// Because this can be caused purely by the dependencies - /// themselves, it's not always possible to fix this issue. - /// In those cases, you can allow that specific crate using - /// the `allowed-duplicate-crates` configuration option. - /// - /// ### Example - /// ```toml - /// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning. - /// [dependencies] - /// ctrlc = "=3.1.0" - /// ansi_term = "=0.11.0" - /// ``` - #[clippy::version = "pre 1.29.0"] - pub MULTIPLE_CRATE_VERSIONS, - cargo, - "multiple versions of the same crate being used" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for wildcard dependencies in the `Cargo.toml`. - /// - /// ### Why is this bad? - /// [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html), - /// it is highly unlikely that you work with any possible version of your dependency, - /// and wildcard dependencies would cause unnecessary breakage in the ecosystem. - /// - /// ### Example - /// ```toml - /// [dependencies] - /// regex = "*" - /// ``` - /// Use instead: - /// ```toml - /// [dependencies] - /// # allow patch updates, but not minor or major version changes - /// some_crate_1 = "~1.2.3" - /// - /// # pin the version to a specific version - /// some_crate_2 = "=1.2.3" - /// ``` - #[clippy::version = "1.32.0"] - pub WILDCARD_DEPENDENCIES, - cargo, - "wildcard dependencies being used" -} - declare_clippy_lint! { /// ### What it does /// Checks for lint groups with the same priority as lints in the `Cargo.toml` @@ -213,20 +93,140 @@ "a lint group in `Cargo.toml` at the same priority as a lint" } -pub struct Cargo { - allowed_duplicate_crates: FxHashSet, - ignore_publish: bool, +declare_clippy_lint! { + /// ### What it does + /// Checks to see if multiple versions of a crate are being + /// used. + /// + /// ### Why is this bad? + /// This bloats the size of targets, and can lead to + /// confusing error messages when structs or traits are used interchangeably + /// between different versions of a crate. + /// + /// ### Known problems + /// Because this can be caused purely by the dependencies + /// themselves, it's not always possible to fix this issue. + /// In those cases, you can allow that specific crate using + /// the `allowed-duplicate-crates` configuration option. + /// + /// ### Example + /// ```toml + /// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning. + /// [dependencies] + /// ctrlc = "=3.1.0" + /// ansi_term = "=0.11.0" + /// ``` + #[clippy::version = "pre 1.29.0"] + pub MULTIPLE_CRATE_VERSIONS, + cargo, + "multiple versions of the same crate being used" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for negative feature names with prefix `no-` or `not-` + /// + /// ### Why is this bad? + /// Features are supposed to be additive, and negatively-named features violate it. + /// + /// ### Example + /// ```toml + /// # The `Cargo.toml` with negative feature names + /// [features] + /// default = [] + /// no-abc = [] + /// not-def = [] + /// + /// ``` + /// Use instead: + /// ```toml + /// [features] + /// default = ["abc", "def"] + /// abc = [] + /// def = [] + /// + /// ``` + #[clippy::version = "1.57.0"] + pub NEGATIVE_FEATURE_NAMES, + cargo, + "usage of a negative feature name" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for feature names with prefix `use-`, `with-` or suffix `-support` + /// + /// ### Why is this bad? + /// These prefixes and suffixes have no significant meaning. + /// + /// ### Example + /// ```toml + /// # The `Cargo.toml` with feature name redundancy + /// [features] + /// default = ["use-abc", "with-def", "ghi-support"] + /// use-abc = [] // redundant + /// with-def = [] // redundant + /// ghi-support = [] // redundant + /// ``` + /// + /// Use instead: + /// ```toml + /// [features] + /// default = ["abc", "def", "ghi"] + /// abc = [] + /// def = [] + /// ghi = [] + /// ``` + /// + #[clippy::version = "1.57.0"] + pub REDUNDANT_FEATURE_NAMES, + cargo, + "usage of a redundant feature name" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for wildcard dependencies in the `Cargo.toml`. + /// + /// ### Why is this bad? + /// [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html), + /// it is highly unlikely that you work with any possible version of your dependency, + /// and wildcard dependencies would cause unnecessary breakage in the ecosystem. + /// + /// ### Example + /// ```toml + /// [dependencies] + /// regex = "*" + /// ``` + /// Use instead: + /// ```toml + /// [dependencies] + /// # allow patch updates, but not minor or major version changes + /// some_crate_1 = "~1.2.3" + /// + /// # pin the version to a specific version + /// some_crate_2 = "=1.2.3" + /// ``` + #[clippy::version = "1.32.0"] + pub WILDCARD_DEPENDENCIES, + cargo, + "wildcard dependencies being used" } impl_lint_pass!(Cargo => [ CARGO_COMMON_METADATA, - REDUNDANT_FEATURE_NAMES, - NEGATIVE_FEATURE_NAMES, - MULTIPLE_CRATE_VERSIONS, - WILDCARD_DEPENDENCIES, LINT_GROUPS_PRIORITY, + MULTIPLE_CRATE_VERSIONS, + NEGATIVE_FEATURE_NAMES, + REDUNDANT_FEATURE_NAMES, + WILDCARD_DEPENDENCIES, ]); +pub struct Cargo { + allowed_duplicate_crates: FxHashSet, + ignore_publish: bool, +} + impl Cargo { pub fn new(conf: &'static Conf) -> Self { Self { diff --git a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs index 15ecba20a0bc..10c97fac992e 100644 --- a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs +++ b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -12,7 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, if let ty::RawPtr(ptrty, Mutability::Mut) = cast_to.kind() && let ty::RawPtr(_, Mutability::Not) = cx.typeck_results().node_type(cast_expr.hir_id).kind() && let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind - && method_name.ident.name == rustc_span::sym::as_ptr + && method_name.ident.name == sym::as_ptr && let Some(as_ptr_did) = cx .typeck_results() .type_dependent_def_id(cast_expr.peel_blocks().hir_id) diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index 3c9ebef73f0d..75761de4ae73 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -36,50 +36,231 @@ declare_clippy_lint! { /// ### What it does - /// Checks for casts from any numeric type to a float type where - /// the receiving type cannot store all values from the original type without - /// rounding errors. This possible rounding is to be expected, so this lint is - /// `Allow` by default. + /// Checks for the usage of `as *const _` or `as *mut _` conversion using inferred type. /// - /// Basically, this warns on casting any integer with 32 or more bits to `f32` - /// or any 64-bit integer to `f64`. - /// - /// ### Why is this bad? - /// It's not bad at all. But in some applications it can be - /// helpful to know where precision loss can take place. This lint can help find - /// those places in the code. + /// ### Why restrict this? + /// The conversion might include a dangerous cast that might go undetected due to the type being inferred. /// /// ### Example /// ```no_run - /// let x = u64::MAX; - /// x as f64; + /// fn as_usize(t: &T) -> usize { + /// // BUG: `t` is already a reference, so we will here + /// // return a dangling pointer to a temporary value instead + /// &t as *const _ as usize + /// } /// ``` - #[clippy::version = "pre 1.29.0"] - pub CAST_PRECISION_LOSS, - pedantic, - "casts that cause loss of precision, e.g., `x as f32` where `x: u64`" + /// Use instead: + /// ```no_run + /// fn as_usize(t: &T) -> usize { + /// t as *const T as usize + /// } + /// ``` + #[clippy::version = "1.85.0"] + pub AS_POINTER_UNDERSCORE, + restriction, + "detects `as *mut _` and `as *const _` conversion" } declare_clippy_lint! { /// ### What it does - /// Checks for casts from a signed to an unsigned numeric - /// type. In this case, negative values wrap around to large positive values, - /// which can be quite surprising in practice. However, since the cast works as - /// defined, this lint is `Allow` by default. + /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer. /// /// ### Why is this bad? - /// Possibly surprising results. You can activate this lint - /// as a one-time check to see where numeric wrapping can arise. + /// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior + /// mutability is used, making it unlikely that having it as a mutable pointer is correct. /// /// ### Example /// ```no_run - /// let y: i8 = -1; - /// y as u64; // will return 18446744073709551615 + /// let mut vec = Vec::::with_capacity(1); + /// let ptr = vec.as_ptr() as *mut u8; + /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR + /// ``` + /// Use instead: + /// ```no_run + /// let mut vec = Vec::::with_capacity(1); + /// let ptr = vec.as_mut_ptr(); + /// unsafe { ptr.write(4) }; + /// ``` + #[clippy::version = "1.66.0"] + pub AS_PTR_CAST_MUT, + nursery, + "casting the result of the `&self`-taking `as_ptr` to a mutable pointer" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for the usage of `as _` conversion using inferred type. + /// + /// ### Why restrict this? + /// The conversion might include lossy conversion or a dangerous cast that might go + /// undetected due to the type being inferred. + /// + /// The lint is allowed by default as using `_` is less wordy than always specifying the type. + /// + /// ### Example + /// ```no_run + /// fn foo(n: usize) {} + /// let n: u16 = 256; + /// foo(n as _); + /// ``` + /// Use instead: + /// ```no_run + /// fn foo(n: usize) {} + /// let n: u16 = 256; + /// foo(n as usize); + /// ``` + #[clippy::version = "1.63.0"] + pub AS_UNDERSCORE, + restriction, + "detects `as _` conversion" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for the usage of `&expr as *const T` or + /// `&mut expr as *mut T`, and suggest using `&raw const` or + /// `&raw mut` instead. + /// + /// ### Why is this bad? + /// This would improve readability and avoid creating a reference + /// that points to an uninitialized value or unaligned place. + /// Read the `&raw` explanation in the Reference for more information. + /// + /// ### Example + /// ```no_run + /// let val = 1; + /// let p = &val as *const i32; + /// + /// let mut val_mut = 1; + /// let p_mut = &mut val_mut as *mut i32; + /// ``` + /// Use instead: + /// ```no_run + /// let val = 1; + /// let p = &raw const val; + /// + /// let mut val_mut = 1; + /// let p_mut = &raw mut val_mut; + /// ``` + #[clippy::version = "1.60.0"] + pub BORROW_AS_PTR, + pedantic, + "borrowing just to cast to a raw pointer" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of the `abs()` method that cast the result to unsigned. + /// + /// ### Why is this bad? + /// The `unsigned_abs()` method avoids panic when called on the MIN value. + /// + /// ### Example + /// ```no_run + /// let x: i32 = -42; + /// let y: u32 = x.abs() as u32; + /// ``` + /// Use instead: + /// ```no_run + /// let x: i32 = -42; + /// let y: u32 = x.unsigned_abs(); + /// ``` + #[clippy::version = "1.62.0"] + pub CAST_ABS_TO_UNSIGNED, + suspicious, + "casting the result of `abs()` to an unsigned integer can panic" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for casts from an enum tuple constructor to an integer. + /// + /// ### Why is this bad? + /// The cast is easily confused with casting a c-like enum value to an integer. + /// + /// ### Example + /// ```no_run + /// enum E { X(i32) }; + /// let _ = E::X as usize; + /// ``` + #[clippy::version = "1.61.0"] + pub CAST_ENUM_CONSTRUCTOR, + suspicious, + "casts from an enum tuple constructor to an integer" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for casts from an enum type to an integral type that will definitely truncate the + /// value. + /// + /// ### Why is this bad? + /// The resulting integral value will not match the value of the variant it came from. + /// + /// ### Example + /// ```no_run + /// enum E { X = 256 }; + /// let _ = E::X as u8; + /// ``` + #[clippy::version = "1.61.0"] + pub CAST_ENUM_TRUNCATION, + suspicious, + "casts from an enum type to an integral type that will truncate the value" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for casts between numeric types that can be replaced by safe + /// conversion functions. + /// + /// ### Why is this bad? + /// Rust's `as` keyword will perform many kinds of conversions, including + /// silently lossy conversions. Conversion functions such as `i32::from` + /// will only perform lossless conversions. Using the conversion functions + /// prevents conversions from becoming silently lossy if the input types + /// ever change, and makes it clear for people reading the code that the + /// conversion is lossless. + /// + /// ### Example + /// ```no_run + /// fn as_u64(x: u8) -> u64 { + /// x as u64 + /// } + /// ``` + /// + /// Using `::from` would look like this: + /// + /// ```no_run + /// fn as_u64(x: u8) -> u64 { + /// u64::from(x) + /// } /// ``` #[clippy::version = "pre 1.29.0"] - pub CAST_SIGN_LOSS, + pub CAST_LOSSLESS, pedantic, - "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`" + "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for a known NaN float being cast to an integer + /// + /// ### Why is this bad? + /// NaNs are cast into zero, so one could simply use this and make the + /// code more readable. The lint could also hint at a programmer error. + /// + /// ### Example + /// ```rust,ignore + /// let _ = (0.0_f32 / 0.0) as u64; + /// ``` + /// Use instead: + /// ```rust,ignore + /// let _ = 0_u64; + /// ``` + #[clippy::version = "1.66.0"] + pub CAST_NAN_TO_INT, + suspicious, + "casting a known floating-point NaN into an integer" } declare_clippy_lint! { @@ -159,71 +340,28 @@ declare_clippy_lint! { /// ### What it does - /// Checks for casts between numeric types that can be replaced by safe - /// conversion functions. + /// Checks for casts from any numeric type to a float type where + /// the receiving type cannot store all values from the original type without + /// rounding errors. This possible rounding is to be expected, so this lint is + /// `Allow` by default. + /// + /// Basically, this warns on casting any integer with 32 or more bits to `f32` + /// or any 64-bit integer to `f64`. /// /// ### Why is this bad? - /// Rust's `as` keyword will perform many kinds of conversions, including - /// silently lossy conversions. Conversion functions such as `i32::from` - /// will only perform lossless conversions. Using the conversion functions - /// prevents conversions from becoming silently lossy if the input types - /// ever change, and makes it clear for people reading the code that the - /// conversion is lossless. + /// It's not bad at all. But in some applications it can be + /// helpful to know where precision loss can take place. This lint can help find + /// those places in the code. /// /// ### Example /// ```no_run - /// fn as_u64(x: u8) -> u64 { - /// x as u64 - /// } - /// ``` - /// - /// Using `::from` would look like this: - /// - /// ```no_run - /// fn as_u64(x: u8) -> u64 { - /// u64::from(x) - /// } + /// let x = u64::MAX; + /// x as f64; /// ``` #[clippy::version = "pre 1.29.0"] - pub CAST_LOSSLESS, + pub CAST_PRECISION_LOSS, pedantic, - "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for casts to the same type, casts of int literals to integer - /// types, casts of float literals to float types, and casts between raw - /// pointers that don't change type or constness. - /// - /// ### Why is this bad? - /// It's just unnecessary. - /// - /// ### Known problems - /// When the expression on the left is a function call, the lint considers - /// the return type to be a type alias if it's aliased through a `use` - /// statement (like `use std::io::Result as IoResult`). It will not lint - /// such cases. - /// - /// This check will only work on primitive types without any intermediate - /// references: raw pointers and trait objects may or may not work. - /// - /// ### Example - /// ```no_run - /// let _ = 2i32 as i32; - /// let _ = 0.5 as f32; - /// ``` - /// - /// Better: - /// - /// ```no_run - /// let _ = 2_i32; - /// let _ = 0.5_f32; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub UNNECESSARY_CAST, - complexity, - "cast to the same type, e.g., `x as i32` where `x: i32`" + "casts that cause loss of precision, e.g., `x as f32` where `x: u64`" } declare_clippy_lint! { @@ -254,6 +392,154 @@ "cast from a pointer to a more strictly aligned pointer" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts from a signed to an unsigned numeric + /// type. In this case, negative values wrap around to large positive values, + /// which can be quite surprising in practice. However, since the cast works as + /// defined, this lint is `Allow` by default. + /// + /// ### Why is this bad? + /// Possibly surprising results. You can activate this lint + /// as a one-time check to see where numeric wrapping can arise. + /// + /// ### Example + /// ```no_run + /// let y: i8 = -1; + /// y as u64; // will return 18446744073709551615 + /// ``` + #[clippy::version = "pre 1.29.0"] + pub CAST_SIGN_LOSS, + pedantic, + "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `as` casts between raw pointers to slices with differently sized elements. + /// + /// ### Why is this bad? + /// The produced raw pointer to a slice does not update its length metadata. The produced + /// pointer will point to a different number of bytes than the original pointer because the + /// length metadata of a raw slice pointer is in elements rather than bytes. + /// Producing a slice reference from the raw pointer will either create a slice with + /// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior. + /// + /// ### Example + /// // Missing data + /// ```no_run + /// let a = [1_i32, 2, 3, 4]; + /// let p = &a as *const [i32] as *const [u8]; + /// unsafe { + /// println!("{:?}", &*p); + /// } + /// ``` + /// // Undefined Behavior (note: also potential alignment issues) + /// ```no_run + /// let a = [1_u8, 2, 3, 4]; + /// let p = &a as *const [u8] as *const [u32]; + /// unsafe { + /// println!("{:?}", &*p); + /// } + /// ``` + /// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length + /// ```no_run + /// let a = [1_i32, 2, 3, 4]; + /// let old_ptr = &a as *const [i32]; + /// // The data pointer is cast to a pointer to the target `u8` not `[u8]` + /// // The length comes from the known length of 4 i32s times the 4 bytes per i32 + /// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16); + /// unsafe { + /// println!("{:?}", &*new_ptr); + /// } + /// ``` + #[clippy::version = "1.61.0"] + pub CAST_SLICE_DIFFERENT_SIZES, + correctness, + "casting using `as` between raw pointers to slices of types with different sizes" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for a raw slice being cast to a slice pointer + /// + /// ### Why is this bad? + /// This can result in multiple `&mut` references to the same location when only a pointer is + /// required. + /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require + /// the same [safety requirements] to be upheld. + /// + /// ### Example + /// ```rust,ignore + /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _; + /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _; + /// ``` + /// Use instead: + /// ```rust,ignore + /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len); + /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len); + /// ``` + /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety + #[clippy::version = "1.65.0"] + pub CAST_SLICE_FROM_RAW_PARTS, + suspicious, + "casting a slice created from a pointer and length to a slice pointer" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for expressions where a character literal is cast + /// to `u8` and suggests using a byte literal instead. + /// + /// ### Why is this bad? + /// In general, casting values to smaller types is + /// error-prone and should be avoided where possible. In the particular case of + /// converting a character literal to `u8`, it is easy to avoid by just using a + /// byte literal instead. As an added bonus, `b'a'` is also slightly shorter + /// than `'a' as u8`. + /// + /// ### Example + /// ```rust,ignore + /// 'x' as u8 + /// ``` + /// + /// A better version, using the byte literal: + /// + /// ```rust,ignore + /// b'x' + /// ``` + #[clippy::version = "pre 1.29.0"] + pub CHAR_LIT_AS_U8, + complexity, + "casting a character literal to `u8` truncates" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of a primitive method pointer like `max`/`min` to any integer type. + /// + /// ### Why restrict this? + /// Casting a function pointer to an integer can have surprising results and can occur + /// accidentally if parentheses are omitted from a function call. If you aren't doing anything + /// low-level with function pointers then you can opt out of casting functions to integers in + /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function + /// pointer casts in your code. + /// + /// ### Example + /// ```no_run + /// let _ = u16::max as usize; + /// ``` + /// + /// Use instead: + /// ```no_run + /// let _ = u16::MAX as usize; + /// ``` + #[clippy::version = "1.89.0"] + pub CONFUSING_METHOD_TO_NUMERIC_CAST, + suspicious, + "casting a primitive method pointer to any integer type" +} + declare_clippy_lint! { /// ### What it does /// Checks for casts of function pointers to something other than `usize`. @@ -284,39 +570,6 @@ "casting a function pointer to a numeric type other than `usize`" } -declare_clippy_lint! { - /// ### What it does - /// Checks for casts of a function pointer to a numeric type not wide enough to - /// store an address. - /// - /// ### Why is this bad? - /// Such a cast discards some bits of the function's address. If this is intended, it would be more - /// clearly expressed by casting to `usize` first, then casting the `usize` to the intended type (with - /// a comment) to perform the truncation. - /// - /// ### Example - /// ```no_run - /// fn fn1() -> i16 { - /// 1 - /// }; - /// let _ = fn1 as i32; - /// ``` - /// - /// Use instead: - /// ```no_run - /// // Cast to usize first, then comment with the reason for the truncation - /// fn fn1() -> i16 { - /// 1 - /// }; - /// let fn_ptr = fn1 as usize; - /// let fn_ptr_truncated = fn_ptr as i32; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION, - style, - "casting a function pointer to a numeric type not wide enough to store the address" -} - declare_clippy_lint! { /// ### What it does /// Checks for casts of a function pointer to any integer type. @@ -361,30 +614,87 @@ declare_clippy_lint! { /// ### What it does - /// Checks for expressions where a character literal is cast - /// to `u8` and suggests using a byte literal instead. + /// Checks for casts of a function pointer to a numeric type not wide enough to + /// store an address. /// /// ### Why is this bad? - /// In general, casting values to smaller types is - /// error-prone and should be avoided where possible. In the particular case of - /// converting a character literal to `u8`, it is easy to avoid by just using a - /// byte literal instead. As an added bonus, `b'a'` is also slightly shorter - /// than `'a' as u8`. + /// Such a cast discards some bits of the function's address. If this is intended, it would be more + /// clearly expressed by casting to `usize` first, then casting the `usize` to the intended type (with + /// a comment) to perform the truncation. /// /// ### Example - /// ```rust,ignore - /// 'x' as u8 + /// ```no_run + /// fn fn1() -> i16 { + /// 1 + /// }; + /// let _ = fn1 as i32; /// ``` /// - /// A better version, using the byte literal: - /// - /// ```rust,ignore - /// b'x' + /// Use instead: + /// ```no_run + /// // Cast to usize first, then comment with the reason for the truncation + /// fn fn1() -> i16 { + /// 1 + /// }; + /// let fn_ptr = fn1 as usize; + /// let fn_ptr_truncated = fn_ptr as i32; /// ``` #[clippy::version = "pre 1.29.0"] - pub CHAR_LIT_AS_U8, - complexity, - "casting a character literal to `u8` truncates" + pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + style, + "casting a function pointer to a numeric type not wide enough to store the address" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of small constant literals or `mem::align_of` results to raw pointers. + /// + /// ### Why is this bad? + /// This creates a dangling pointer and is better expressed as + /// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}. + /// + /// ### Example + /// ```no_run + /// let ptr = 4 as *const u32; + /// let aligned = std::mem::align_of::() as *const u32; + /// let mut_ptr: *mut i64 = 8 as *mut _; + /// ``` + /// Use instead: + /// ```no_run + /// let ptr = std::ptr::dangling::(); + /// let aligned = std::ptr::dangling::(); + /// let mut_ptr: *mut i64 = std::ptr::dangling_mut(); + /// ``` + #[clippy::version = "1.88.0"] + pub MANUAL_DANGLING_PTR, + style, + "casting small constant literals to pointers to create dangling pointers" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for bindings (constants, statics, or let bindings) that are defined + /// with one numeric type but are consistently cast to a different type in all usages. + /// + /// ### Why is this bad? + /// If a binding is always cast to a different type when used, it would be clearer + /// and more efficient to define it with the target type from the start. + /// + /// ### Example + /// ```no_run + /// const SIZE: u16 = 15; + /// let arr: [u8; SIZE as usize] = [0; SIZE as usize]; + /// ``` + /// + /// Use instead: + /// ```no_run + /// const SIZE: usize = 15; + /// let arr: [u8; SIZE] = [0; SIZE]; + /// ``` + #[clippy::version = "1.93.0"] + pub NEEDLESS_TYPE_CAST, + nursery, + "binding defined with one type but always cast to another" } declare_clippy_lint! { @@ -455,243 +765,62 @@ declare_clippy_lint! { /// ### What it does - /// Checks for casts from an enum type to an integral type that will definitely truncate the - /// value. + /// Checks for casts of references to pointer using `as` + /// and suggests `std::ptr::from_ref` and `std::ptr::from_mut` instead. /// /// ### Why is this bad? - /// The resulting integral value will not match the value of the variant it came from. + /// Using `as` casts may result in silently changing mutability or type. /// /// ### Example /// ```no_run - /// enum E { X = 256 }; - /// let _ = E::X as u8; - /// ``` - #[clippy::version = "1.61.0"] - pub CAST_ENUM_TRUNCATION, - suspicious, - "casts from an enum type to an integral type that will truncate the value" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `as` casts between raw pointers to slices with differently sized elements. - /// - /// ### Why is this bad? - /// The produced raw pointer to a slice does not update its length metadata. The produced - /// pointer will point to a different number of bytes than the original pointer because the - /// length metadata of a raw slice pointer is in elements rather than bytes. - /// Producing a slice reference from the raw pointer will either create a slice with - /// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior. - /// - /// ### Example - /// // Missing data - /// ```no_run - /// let a = [1_i32, 2, 3, 4]; - /// let p = &a as *const [i32] as *const [u8]; - /// unsafe { - /// println!("{:?}", &*p); - /// } - /// ``` - /// // Undefined Behavior (note: also potential alignment issues) - /// ```no_run - /// let a = [1_u8, 2, 3, 4]; - /// let p = &a as *const [u8] as *const [u32]; - /// unsafe { - /// println!("{:?}", &*p); - /// } - /// ``` - /// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length - /// ```no_run - /// let a = [1_i32, 2, 3, 4]; - /// let old_ptr = &a as *const [i32]; - /// // The data pointer is cast to a pointer to the target `u8` not `[u8]` - /// // The length comes from the known length of 4 i32s times the 4 bytes per i32 - /// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16); - /// unsafe { - /// println!("{:?}", &*new_ptr); - /// } - /// ``` - #[clippy::version = "1.61.0"] - pub CAST_SLICE_DIFFERENT_SIZES, - correctness, - "casting using `as` between raw pointers to slices of types with different sizes" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for casts from an enum tuple constructor to an integer. - /// - /// ### Why is this bad? - /// The cast is easily confused with casting a c-like enum value to an integer. - /// - /// ### Example - /// ```no_run - /// enum E { X(i32) }; - /// let _ = E::X as usize; - /// ``` - #[clippy::version = "1.61.0"] - pub CAST_ENUM_CONSTRUCTOR, - suspicious, - "casts from an enum tuple constructor to an integer" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of the `abs()` method that cast the result to unsigned. - /// - /// ### Why is this bad? - /// The `unsigned_abs()` method avoids panic when called on the MIN value. - /// - /// ### Example - /// ```no_run - /// let x: i32 = -42; - /// let y: u32 = x.abs() as u32; + /// let a_ref = &1; + /// let a_ptr = a_ref as *const _; /// ``` /// Use instead: /// ```no_run - /// let x: i32 = -42; - /// let y: u32 = x.unsigned_abs(); + /// let a_ref = &1; + /// let a_ptr = std::ptr::from_ref(a_ref); /// ``` - #[clippy::version = "1.62.0"] - pub CAST_ABS_TO_UNSIGNED, - suspicious, - "casting the result of `abs()` to an unsigned integer can panic" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for the usage of `as _` conversion using inferred type. - /// - /// ### Why restrict this? - /// The conversion might include lossy conversion or a dangerous cast that might go - /// undetected due to the type being inferred. - /// - /// The lint is allowed by default as using `_` is less wordy than always specifying the type. - /// - /// ### Example - /// ```no_run - /// fn foo(n: usize) {} - /// let n: u16 = 256; - /// foo(n as _); - /// ``` - /// Use instead: - /// ```no_run - /// fn foo(n: usize) {} - /// let n: u16 = 256; - /// foo(n as usize); - /// ``` - #[clippy::version = "1.63.0"] - pub AS_UNDERSCORE, - restriction, - "detects `as _` conversion" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for the usage of `&expr as *const T` or - /// `&mut expr as *mut T`, and suggest using `&raw const` or - /// `&raw mut` instead. - /// - /// ### Why is this bad? - /// This would improve readability and avoid creating a reference - /// that points to an uninitialized value or unaligned place. - /// Read the `&raw` explanation in the Reference for more information. - /// - /// ### Example - /// ```no_run - /// let val = 1; - /// let p = &val as *const i32; - /// - /// let mut val_mut = 1; - /// let p_mut = &mut val_mut as *mut i32; - /// ``` - /// Use instead: - /// ```no_run - /// let val = 1; - /// let p = &raw const val; - /// - /// let mut val_mut = 1; - /// let p_mut = &raw mut val_mut; - /// ``` - #[clippy::version = "1.60.0"] - pub BORROW_AS_PTR, + #[clippy::version = "1.78.0"] + pub REF_AS_PTR, pedantic, - "borrowing just to cast to a raw pointer" + "using `as` to cast a reference to pointer" } declare_clippy_lint! { /// ### What it does - /// Checks for a raw slice being cast to a slice pointer + /// Checks for casts to the same type, casts of int literals to integer + /// types, casts of float literals to float types, and casts between raw + /// pointers that don't change type or constness. /// /// ### Why is this bad? - /// This can result in multiple `&mut` references to the same location when only a pointer is - /// required. - /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require - /// the same [safety requirements] to be upheld. + /// It's just unnecessary. /// - /// ### Example - /// ```rust,ignore - /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _; - /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _; - /// ``` - /// Use instead: - /// ```rust,ignore - /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len); - /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len); - /// ``` - /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety - #[clippy::version = "1.65.0"] - pub CAST_SLICE_FROM_RAW_PARTS, - suspicious, - "casting a slice created from a pointer and length to a slice pointer" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer. + /// ### Known problems + /// When the expression on the left is a function call, the lint considers + /// the return type to be a type alias if it's aliased through a `use` + /// statement (like `use std::io::Result as IoResult`). It will not lint + /// such cases. /// - /// ### Why is this bad? - /// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior - /// mutability is used, making it unlikely that having it as a mutable pointer is correct. + /// This check will only work on primitive types without any intermediate + /// references: raw pointers and trait objects may or may not work. /// /// ### Example /// ```no_run - /// let mut vec = Vec::::with_capacity(1); - /// let ptr = vec.as_ptr() as *mut u8; - /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR + /// let _ = 2i32 as i32; + /// let _ = 0.5 as f32; /// ``` - /// Use instead: + /// + /// Better: + /// /// ```no_run - /// let mut vec = Vec::::with_capacity(1); - /// let ptr = vec.as_mut_ptr(); - /// unsafe { ptr.write(4) }; + /// let _ = 2_i32; + /// let _ = 0.5_f32; /// ``` - #[clippy::version = "1.66.0"] - pub AS_PTR_CAST_MUT, - nursery, - "casting the result of the `&self`-taking `as_ptr` to a mutable pointer" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for a known NaN float being cast to an integer - /// - /// ### Why is this bad? - /// NaNs are cast into zero, so one could simply use this and make the - /// code more readable. The lint could also hint at a programmer error. - /// - /// ### Example - /// ```rust,ignore - /// let _ = (0.0_f32 / 0.0) as u64; - /// ``` - /// Use instead: - /// ```rust,ignore - /// let _ = 0_u64; - /// ``` - #[clippy::version = "1.66.0"] - pub CAST_NAN_TO_INT, - suspicious, - "casting a known floating-point NaN into an integer" + #[clippy::version = "pre 1.29.0"] + pub UNNECESSARY_CAST, + complexity, + "cast to the same type, e.g., `x as i32` where `x: i32`" } declare_clippy_lint! { @@ -717,134 +846,36 @@ "using `0 as *{const, mut} T`" } -declare_clippy_lint! { - /// ### What it does - /// Checks for casts of references to pointer using `as` - /// and suggests `std::ptr::from_ref` and `std::ptr::from_mut` instead. - /// - /// ### Why is this bad? - /// Using `as` casts may result in silently changing mutability or type. - /// - /// ### Example - /// ```no_run - /// let a_ref = &1; - /// let a_ptr = a_ref as *const _; - /// ``` - /// Use instead: - /// ```no_run - /// let a_ref = &1; - /// let a_ptr = std::ptr::from_ref(a_ref); - /// ``` - #[clippy::version = "1.78.0"] - pub REF_AS_PTR, - pedantic, - "using `as` to cast a reference to pointer" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for the usage of `as *const _` or `as *mut _` conversion using inferred type. - /// - /// ### Why restrict this? - /// The conversion might include a dangerous cast that might go undetected due to the type being inferred. - /// - /// ### Example - /// ```no_run - /// fn as_usize(t: &T) -> usize { - /// // BUG: `t` is already a reference, so we will here - /// // return a dangling pointer to a temporary value instead - /// &t as *const _ as usize - /// } - /// ``` - /// Use instead: - /// ```no_run - /// fn as_usize(t: &T) -> usize { - /// t as *const T as usize - /// } - /// ``` - #[clippy::version = "1.85.0"] - pub AS_POINTER_UNDERSCORE, - restriction, - "detects `as *mut _` and `as *const _` conversion" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for casts of small constant literals or `mem::align_of` results to raw pointers. - /// - /// ### Why is this bad? - /// This creates a dangling pointer and is better expressed as - /// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}. - /// - /// ### Example - /// ```no_run - /// let ptr = 4 as *const u32; - /// let aligned = std::mem::align_of::() as *const u32; - /// let mut_ptr: *mut i64 = 8 as *mut _; - /// ``` - /// Use instead: - /// ```no_run - /// let ptr = std::ptr::dangling::(); - /// let aligned = std::ptr::dangling::(); - /// let mut_ptr: *mut i64 = std::ptr::dangling_mut(); - /// ``` - #[clippy::version = "1.88.0"] - pub MANUAL_DANGLING_PTR, - style, - "casting small constant literals to pointers to create dangling pointers" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for casts of a primitive method pointer like `max`/`min` to any integer type. - /// - /// ### Why restrict this? - /// Casting a function pointer to an integer can have surprising results and can occur - /// accidentally if parentheses are omitted from a function call. If you aren't doing anything - /// low-level with function pointers then you can opt out of casting functions to integers in - /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function - /// pointer casts in your code. - /// - /// ### Example - /// ```no_run - /// let _ = u16::max as usize; - /// ``` - /// - /// Use instead: - /// ```no_run - /// let _ = u16::MAX as usize; - /// ``` - #[clippy::version = "1.89.0"] - pub CONFUSING_METHOD_TO_NUMERIC_CAST, - suspicious, - "casting a primitive method pointer to any integer type" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for bindings (constants, statics, or let bindings) that are defined - /// with one numeric type but are consistently cast to a different type in all usages. - /// - /// ### Why is this bad? - /// If a binding is always cast to a different type when used, it would be clearer - /// and more efficient to define it with the target type from the start. - /// - /// ### Example - /// ```no_run - /// const SIZE: u16 = 15; - /// let arr: [u8; SIZE as usize] = [0; SIZE as usize]; - /// ``` - /// - /// Use instead: - /// ```no_run - /// const SIZE: usize = 15; - /// let arr: [u8; SIZE] = [0; SIZE]; - /// ``` - #[clippy::version = "1.93.0"] - pub NEEDLESS_TYPE_CAST, - nursery, - "binding defined with one type but always cast to another" -} +impl_lint_pass!(Casts => [ + AS_POINTER_UNDERSCORE, + AS_PTR_CAST_MUT, + AS_UNDERSCORE, + BORROW_AS_PTR, + CAST_ABS_TO_UNSIGNED, + CAST_ENUM_CONSTRUCTOR, + CAST_ENUM_TRUNCATION, + CAST_LOSSLESS, + CAST_NAN_TO_INT, + CAST_POSSIBLE_TRUNCATION, + CAST_POSSIBLE_WRAP, + CAST_PRECISION_LOSS, + CAST_PTR_ALIGNMENT, + CAST_SIGN_LOSS, + CAST_SLICE_DIFFERENT_SIZES, + CAST_SLICE_FROM_RAW_PARTS, + CHAR_LIT_AS_U8, + CONFUSING_METHOD_TO_NUMERIC_CAST, + FN_TO_NUMERIC_CAST, + FN_TO_NUMERIC_CAST_ANY, + FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + MANUAL_DANGLING_PTR, + NEEDLESS_TYPE_CAST, + PTR_AS_PTR, + PTR_CAST_CONSTNESS, + REF_AS_PTR, + UNNECESSARY_CAST, + ZERO_PTR, +]); pub struct Casts { msrv: Msrv, @@ -856,37 +887,6 @@ pub fn new(conf: &'static Conf) -> Self { } } -impl_lint_pass!(Casts => [ - CAST_PRECISION_LOSS, - CAST_SIGN_LOSS, - CAST_POSSIBLE_TRUNCATION, - CAST_POSSIBLE_WRAP, - CAST_LOSSLESS, - CAST_PTR_ALIGNMENT, - CAST_SLICE_DIFFERENT_SIZES, - UNNECESSARY_CAST, - FN_TO_NUMERIC_CAST_ANY, - FN_TO_NUMERIC_CAST, - FN_TO_NUMERIC_CAST_WITH_TRUNCATION, - CHAR_LIT_AS_U8, - PTR_AS_PTR, - PTR_CAST_CONSTNESS, - CAST_ENUM_TRUNCATION, - CAST_ENUM_CONSTRUCTOR, - CAST_ABS_TO_UNSIGNED, - AS_UNDERSCORE, - BORROW_AS_PTR, - CAST_SLICE_FROM_RAW_PARTS, - AS_PTR_CAST_MUT, - CAST_NAN_TO_INT, - ZERO_PTR, - REF_AS_PTR, - AS_POINTER_UNDERSCORE, - MANUAL_DANGLING_PTR, - CONFUSING_METHOD_TO_NUMERIC_CAST, - NEEDLESS_TYPE_CAST, -]); - impl<'tcx> LateLintPass<'tcx> for Casts { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.in_external_macro(cx.sess().source_map()) { 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 7bfe9201d812..5cc41c121965 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,17 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::MaybeResPath as _; use clippy_utils::source::{SpanRangeExt, snippet_opt}; use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; -use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias}; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, sym}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp}; +use rustc_hir::{Expr, ExprKind, FnRetTy, Lit, Node, Path, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use std::ops::ControlFlow; use super::UNNECESSARY_CAST; @@ -39,10 +39,8 @@ pub(super) fn check<'tcx>( // 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) { - return false; - } + TyKind::Path(qpath) if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) => { + return false; }, // Ignore `p as *const _` TyKind::Infer(()) => return false, @@ -97,7 +95,7 @@ pub(super) fn check<'tcx>( // skip cast of fn call that returns type alias if let ExprKind::Cast(inner, ..) = expr.kind - && is_cast_from_ty_alias(cx, inner, cast_from) + && is_cast_from_ty_alias(cx, inner) { return false; } @@ -270,34 +268,25 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 { /// Finds whether an `Expr` returns a type alias. /// -/// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark, -/// dark path reimplementing this (or something similar). -fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>, cast_from: Ty<'tcx>) -> bool { +/// When in doubt, for example because it calls a non-local function that we don't have the +/// declaration for, assume if might be a type alias. +fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>) -> bool { for_each_expr_without_closures(expr, |expr| { // Calls are a `Path`, and usage of locals are a `Path`. So, this checks // - call() as i32 // - local as i32 if let ExprKind::Path(qpath) = expr.kind { let res = cx.qpath_res(&qpath, expr.hir_id); - // Function call if let Res::Def(DefKind::Fn, def_id) = res { - let Some(snippet) = cx.tcx.def_span(def_id).get_source_text(cx) else { - return ControlFlow::Continue(()); + let Some(def_id) = def_id.as_local() else { + // External function, we can't know, better be safe + return ControlFlow::Break(()); }; - // This is the worst part of this entire function. This is the only way I know of to - // check whether a function returns a type alias. Sure, you can get the return type - // from a function in the current crate as an hir ty, but how do you get it for - // external functions?? Simple: It's impossible. So, we check whether a part of the - // function's declaration snippet is exactly equal to the `Ty`. That way, we can - // see whether it's a type alias. - // - // FIXME: This won't work if the type is given an alias through `use`, should we - // consider this a type alias as well? - if !snippet - .split("->") - .skip(1) - .any(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from))) + if let Some(FnRetTy::Return(ty)) = cx.tcx.hir_get_fn_output(def_id) + && let TyKind::Path(qpath) = ty.kind + && is_ty_alias(&qpath) { + // Function call to a local function returning a type alias return ControlFlow::Break(()); } // Local usage @@ -305,7 +294,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx && let Node::LetStmt(l) = cx.tcx.parent_hir_node(hir_id) { if let Some(e) = l.init - && is_cast_from_ty_alias(cx, e, cast_from) + && is_cast_from_ty_alias(cx, e) { return ControlFlow::Break::<()>(()); } @@ -323,7 +312,3 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx }) .is_some() } - -fn snippet_eq_ty(snippet: &str, ty: Ty<'_>) -> bool { - snippet.trim() == ty.to_string() || snippet.trim().contains(&format!("::{ty}")) -} diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index 8303897d1294..5e9009c67197 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -34,6 +34,8 @@ "`try_from` could replace manual bounds checking when casting" } +impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); + pub struct CheckedConversions { msrv: Msrv, } @@ -44,8 +46,6 @@ pub fn new(conf: &'static Conf) -> Self { } } -impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); - impl LateLintPass<'_> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { if let ExprKind::Binary(op, lhs, rhs) = item.kind diff --git a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs index 35b799aefb04..c5eabe4c2b88 100644 --- a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -44,6 +44,8 @@ "cloning a reference for slice references" } +impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]); + pub struct ClonedRefToSliceRefs<'a> { msrv: &'a Msrv, } @@ -53,8 +55,6 @@ pub fn new(conf: &'a Conf) -> Self { } } -impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]); - impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if self.msrv.meets(cx, { diff --git a/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs b/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs index 2e3acb7748e2..1a7e20b98271 100644 --- a/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs +++ b/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs @@ -46,6 +46,7 @@ nursery, "coercing to `&dyn Any` when dereferencing could produce a `dyn Any` without coercion is usually not intended" } + declare_lint_pass!(CoerceContainerToAny => [COERCE_CONTAINER_TO_ANY]); impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 595625c08bef..911ca306aca4 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -40,6 +40,8 @@ @eval_always = true } +impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]); + pub struct CognitiveComplexity { limit: LimitStack, } @@ -52,8 +54,6 @@ pub fn new(conf: &'static Conf) -> Self { } } -impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]); - impl CognitiveComplexity { fn check<'tcx>( &self, @@ -81,10 +81,8 @@ fn check<'tcx>( } cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64; }, - ExprKind::Ret(_) => { - if !matches!(prev_expr, Some(ExprKind::Ret(_))) { - returns += 1; - } + ExprKind::Ret(_) if !matches!(prev_expr, Some(ExprKind::Ret(_))) => { + returns += 1; }, _ => {}, } @@ -146,6 +144,7 @@ fn check_fn( span: Span, def_id: LocalDefId, ) { + #[allow(deprecated)] if !cx.tcx.has_attr(def_id, sym::test) { let expr = if kind.asyncness().is_async() { match get_async_fn_body(cx.tcx, body) { diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index be07ce1272bd..a76027caebc8 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -12,38 +12,6 @@ use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Span, Symbol}; -declare_clippy_lint! { - /// ### What it does - /// Checks for nested `if` statements which can be collapsed - /// by `&&`-combining their conditions. - /// - /// ### Why is this bad? - /// Each `if`-statement adds one level of nesting, which - /// makes code look more complex than it really is. - /// - /// ### Example - /// ```no_run - /// # let (x, y) = (true, true); - /// if x { - /// if y { - /// // … - /// } - /// } - /// ``` - /// - /// Use instead: - /// ```no_run - /// # let (x, y) = (true, true); - /// if x && y { - /// // … - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub COLLAPSIBLE_IF, - style, - "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`" -} - declare_clippy_lint! { /// ### What it does /// Checks for collapsible `else { if ... }` expressions @@ -80,6 +48,40 @@ "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)" } +declare_clippy_lint! { + /// ### What it does + /// Checks for nested `if` statements which can be collapsed + /// by `&&`-combining their conditions. + /// + /// ### Why is this bad? + /// Each `if`-statement adds one level of nesting, which + /// makes code look more complex than it really is. + /// + /// ### Example + /// ```no_run + /// # let (x, y) = (true, true); + /// if x { + /// if y { + /// // … + /// } + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// # let (x, y) = (true, true); + /// if x && y { + /// // … + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub COLLAPSIBLE_IF, + style, + "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`" +} + +impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_ELSE_IF, COLLAPSIBLE_IF]); + pub struct CollapsibleIf { msrv: Msrv, lint_commented_code: bool, @@ -259,8 +261,6 @@ fn check_significant_tokens_and_expect_attrs( } } -impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); - impl LateLintPass<'_> for CollapsibleIf { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::If(cond, then, else_) = &expr.kind @@ -307,7 +307,7 @@ fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { } /// If the expression is a `||`, suggest parentheses around it. -fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { +pub(super) fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { if let ExprKind::Binary(op, _, _) = expr.peel_drop_temps().kind && op.node == BinOpKind::Or { @@ -334,7 +334,7 @@ fn span_extract_keyword(sm: &SourceMap, span: Span, keyword: &str) -> Option (Span, Span, Span) { +pub(super) fn peel_parens(sm: &SourceMap, mut span: Span) -> (Span, Span, Span) { use crate::rustc_span::Pos; let start = span.shrink_to_lo(); diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index fd84ce70bd71..ddd3e8b805d1 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::get_enclosing_block; use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::visitors::{Visitable, for_each_expr}; +use clippy_utils::{get_enclosing_block, sym}; use core::ops::ControlFlow; use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -41,6 +40,7 @@ nursery, "a collection is never queried" } + declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]); impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { 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 19f62e8bf79c..509b345048c1 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 @@ -49,6 +49,7 @@ suspicious, "using `crate` in a macro definition" } + declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); impl EarlyLintPass for CrateInMacroDef { diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs index 695b25aeb0b7..b3be4e5a3f8e 100644 --- a/src/tools/clippy/clippy_lints/src/create_dir.rs +++ b/src/tools/clippy/clippy_lints/src/create_dir.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -51,7 +51,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { suggestions.push((path.span.shrink_to_lo(), "std::fs::".to_owned())); } - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( "consider calling `std::fs::create_dir_all` instead", suggestions, Applicability::MaybeIncorrect, diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index 45de4035d992..eb14ef18c03d 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -1,14 +1,14 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_in_test; use clippy_utils::macros::{MacroCall, macro_backtrace}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{is_in_test, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::{Arm, Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, SyntaxContext, sym}; +use rustc_span::{Span, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -33,6 +33,8 @@ "`dbg!` macro is intended as a debugging tool" } +impl_lint_pass!(DbgMacro => [DBG_MACRO]); + pub struct DbgMacro { allow_dbg_in_tests: bool, /// Tracks the `dbg!` macro callsites that are already checked. @@ -41,8 +43,6 @@ pub struct DbgMacro { prev_ctxt: SyntaxContext, } -impl_lint_pass!(DbgMacro => [DBG_MACRO]); - impl DbgMacro { pub fn new(conf: &'static Conf) -> Self { DbgMacro { diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index a04d133b0d72..e16b194c0cad 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -100,11 +100,12 @@ crate::dereference::NEEDLESS_BORROW_INFO, crate::dereference::REF_BINDING_TO_REFERENCE_INFO, crate::derivable_impls::DERIVABLE_IMPLS_INFO, - crate::derive::DERIVED_HASH_WITH_MANUAL_EQ_INFO, crate::derive::DERIVE_ORD_XOR_PARTIAL_ORD_INFO, crate::derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ_INFO, + crate::derive::DERIVED_HASH_WITH_MANUAL_EQ_INFO, crate::derive::EXPL_IMPL_CLONE_ON_COPY_INFO, crate::derive::UNSAFE_DERIVE_DESERIALIZE_INFO, + crate::disallowed_fields::DISALLOWED_FIELDS_INFO, crate::disallowed_macros::DISALLOWED_MACROS_INFO, crate::disallowed_methods::DISALLOWED_METHODS_INFO, crate::disallowed_names::DISALLOWED_NAMES_INFO, @@ -173,6 +174,7 @@ crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO, crate::format_args::UNINLINED_FORMAT_ARGS_INFO, crate::format_args::UNNECESSARY_DEBUG_FORMATTING_INFO, + crate::format_args::UNNECESSARY_TRAILING_COMMA_INFO, crate::format_args::UNUSED_FORMAT_SPECS_INFO, crate::format_impl::PRINT_IN_FORMAT_IMPL_INFO, crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO, @@ -204,8 +206,8 @@ crate::if_not_else::IF_NOT_ELSE_INFO, crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO, crate::ifs::BRANCHES_SHARING_CODE_INFO, - crate::ifs::IFS_SAME_COND_INFO, crate::ifs::IF_SAME_THEN_ELSE_INFO, + crate::ifs::IFS_SAME_COND_INFO, crate::ifs::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, crate::ignored_unit_patterns::IGNORED_UNIT_PATTERNS_INFO, crate::impl_hash_with_borrow_str_and_bytes::IMPL_HASH_BORROW_WITH_STR_AND_BYTES_INFO, @@ -336,8 +338,8 @@ crate::matches::MATCH_SAME_ARMS_INFO, crate::matches::MATCH_SINGLE_BINDING_INFO, crate::matches::MATCH_STR_CASE_MISMATCH_INFO, - crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO, crate::matches::MATCH_WILD_ERR_ARM_INFO, + crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO, crate::matches::NEEDLESS_MATCH_INFO, crate::matches::REDUNDANT_GUARDS_INFO, crate::matches::REDUNDANT_PATTERN_MATCHING_INFO, @@ -359,9 +361,9 @@ crate::methods::CHARS_LAST_CMP_INFO, crate::methods::CHARS_NEXT_CMP_INFO, crate::methods::CLEAR_WITH_DRAIN_INFO, - crate::methods::CLONED_INSTEAD_OF_COPIED_INFO, crate::methods::CLONE_ON_COPY_INFO, crate::methods::CLONE_ON_REF_PTR_INFO, + crate::methods::CLONED_INSTEAD_OF_COPIED_INFO, crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, crate::methods::CONST_IS_EMPTY_INFO, crate::methods::DOUBLE_ENDED_ITERATOR_LAST_INFO, @@ -389,7 +391,6 @@ crate::methods::IO_OTHER_ERROR_INFO, crate::methods::IP_CONSTANT_INFO, crate::methods::IS_DIGIT_ASCII_RADIX_INFO, - crate::methods::ITERATOR_STEP_BY_ZERO_INFO, crate::methods::ITER_CLONED_COLLECT_INFO, crate::methods::ITER_COUNT_INFO, crate::methods::ITER_FILTER_IS_OK_INFO, @@ -405,10 +406,11 @@ crate::methods::ITER_SKIP_NEXT_INFO, crate::methods::ITER_SKIP_ZERO_INFO, crate::methods::ITER_WITH_DRAIN_INFO, + crate::methods::ITERATOR_STEP_BY_ZERO_INFO, crate::methods::JOIN_ABSOLUTE_PATHS_INFO, crate::methods::LINES_FILTER_MAP_OK_INFO, - crate::methods::MANUAL_CONTAINS_INFO, crate::methods::MANUAL_C_STR_LITERALS_INFO, + crate::methods::MANUAL_CONTAINS_INFO, crate::methods::MANUAL_FILTER_MAP_INFO, crate::methods::MANUAL_FIND_MAP_INFO, crate::methods::MANUAL_INSPECT_INFO, @@ -437,8 +439,8 @@ crate::methods::NEEDLESS_OPTION_TAKE_INFO, crate::methods::NEEDLESS_SPLITN_INFO, crate::methods::NEW_RET_NO_SELF_INFO, - crate::methods::NONSENSICAL_OPEN_OPTIONS_INFO, crate::methods::NO_EFFECT_REPLACE_INFO, + crate::methods::NONSENSICAL_OPEN_OPTIONS_INFO, crate::methods::OBFUSCATED_IF_ELSE_INFO, crate::methods::OK_EXPECT_INFO, crate::methods::OPTION_AS_REF_CLONED_INFO, @@ -452,8 +454,8 @@ crate::methods::PTR_OFFSET_BY_LITERAL_INFO, crate::methods::PTR_OFFSET_WITH_CAST_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, - crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, + crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::REDUNDANT_AS_STR_INFO, crate::methods::REDUNDANT_ITER_CLONED_INFO, crate::methods::REPEAT_ONCE_INFO, @@ -468,9 +470,9 @@ crate::methods::SKIP_WHILE_NEXT_INFO, crate::methods::SLICED_STRING_AS_BYTES_INFO, crate::methods::STABLE_SORT_PRIMITIVE_INFO, + crate::methods::STR_SPLIT_AT_NEWLINE_INFO, crate::methods::STRING_EXTEND_CHARS_INFO, crate::methods::STRING_LIT_CHARS_ANY_INFO, - crate::methods::STR_SPLIT_AT_NEWLINE_INFO, crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO, crate::methods::SUSPICIOUS_MAP_INFO, crate::methods::SUSPICIOUS_OPEN_OPTIONS_INFO, @@ -641,8 +643,8 @@ crate::ranges::RANGE_MINUS_ONE_INFO, crate::ranges::RANGE_PLUS_ONE_INFO, crate::ranges::REVERSED_EMPTY_RANGES_INFO, - crate::raw_strings::NEEDLESS_RAW_STRINGS_INFO, crate::raw_strings::NEEDLESS_RAW_STRING_HASHES_INFO, + crate::raw_strings::NEEDLESS_RAW_STRINGS_INFO, crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO, crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO, crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO, @@ -695,12 +697,12 @@ crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO, crate::string_patterns::MANUAL_PATTERN_CHAR_COMPARISON_INFO, crate::string_patterns::SINGLE_CHAR_PATTERN_INFO, + crate::strings::STR_TO_STRING_INFO, crate::strings::STRING_ADD_INFO, crate::strings::STRING_ADD_ASSIGN_INFO, crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO, crate::strings::STRING_LIT_AS_BYTES_INFO, crate::strings::STRING_SLICE_INFO, - crate::strings::STR_TO_STRING_INFO, crate::strings::TRIM_SPLIT_WHITESPACE_INFO, crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO, crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO, @@ -724,7 +726,6 @@ crate::transmute::CROSSPOINTER_TRANSMUTE_INFO, crate::transmute::EAGER_TRANSMUTE_INFO, crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS_INFO, - crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO, crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, crate::transmute::TRANSMUTE_INT_TO_NON_ZERO_INFO, @@ -732,6 +733,7 @@ crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO, + crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, crate::transmute::TRANSMUTING_NULL_INFO, crate::transmute::UNSOUND_COLLECTION_TRANSMUTE_INFO, crate::transmute::USELESS_TRANSMUTE_INFO, @@ -789,20 +791,20 @@ crate::useless_vec::USELESS_VEC_INFO, crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO, crate::visibility::NEEDLESS_PUB_SELF_INFO, - crate::visibility::PUB_WITHOUT_SHORTHAND_INFO, crate::visibility::PUB_WITH_SHORTHAND_INFO, + crate::visibility::PUB_WITHOUT_SHORTHAND_INFO, crate::volatile_composites::VOLATILE_COMPOSITES_INFO, crate::wildcard_imports::ENUM_GLOB_USE_INFO, crate::wildcard_imports::WILDCARD_IMPORTS_INFO, - crate::write::PRINTLN_EMPTY_STRING_INFO, crate::write::PRINT_LITERAL_INFO, crate::write::PRINT_STDERR_INFO, crate::write::PRINT_STDOUT_INFO, crate::write::PRINT_WITH_NEWLINE_INFO, + crate::write::PRINTLN_EMPTY_STRING_INFO, crate::write::USE_DEBUG_INFO, - crate::write::WRITELN_EMPTY_STRING_INFO, crate::write::WRITE_LITERAL_INFO, crate::write::WRITE_WITH_NEWLINE_INFO, + crate::write::WRITELN_EMPTY_STRING_INFO, crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO, crate::zero_repeat_side_effects::ZERO_REPEAT_SIDE_EFFECTS_INFO, crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index a48e4d2fbd57..2064d896861b 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -69,14 +69,14 @@ "binding initialized with Default should have its fields set in the initializer" } +impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); + #[derive(Default)] pub struct Default { // Spans linted by `field_reassign_with_default`. reassigned_linted: FxHashSet, } -impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); - impl<'tcx> LateLintPass<'tcx> for Default { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs index 641f8ae03b72..c831f96443c6 100644 --- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs +++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs @@ -45,7 +45,10 @@ complexity, "unit structs can be constructed without calling `default`" } -declare_lint_pass!(DefaultConstructedUnitStructs => [DEFAULT_CONSTRUCTED_UNIT_STRUCTS]); + +declare_lint_pass!(DefaultConstructedUnitStructs => [ + DEFAULT_CONSTRUCTED_UNIT_STRUCTS, +]); fn is_alias(ty: hir::Ty<'_>) -> bool { if let hir::TyKind::Path(ref qpath) = ty.kind { diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs index 056e39c02af9..fffdea863f51 100644 --- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::{last_path_segment, std_or_core}; +use clippy_utils::{last_path_segment, std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, GenericArg, QPath, TyKind, def}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{SyntaxContext, sym}; +use rustc_span::SyntaxContext; declare_clippy_lint! { /// ### What it does @@ -28,6 +28,7 @@ style, "check `std::iter::Empty::default()` and replace with `std::iter::empty()`" } + declare_lint_pass!(DefaultIterEmpty => [DEFAULT_INSTEAD_OF_ITER_EMPTY]); impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty { diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs index df6525ce040e..61656d6cede5 100644 --- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs +++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_hir::attrs::{AttributeKind, ReprAttr}; +use rustc_hir::attrs::ReprAttr; use rustc_hir::{HirId, Item, ItemKind, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; @@ -48,6 +48,7 @@ restriction, "unions without a `#[repr(C)]` attribute" } + declare_lint_pass!(DefaultUnionRepresentation => [DEFAULT_UNION_REPRESENTATION]); impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { @@ -99,5 +100,5 @@ fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: ty::GenericArgsR fn has_c_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { let attrs = cx.tcx.hir_attrs(hir_id); - find_attr!(attrs, AttributeKind::Repr { reprs, .. } if reprs.iter().any(|(x, _)| *x == ReprAttr::ReprC)) + find_attr!(attrs, Repr { reprs, .. } if reprs.iter().any(|(x, _)| *x == ReprAttr::ReprC)) } diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 32fd4afb122e..26dfa7593f22 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -4,7 +4,7 @@ use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ - DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, + DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -19,10 +19,32 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; use std::borrow::Cow; +declare_clippy_lint! { + /// ### What it does + /// Checks for dereferencing expressions which would be covered by auto-deref. + /// + /// ### Why is this bad? + /// This unnecessarily complicates the code. + /// + /// ### Example + /// ```no_run + /// let x = String::new(); + /// let y: &str = &*x; + /// ``` + /// Use instead: + /// ```no_run + /// let x = String::new(); + /// let y: &str = &x; + /// ``` + #[clippy::version = "1.64.0"] + pub EXPLICIT_AUTO_DEREF, + complexity, + "dereferencing when the compiler would automatically dereference" +} + declare_clippy_lint! { /// ### What it does /// Checks for explicit `deref()` or `deref_mut()` method calls. @@ -120,34 +142,11 @@ "`ref` binding to a reference" } -declare_clippy_lint! { - /// ### What it does - /// Checks for dereferencing expressions which would be covered by auto-deref. - /// - /// ### Why is this bad? - /// This unnecessarily complicates the code. - /// - /// ### Example - /// ```no_run - /// let x = String::new(); - /// let y: &str = &*x; - /// ``` - /// Use instead: - /// ```no_run - /// let x = String::new(); - /// let y: &str = &x; - /// ``` - #[clippy::version = "1.64.0"] - pub EXPLICIT_AUTO_DEREF, - complexity, - "dereferencing when the compiler would automatically dereference" -} - impl_lint_pass!(Dereferencing<'_> => [ + EXPLICIT_AUTO_DEREF, EXPLICIT_DEREF_METHODS, NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE, - EXPLICIT_AUTO_DEREF, ]); #[derive(Default)] @@ -855,6 +854,7 @@ fn for_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Self { | TyKind::Ptr(_) | TyKind::FnPtr(_) | TyKind::Pat(..) + | TyKind::FieldOf(..) | TyKind::Never | TyKind::Tup(_) | TyKind::Path(_) => Self::Deref, diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index 992ed320ce68..c04163b1c7a0 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -56,6 +56,8 @@ "manual implementation of the `Default` trait which is equal to a derive" } +impl_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]); + pub struct DerivableImpls { msrv: Msrv, } @@ -66,8 +68,6 @@ pub fn new(conf: &'static Conf) -> Self { } } -impl_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]); - fn is_path_self(e: &Expr<'_>) -> bool { if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind { matches!(p.res, Res::SelfCtor(..) | Res::Def(DefKind::Ctor(..), _)) diff --git a/src/tools/clippy/clippy_lints/src/derive/mod.rs b/src/tools/clippy/clippy_lints/src/derive/mod.rs index 86614201c406..fa1a7037154e 100644 --- a/src/tools/clippy/clippy_lints/src/derive/mod.rs +++ b/src/tools/clippy/clippy_lints/src/derive/mod.rs @@ -10,36 +10,6 @@ mod expl_impl_clone_on_copy; mod unsafe_derive_deserialize; -declare_clippy_lint! { - /// ### What it does - /// Lints against manual `PartialEq` implementations for types with a derived `Hash` - /// implementation. - /// - /// ### Why is this bad? - /// The implementation of these traits must agree (for - /// example for use with `HashMap`) so it’s probably a bad idea to use a - /// default-generated `Hash` implementation with an explicitly defined - /// `PartialEq`. In particular, the following must hold for any type: - /// - /// ```text - /// k1 == k2 ⇒ hash(k1) == hash(k2) - /// ``` - /// - /// ### Example - /// ```ignore - /// #[derive(Hash)] - /// struct Foo; - /// - /// impl PartialEq for Foo { - /// ... - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub DERIVED_HASH_WITH_MANUAL_EQ, - correctness, - "deriving `Hash` but implementing `PartialEq` explicitly" -} - declare_clippy_lint! { /// ### What it does /// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord` @@ -91,6 +61,68 @@ "deriving `Ord` but implementing `PartialOrd` explicitly" } +declare_clippy_lint! { + /// ### What it does + /// Checks for types that derive `PartialEq` and could implement `Eq`. + /// + /// ### Why is this bad? + /// If a type `T` derives `PartialEq` and all of its members implement `Eq`, + /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used + /// in APIs that require `Eq` types. It also allows structs containing `T` to derive + /// `Eq` themselves. + /// + /// ### Example + /// ```no_run + /// #[derive(PartialEq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + /// Use instead: + /// ```no_run + /// #[derive(PartialEq, Eq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + #[clippy::version = "1.63.0"] + pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, + nursery, + "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" +} + +declare_clippy_lint! { + /// ### What it does + /// Lints against manual `PartialEq` implementations for types with a derived `Hash` + /// implementation. + /// + /// ### Why is this bad? + /// The implementation of these traits must agree (for + /// example for use with `HashMap`) so it’s probably a bad idea to use a + /// default-generated `Hash` implementation with an explicitly defined + /// `PartialEq`. In particular, the following must hold for any type: + /// + /// ```text + /// k1 == k2 ⇒ hash(k1) == hash(k2) + /// ``` + /// + /// ### Example + /// ```ignore + /// #[derive(Hash)] + /// struct Foo; + /// + /// impl PartialEq for Foo { + /// ... + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub DERIVED_HASH_WITH_MANUAL_EQ, + correctness, + "deriving `Hash` but implementing `PartialEq` explicitly" +} + declare_clippy_lint! { /// ### What it does /// Checks for explicit `Clone` implementations for `Copy` @@ -152,44 +184,12 @@ "deriving `serde::Deserialize` on a type that has methods using `unsafe`" } -declare_clippy_lint! { - /// ### What it does - /// Checks for types that derive `PartialEq` and could implement `Eq`. - /// - /// ### Why is this bad? - /// If a type `T` derives `PartialEq` and all of its members implement `Eq`, - /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used - /// in APIs that require `Eq` types. It also allows structs containing `T` to derive - /// `Eq` themselves. - /// - /// ### Example - /// ```no_run - /// #[derive(PartialEq)] - /// struct Foo { - /// i_am_eq: i32, - /// i_am_eq_too: Vec, - /// } - /// ``` - /// Use instead: - /// ```no_run - /// #[derive(PartialEq, Eq)] - /// struct Foo { - /// i_am_eq: i32, - /// i_am_eq_too: Vec, - /// } - /// ``` - #[clippy::version = "1.63.0"] - pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, - nursery, - "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" -} - declare_lint_pass!(Derive => [ - EXPL_IMPL_CLONE_ON_COPY, DERIVED_HASH_WITH_MANUAL_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, + DERIVE_PARTIAL_EQ_WITHOUT_EQ, + EXPL_IMPL_CLONE_ON_COPY, UNSAFE_DERIVE_DESERIALIZE, - DERIVE_PARTIAL_EQ_WITHOUT_EQ ]); impl<'tcx> LateLintPass<'tcx> for Derive { diff --git a/src/tools/clippy/clippy_lints/src/derive/unsafe_derive_deserialize.rs b/src/tools/clippy/clippy_lints/src/derive/unsafe_derive_deserialize.rs index 38f3251fd389..efcf2b70f0ca 100644 --- a/src/tools/clippy/clippy_lints/src/derive/unsafe_derive_deserialize.rs +++ b/src/tools/clippy/clippy_lints/src/derive/unsafe_derive_deserialize.rs @@ -1,14 +1,14 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::{is_lint_allowed, paths}; +use clippy_utils::{is_lint_allowed, paths, sym}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, UnsafeSource}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; -use rustc_span::{Span, sym}; +use rustc_span::Span; use super::UNSAFE_DERIVE_DESERIALIZE; diff --git a/src/tools/clippy/clippy_lints/src/disallowed_fields.rs b/src/tools/clippy/clippy_lints/src/disallowed_fields.rs new file mode 100644 index 000000000000..9873c32f427f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/disallowed_fields.rs @@ -0,0 +1,160 @@ +use clippy_config::Conf; +use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::PathNS; +use clippy_utils::ty::get_field_def_id_by_name; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Expr, ExprKind, Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; +use rustc_session::impl_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Denies the configured fields in clippy.toml + /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// fields are defined in the clippy.toml file. + /// + /// ### Why is this bad? + /// Some fields are undesirable in certain contexts, and it's beneficial to + /// lint for them as needed. + /// + /// ### Example + /// An example clippy.toml configuration: + /// ```toml + /// # clippy.toml + /// disallowed-fields = [ + /// # Can use a string as the path of the disallowed field. + /// "std::ops::Range::start", + /// # Can also use an inline table with a `path` key. + /// { path = "std::ops::Range::start" }, + /// # When using an inline table, can add a `reason` for why the field + /// # is disallowed. + /// { path = "std::ops::Range::start", reason = "The start of the range is not used" }, + /// ] + /// ``` + /// + /// ```rust + /// use std::ops::Range; + /// + /// let range = Range { start: 0, end: 1 }; + /// println!("{}", range.start); // `start` is disallowed in the config. + /// ``` + /// + /// Use instead: + /// ```rust + /// use std::ops::Range; + /// + /// let range = Range { start: 0, end: 1 }; + /// println!("{}", range.end); // `end` is _not_ disallowed in the config. + /// ``` + #[clippy::version = "1.93.0"] + pub DISALLOWED_FIELDS, + style, + "declaration of a disallowed field use" +} + +impl_lint_pass!(DisallowedFields => [DISALLOWED_FIELDS]); + +pub struct DisallowedFields { + disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, +} + +impl DisallowedFields { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { + let (disallowed, _) = create_disallowed_map( + tcx, + &conf.disallowed_fields, + PathNS::Field, + |def_kind| matches!(def_kind, DefKind::Field), + "field", + false, + ); + Self { disallowed } + } +} + +impl<'tcx> LateLintPass<'tcx> for DisallowedFields { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let (id, span) = match &expr.kind { + ExprKind::Path(path) if let Res::Def(_, id) = cx.qpath_res(path, expr.hir_id) => (id, expr.span), + ExprKind::Field(e, ident) => { + // Very round-about way to get the field `DefId` from the expr: first we get its + // parent `Ty`. Then we go through all its fields to find the one with the expected + // name and get the `DefId` from it. + if let Some(parent_ty) = cx.typeck_results().expr_ty_adjusted_opt(e) + && let Some(field_def_id) = get_field_def_id_by_name(parent_ty, ident.name) + { + (field_def_id, ident.span) + } else { + return; + } + }, + _ => return, + }; + if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) { + span_lint_and_then( + cx, + DISALLOWED_FIELDS, + span, + format!("use of a disallowed field `{path}`"), + disallowed_path.diag_amendment(span), + ); + } + } + + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { + let PatKind::Struct(struct_path, pat_fields, _) = pat.kind else { + return; + }; + match cx.typeck_results().qpath_res(&struct_path, pat.hir_id) { + Res::Def(DefKind::Struct, struct_def_id) => { + let adt_def = cx.tcx.adt_def(struct_def_id); + for field in pat_fields { + if let Some(def_id) = adt_def.all_fields().find_map(|adt_field| { + if field.ident.name == adt_field.name { + Some(adt_field.did) + } else { + None + } + }) && let Some(&(path, disallowed_path)) = self.disallowed.get(&def_id) + { + span_lint_and_then( + cx, + DISALLOWED_FIELDS, + field.span, + format!("use of a disallowed field `{path}`"), + disallowed_path.diag_amendment(field.span), + ); + } + } + }, + Res::Def(DefKind::Variant, variant_def_id) => { + let enum_def_id = cx.tcx.parent(variant_def_id); + let variant = cx.tcx.adt_def(enum_def_id).variant_with_id(variant_def_id); + + for field in pat_fields { + if let Some(def_id) = variant.fields.iter().find_map(|adt_field| { + if field.ident.name == adt_field.name { + Some(adt_field.did) + } else { + None + } + }) && let Some(&(path, disallowed_path)) = self.disallowed.get(&def_id) + { + span_lint_and_then( + cx, + DISALLOWED_FIELDS, + field.span, + format!("use of a disallowed field `{path}`"), + disallowed_path.diag_amendment(field.span), + ); + } + } + }, + _ => {}, + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index 1c9c971730f6..7253b65e4337 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -63,6 +63,8 @@ "use of a disallowed macro" } +impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]); + pub struct DisallowedMacros { disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, seen: FxHashSet, @@ -125,8 +127,6 @@ fn check(&mut self, cx: &LateContext<'_>, span: Span, derive_src: Option [DISALLOWED_MACROS]); - impl LateLintPass<'_> for DisallowedMacros { fn check_crate(&mut self, cx: &LateContext<'_>) { // once we check a crate in the late pass we can emit the early pass lints diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index 58403ad19235..e2fd71b7d990 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -61,6 +61,8 @@ "use of a disallowed method call" } +impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); + pub struct DisallowedMethods { disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, } @@ -84,8 +86,6 @@ pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { } } -impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); - impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.desugaring_kind().is_some() { diff --git a/src/tools/clippy/clippy_lints/src/disallowed_names.rs b/src/tools/clippy/clippy_lints/src/disallowed_names.rs index 566aa12b08f5..1f658b2aa068 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_names.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_names.rs @@ -26,6 +26,8 @@ "usage of a disallowed/placeholder name" } +impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]); + pub struct DisallowedNames { disallow: FxHashSet, } @@ -38,8 +40,6 @@ pub fn new(conf: &'static Conf) -> Self { } } -impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]); - impl<'tcx> LateLintPass<'tcx> for DisallowedNames { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { if let PatKind::Binding(.., ident, _) = pat.kind 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 5b4fe2a6b803..4596d9457c0b 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs @@ -45,6 +45,8 @@ "usage of non-allowed Unicode scripts" } +impl_lint_pass!(DisallowedScriptIdents => [DISALLOWED_SCRIPT_IDENTS]); + pub struct DisallowedScriptIdents { whitelist: FxHashSet

{ diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 594147720193..ad6479f84c73 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1532,7 +1532,7 @@ pub const fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { } } -#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_conversion_trait_impls", since = "1.95.0")] impl From<[MaybeUninit; N]> for MaybeUninit<[T; N]> { #[inline] fn from(arr: [MaybeUninit; N]) -> Self { @@ -1540,7 +1540,7 @@ pub const fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { } } -#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_conversion_trait_impls", since = "1.95.0")] impl AsRef<[MaybeUninit; N]> for MaybeUninit<[T; N]> { #[inline] fn as_ref(&self) -> &[MaybeUninit; N] { @@ -1549,7 +1549,7 @@ pub const fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { } } -#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_conversion_trait_impls", since = "1.95.0")] impl AsRef<[MaybeUninit]> for MaybeUninit<[T; N]> { #[inline] fn as_ref(&self) -> &[MaybeUninit] { @@ -1557,7 +1557,7 @@ fn as_ref(&self) -> &[MaybeUninit] { } } -#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_conversion_trait_impls", since = "1.95.0")] impl AsMut<[MaybeUninit; N]> for MaybeUninit<[T; N]> { #[inline] fn as_mut(&mut self) -> &mut [MaybeUninit; N] { @@ -1566,7 +1566,7 @@ fn as_ref(&self) -> &[MaybeUninit] { } } -#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_conversion_trait_impls", since = "1.95.0")] impl AsMut<[MaybeUninit]> for MaybeUninit<[T; N]> { #[inline] fn as_mut(&mut self) -> &mut [MaybeUninit] { @@ -1574,7 +1574,7 @@ fn as_mut(&mut self) -> &mut [MaybeUninit] { } } -#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_conversion_trait_impls", since = "1.95.0")] impl From> for [MaybeUninit; N] { #[inline] fn from(arr: MaybeUninit<[T; N]>) -> Self { diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index eb6f8f975721..d8521e79006e 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1488,12 +1488,13 @@ impl SizedTypeProperties for T {} /// /// [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")] pub const unsafe fn conjure_zst() -> T { const_assert!( size_of::() == 0, - "mem::conjure_zst invoked on a nonzero-sized type", - "mem::conjure_zst invoked on type {t}, which is not zero-sized", - t: &str = stringify!(T) + "mem::conjure_zst invoked on a non-zero-sized type", + "mem::conjure_zst invoked on type {name}, which is not zero-sized", + name: &str = crate::any::type_name::() ); // SAFETY: because the caller must guarantee that it's inhabited and zero-sized, diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index f8c2a259ba7e..e4d47dedb860 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -3,6 +3,8 @@ use crate::any::TypeId; use crate::intrinsics::{type_id, type_of}; +use crate::marker::PointeeSized; +use crate::ptr::DynMetadata; /// Compile-time type information. #[derive(Debug)] @@ -16,6 +18,21 @@ pub struct Type { pub size: Option, } +/// Info of a trait implementation, you can retrieve the vtable with [Self::get_vtable] +#[derive(Debug, PartialEq, Eq)] +#[unstable(feature = "type_info", issue = "146922")] +#[non_exhaustive] +pub struct TraitImpl { + pub(crate) vtable: DynMetadata, +} + +impl TraitImpl { + /// Gets the raw vtable for type reflection mapping + pub const fn get_vtable(&self) -> DynMetadata { + self.vtable + } +} + impl TypeId { /// Compute the type information of a concrete type. /// It can only be called at compile time. @@ -75,6 +92,8 @@ pub enum TypeKind { Reference(Reference), /// Pointers. Pointer(Pointer), + /// Function pointers. + FnPtr(FnPtr), /// FIXME(#146922): add all the common types Other, } @@ -151,7 +170,7 @@ pub struct Trait { pub is_auto: bool, } -/// Compile-time type information about arrays. +/// Compile-time type information about structs. #[derive(Debug)] #[non_exhaustive] #[unstable(feature = "type_info", issue = "146922")] @@ -305,3 +324,39 @@ pub struct Pointer { /// Whether this pointer is mutable or not. pub mutable: bool, } + +#[derive(Debug)] +#[unstable(feature = "type_info", issue = "146922")] +/// Function pointer, e.g. fn(u8), +pub struct FnPtr { + /// Unsafety, true is unsafe + pub unsafety: bool, + + /// Abi, e.g. extern "C" + pub abi: Abi, + + /// Function inputs + pub inputs: &'static [TypeId], + + /// Function return type, default is TypeId::of::<()> + pub output: TypeId, + + /// Vardiadic function, e.g. extern "C" fn add(n: usize, mut args: ...); + pub variadic: bool, +} + +#[derive(Debug, Default)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +/// Abi of [FnPtr] +pub enum Abi { + /// Named abi, e.g. extern "custom", "stdcall" etc. + Named(&'static str), + + /// Default + #[default] + ExternRust, + + /// C-calling convention + ExternC, +} diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index d114b821655b..03bc5f20d7e9 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -275,6 +275,70 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] pub const NEG_INFINITY: f128 = -1.0_f128 / 0.0_f128; + /// Maximum integer that can be represented exactly in an [`f128`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i128`] and [`f128`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f128`] and back to + /// [`i128`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f128`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// [`MAX_EXACT_INTEGER`]: f128::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f128::MIN_EXACT_INTEGER + /// ``` + /// #![feature(f128)] + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// # #[cfg(target_has_reliable_f128)] { + /// let max_exact_int = f128::MAX_EXACT_INTEGER; + /// assert_eq!(max_exact_int, max_exact_int as f128 as i128); + /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f128 as i128); + /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f128 as i128); + /// + /// // Beyond `f128::MAX_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((max_exact_int + 1) as f128, (max_exact_int + 2) as f128); + /// # }} + /// ``` + // #[unstable(feature = "f128", issue = "116909")] + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MAX_EXACT_INTEGER: i128 = (1 << Self::MANTISSA_DIGITS) - 1; + + /// Minimum integer that can be represented exactly in an [`f128`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i128`] and [`f128`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f128`] and back to + /// [`i128`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f128`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// This constant is equivalent to `-MAX_EXACT_INTEGER`. + /// + /// [`MAX_EXACT_INTEGER`]: f128::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f128::MIN_EXACT_INTEGER + /// ``` + /// #![feature(f128)] + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// # #[cfg(target_has_reliable_f128)] { + /// let min_exact_int = f128::MIN_EXACT_INTEGER; + /// assert_eq!(min_exact_int, min_exact_int as f128 as i128); + /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f128 as i128); + /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f128 as i128); + /// + /// // Below `f128::MIN_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((min_exact_int - 1) as f128, (min_exact_int - 2) as f128); + /// # }} + /// ``` + // #[unstable(feature = "f128", issue = "116909")] + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MIN_EXACT_INTEGER: i128 = -Self::MAX_EXACT_INTEGER; + /// Sign bit pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 373225c5806c..1ffaf3b0ce44 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -14,7 +14,7 @@ use crate::convert::FloatToInt; use crate::num::FpCategory; #[cfg(not(test))] -use crate::num::libm; +use crate::num::imp::libm; use crate::panic::const_assert; use crate::{intrinsics, mem}; @@ -269,6 +269,70 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] pub const NEG_INFINITY: f16 = -1.0_f16 / 0.0_f16; + /// Maximum integer that can be represented exactly in an [`f16`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i16`] and [`f16`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f16`] and back to + /// [`i16`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f16`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// [`MAX_EXACT_INTEGER`]: f16::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f16::MIN_EXACT_INTEGER + /// ``` + /// #![feature(f16)] + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// # #[cfg(target_has_reliable_f16)] { + /// let max_exact_int = f16::MAX_EXACT_INTEGER; + /// assert_eq!(max_exact_int, max_exact_int as f16 as i16); + /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f16 as i16); + /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f16 as i16); + /// + /// // Beyond `f16::MAX_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((max_exact_int + 1) as f16, (max_exact_int + 2) as f16); + /// # }} + /// ``` + // #[unstable(feature = "f16", issue = "116909")] + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MAX_EXACT_INTEGER: i16 = (1 << Self::MANTISSA_DIGITS) - 1; + + /// Minimum integer that can be represented exactly in an [`f16`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i16`] and [`f16`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f16`] and back to + /// [`i16`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f16`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// This constant is equivalent to `-MAX_EXACT_INTEGER`. + /// + /// [`MAX_EXACT_INTEGER`]: f16::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f16::MIN_EXACT_INTEGER + /// ``` + /// #![feature(f16)] + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// # #[cfg(target_has_reliable_f16)] { + /// let min_exact_int = f16::MIN_EXACT_INTEGER; + /// assert_eq!(min_exact_int, min_exact_int as f16 as i16); + /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f16 as i16); + /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f16 as i16); + /// + /// // Below `f16::MIN_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((min_exact_int - 1) as f16, (min_exact_int - 2) as f16); + /// # }} + /// ``` + // #[unstable(feature = "f16", issue = "116909")] + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MIN_EXACT_INTEGER: i16 = -Self::MAX_EXACT_INTEGER; + /// Sign bit pub(crate) const SIGN_MASK: u16 = 0x8000; @@ -1457,7 +1521,11 @@ pub const fn algebraic_rem(self, rhs: f16) -> f16 { // Functions in this module fall into `core_float_math` // #[unstable(feature = "core_float_math", issue = "137578")] #[cfg(not(test))] -#[doc(test(attr(feature(cfg_target_has_reliable_f16_f128), expect(internal_features))))] +#[doc(test(attr( + feature(cfg_target_has_reliable_f16_f128), + expect(internal_features), + allow(unused_features) +)))] impl f16 { /// Returns the largest integer less than or equal to `self`. /// diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index f3c7961931a1..4f560201196c 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -393,6 +393,7 @@ pub mod consts { pub const LN_10: f32 = 2.30258509299404568401799145468436421_f32; } +#[doc(test(attr(allow(unused_features))))] impl f32 { /// The radix or base of the internal representation of `f32`. #[stable(feature = "assoc_int_consts", since = "1.43.0")] @@ -513,6 +514,64 @@ impl f32 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32; + /// Maximum integer that can be represented exactly in an [`f32`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i32`] and [`f32`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f32`] and back to + /// [`i32`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f32`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// [`MAX_EXACT_INTEGER`]: f32::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f32::MIN_EXACT_INTEGER + /// ``` + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// let max_exact_int = f32::MAX_EXACT_INTEGER; + /// assert_eq!(max_exact_int, max_exact_int as f32 as i32); + /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f32 as i32); + /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f32 as i32); + /// + /// // Beyond `f32::MAX_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((max_exact_int + 1) as f32, (max_exact_int + 2) as f32); + /// # } + /// ``` + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MAX_EXACT_INTEGER: i32 = (1 << Self::MANTISSA_DIGITS) - 1; + + /// Minimum integer that can be represented exactly in an [`f32`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i32`] and [`f32`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f32`] and back to + /// [`i32`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f32`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// This constant is equivalent to `-MAX_EXACT_INTEGER`. + /// + /// [`MAX_EXACT_INTEGER`]: f32::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f32::MIN_EXACT_INTEGER + /// ``` + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// let min_exact_int = f32::MIN_EXACT_INTEGER; + /// assert_eq!(min_exact_int, min_exact_int as f32 as i32); + /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f32 as i32); + /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f32 as i32); + /// + /// // Below `f32::MIN_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((min_exact_int - 1) as f32, (min_exact_int - 2) as f32); + /// # } + /// ``` + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MIN_EXACT_INTEGER: i32 = -Self::MAX_EXACT_INTEGER; + /// Sign bit pub(crate) const SIGN_MASK: u32 = 0x8000_0000; @@ -1633,7 +1692,7 @@ pub const fn algebraic_rem(self, rhs: f32) -> f32 { #[unstable(feature = "core_float_math", issue = "137578")] pub mod math { use crate::intrinsics; - use crate::num::libm; + use crate::num::imp::libm; /// Experimental version of `floor` in `core`. See [`f32::floor`] for details. /// diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index a6fd3b1cb5d0..061a73ae5f64 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -393,6 +393,7 @@ pub mod consts { pub const LN_10: f64 = 2.30258509299404568401799145468436421_f64; } +#[doc(test(attr(allow(unused_features))))] impl f64 { /// The radix or base of the internal representation of `f64`. #[stable(feature = "assoc_int_consts", since = "1.43.0")] @@ -512,6 +513,64 @@ impl f64 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64; + /// Maximum integer that can be represented exactly in an [`f64`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i64`] and [`f64`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f64`] and back to + /// [`i64`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f64`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// [`MAX_EXACT_INTEGER`]: f64::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f64::MIN_EXACT_INTEGER + /// ``` + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// let max_exact_int = f64::MAX_EXACT_INTEGER; + /// assert_eq!(max_exact_int, max_exact_int as f64 as i64); + /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f64 as i64); + /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f64 as i64); + /// + /// // Beyond `f64::MAX_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((max_exact_int + 1) as f64, (max_exact_int + 2) as f64); + /// # } + /// ``` + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MAX_EXACT_INTEGER: i64 = (1 << Self::MANTISSA_DIGITS) - 1; + + /// Minimum integer that can be represented exactly in an [`f64`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i64`] and [`f64`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f64`] and back to + /// [`i64`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f64`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// This constant is equivalent to `-MAX_EXACT_INTEGER`. + /// + /// [`MAX_EXACT_INTEGER`]: f64::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f64::MIN_EXACT_INTEGER + /// ``` + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// let min_exact_int = f64::MIN_EXACT_INTEGER; + /// assert_eq!(min_exact_int, min_exact_int as f64 as i64); + /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f64 as i64); + /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f64 as i64); + /// + /// // Below `f64::MIN_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((min_exact_int - 1) as f64, (min_exact_int - 2) as f64); + /// # } + /// ``` + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MIN_EXACT_INTEGER: i64 = -Self::MAX_EXACT_INTEGER; + /// Sign bit pub(crate) const SIGN_MASK: u64 = 0x8000_0000_0000_0000; @@ -1631,7 +1690,7 @@ pub const fn algebraic_rem(self, rhs: f64) -> f64 { /// They will be stabilized as inherent methods._ pub mod math { use crate::intrinsics; - use crate::num::libm; + use crate::num::imp::libm; /// Experimental version of `floor` in `core`. See [`f64::floor`] for details. /// diff --git a/library/core/src/num/float_parse.rs b/library/core/src/num/float_parse.rs new file mode 100644 index 000000000000..d8846a0cc0a8 --- /dev/null +++ b/library/core/src/num/float_parse.rs @@ -0,0 +1,133 @@ +//! User-facing API for float parsing. + +use crate::error::Error; +use crate::fmt; +use crate::num::imp::dec2flt; +use crate::str::FromStr; + +macro_rules! from_str_float_impl { + ($t:ty) => { + #[stable(feature = "rust1", since = "1.0.0")] + impl FromStr for $t { + type Err = ParseFloatError; + + /// Converts a string in base 10 to a float. + /// Accepts an optional decimal exponent. + /// + /// This function accepts strings such as + /// + /// * '3.14' + /// * '-3.14' + /// * '2.5E10', or equivalently, '2.5e10' + /// * '2.5E-10' + /// * '5.' + /// * '.5', or, equivalently, '0.5' + /// * '7' + /// * '007' + /// * 'inf', '-inf', '+infinity', 'NaN' + /// + /// Note that alphabetical characters are not case-sensitive. + /// + /// Leading and trailing whitespace represent an error. + /// + /// # Grammar + /// + /// All strings that adhere to the following [EBNF] grammar when + /// lowercased will result in an [`Ok`] being returned: + /// + /// ```txt + /// Float ::= Sign? ( 'inf' | 'infinity' | 'nan' | Number ) + /// Number ::= ( Digit+ | + /// Digit+ '.' Digit* | + /// Digit* '.' Digit+ ) Exp? + /// Exp ::= 'e' Sign? Digit+ + /// Sign ::= [+-] + /// Digit ::= [0-9] + /// ``` + /// + /// [EBNF]: https://www.w3.org/TR/REC-xml/#sec-notation + /// + /// # Arguments + /// + /// * src - A string + /// + /// # Return value + /// + /// `Err(ParseFloatError)` if the string did not represent a valid + /// number. Otherwise, `Ok(n)` where `n` is the closest + /// representable floating-point number to the number represented + /// by `src` (following the same rules for rounding as for the + /// results of primitive operations). + // We add the `#[inline(never)]` attribute, since its content will + // be filled with that of `dec2flt`, which has #[inline(always)]. + // Since `dec2flt` is generic, a normal inline attribute on this function + // with `dec2flt` having no attributes results in heavily repeated + // generation of `dec2flt`, despite the fact only a maximum of 2 + // possible instances can ever exist. Adding #[inline(never)] avoids this. + #[inline(never)] + fn from_str(src: &str) -> Result { + dec2flt::dec2flt(src) + } + } + }; +} + +#[cfg(target_has_reliable_f16)] +from_str_float_impl!(f16); +from_str_float_impl!(f32); +from_str_float_impl!(f64); + +// FIXME(f16): A fallback is used when the backend+target does not support f16 well, in order +// to avoid ICEs. + +#[cfg(not(target_has_reliable_f16))] +#[expect(ineffective_unstable_trait_impl, reason = "stable trait on unstable type")] +#[unstable(feature = "f16", issue = "116909")] +impl FromStr for f16 { + type Err = ParseFloatError; + + #[inline] + fn from_str(_src: &str) -> Result { + unimplemented!("requires target_has_reliable_f16") + } +} + +/// An error which can be returned when parsing a float. +/// +/// This error is used as the error type for the [`FromStr`] implementation +/// for [`f32`] and [`f64`]. +/// +/// # Example +/// +/// ``` +/// use std::str::FromStr; +/// +/// if let Err(e) = f64::from_str("a.12") { +/// println!("Failed conversion to f64: {e}"); +/// } +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct ParseFloatError { + pub(super) kind: FloatErrorKind, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(super) enum FloatErrorKind { + Empty, + Invalid, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for ParseFloatError {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for ParseFloatError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.kind { + FloatErrorKind::Empty => "cannot parse float from empty string", + FloatErrorKind::Invalid => "invalid float literal", + } + .fmt(f) + } +} diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/imp/bignum.rs similarity index 98% rename from library/core/src/num/bignum.rs rename to library/core/src/num/imp/bignum.rs index 95b49a38ded0..9e930ed15a23 100644 --- a/library/core/src/num/bignum.rs +++ b/library/core/src/num/imp/bignum.rs @@ -251,7 +251,7 @@ pub fn mul_pow2(&mut self, bits: usize) -> &mut $name { /// Multiplies itself by `5^e` and returns its own mutable reference. pub fn mul_pow5(&mut self, mut e: usize) -> &mut $name { - use crate::num::bignum::SMALL_POW5; + use crate::num::imp::bignum::SMALL_POW5; // There are exactly n trailing zeros on 2^n, and the only relevant digit sizes // are consecutive powers of two, so this is well suited index for the table. @@ -281,7 +281,7 @@ pub fn mul_pow5(&mut self, mut e: usize) -> &mut $name { pub fn mul_digits<'a>(&'a mut self, other: &[$ty]) -> &'a mut $name { // the internal routine. works best when aa.len() <= bb.len(). fn mul_inner(ret: &mut [$ty; $n], aa: &[$ty], bb: &[$ty]) -> usize { - use crate::num::bignum::FullOps; + use crate::num::imp::bignum::FullOps; let mut retsz = 0; for (i, &a) in aa.iter().enumerate() { @@ -320,7 +320,7 @@ pub fn mul_digits<'a>(&'a mut self, other: &[$ty]) -> &'a mut $name { /// Divides itself by a digit-sized `other` and returns its own /// mutable reference *and* the remainder. pub fn div_rem_small(&mut self, other: $ty) -> (&mut $name, $ty) { - use crate::num::bignum::FullOps; + use crate::num::imp::bignum::FullOps; assert!(other > 0); diff --git a/library/core/src/num/dec2flt/common.rs b/library/core/src/num/imp/dec2flt/common.rs similarity index 100% rename from library/core/src/num/dec2flt/common.rs rename to library/core/src/num/imp/dec2flt/common.rs diff --git a/library/core/src/num/dec2flt/decimal.rs b/library/core/src/num/imp/dec2flt/decimal.rs similarity index 97% rename from library/core/src/num/dec2flt/decimal.rs rename to library/core/src/num/imp/dec2flt/decimal.rs index db7176c12431..27a53d4b9e75 100644 --- a/library/core/src/num/dec2flt/decimal.rs +++ b/library/core/src/num/imp/dec2flt/decimal.rs @@ -1,7 +1,9 @@ //! Representation of a float as the significant digits and exponent. -use crate::num::dec2flt::float::RawFloat; -use crate::num::dec2flt::fpu::set_precision; +use dec2flt::float::RawFloat; +use dec2flt::fpu::set_precision; + +use crate::num::imp::dec2flt; const INT_POW10: [u64; 16] = [ 1, diff --git a/library/core/src/num/dec2flt/decimal_seq.rs b/library/core/src/num/imp/dec2flt/decimal_seq.rs similarity index 99% rename from library/core/src/num/dec2flt/decimal_seq.rs rename to library/core/src/num/imp/dec2flt/decimal_seq.rs index de22280c001c..0c73cdaa03a0 100644 --- a/library/core/src/num/dec2flt/decimal_seq.rs +++ b/library/core/src/num/imp/dec2flt/decimal_seq.rs @@ -9,7 +9,9 @@ //! algorithm can be found in "ParseNumberF64 by Simple Decimal Conversion", //! available online: . -use crate::num::dec2flt::common::{ByteSlice, is_8digits}; +use dec2flt::common::{ByteSlice, is_8digits}; + +use crate::num::imp::dec2flt; /// A decimal floating-point number, represented as a sequence of decimal digits. #[derive(Clone, Debug, PartialEq)] diff --git a/library/core/src/num/dec2flt/float.rs b/library/core/src/num/imp/dec2flt/float.rs similarity index 100% rename from library/core/src/num/dec2flt/float.rs rename to library/core/src/num/imp/dec2flt/float.rs diff --git a/library/core/src/num/dec2flt/fpu.rs b/library/core/src/num/imp/dec2flt/fpu.rs similarity index 100% rename from library/core/src/num/dec2flt/fpu.rs rename to library/core/src/num/imp/dec2flt/fpu.rs diff --git a/library/core/src/num/dec2flt/lemire.rs b/library/core/src/num/imp/dec2flt/lemire.rs similarity index 97% rename from library/core/src/num/dec2flt/lemire.rs rename to library/core/src/num/imp/dec2flt/lemire.rs index f84929a03c17..c3f2723509d0 100644 --- a/library/core/src/num/dec2flt/lemire.rs +++ b/library/core/src/num/imp/dec2flt/lemire.rs @@ -1,10 +1,10 @@ //! Implementation of the Eisel-Lemire algorithm. -use crate::num::dec2flt::common::BiasedFp; -use crate::num::dec2flt::float::RawFloat; -use crate::num::dec2flt::table::{ - LARGEST_POWER_OF_FIVE, POWER_OF_FIVE_128, SMALLEST_POWER_OF_FIVE, -}; +use dec2flt::common::BiasedFp; +use dec2flt::float::RawFloat; +use dec2flt::table::{LARGEST_POWER_OF_FIVE, POWER_OF_FIVE_128, SMALLEST_POWER_OF_FIVE}; + +use crate::num::imp::dec2flt; /// Compute w * 10^q using an extended-precision float representation. /// diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/imp/dec2flt/mod.rs similarity index 62% rename from library/core/src/num/dec2flt/mod.rs rename to library/core/src/num/imp/dec2flt/mod.rs index 66e30e1c5f7f..3f5724add62b 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/imp/dec2flt/mod.rs @@ -87,14 +87,14 @@ issue = "none" )] -use self::common::BiasedFp; -use self::float::RawFloat; -use self::lemire::compute_float; -use self::parse::{parse_inf_nan, parse_number}; -use self::slow::parse_long_mantissa; -use crate::error::Error; -use crate::fmt; -use crate::str::FromStr; +use common::BiasedFp; +use float::RawFloat; +use lemire::compute_float; +use parse::{parse_inf_nan, parse_number}; +use slow::parse_long_mantissa; + +use crate::num::ParseFloatError; +use crate::num::float_parse::FloatErrorKind; mod common; pub mod decimal; @@ -107,131 +107,6 @@ pub mod lemire; pub mod parse; -macro_rules! from_str_float_impl { - ($t:ty) => { - #[stable(feature = "rust1", since = "1.0.0")] - impl FromStr for $t { - type Err = ParseFloatError; - - /// Converts a string in base 10 to a float. - /// Accepts an optional decimal exponent. - /// - /// This function accepts strings such as - /// - /// * '3.14' - /// * '-3.14' - /// * '2.5E10', or equivalently, '2.5e10' - /// * '2.5E-10' - /// * '5.' - /// * '.5', or, equivalently, '0.5' - /// * '7' - /// * '007' - /// * 'inf', '-inf', '+infinity', 'NaN' - /// - /// Note that alphabetical characters are not case-sensitive. - /// - /// Leading and trailing whitespace represent an error. - /// - /// # Grammar - /// - /// All strings that adhere to the following [EBNF] grammar when - /// lowercased will result in an [`Ok`] being returned: - /// - /// ```txt - /// Float ::= Sign? ( 'inf' | 'infinity' | 'nan' | Number ) - /// Number ::= ( Digit+ | - /// Digit+ '.' Digit* | - /// Digit* '.' Digit+ ) Exp? - /// Exp ::= 'e' Sign? Digit+ - /// Sign ::= [+-] - /// Digit ::= [0-9] - /// ``` - /// - /// [EBNF]: https://www.w3.org/TR/REC-xml/#sec-notation - /// - /// # Arguments - /// - /// * src - A string - /// - /// # Return value - /// - /// `Err(ParseFloatError)` if the string did not represent a valid - /// number. Otherwise, `Ok(n)` where `n` is the closest - /// representable floating-point number to the number represented - /// by `src` (following the same rules for rounding as for the - /// results of primitive operations). - // We add the `#[inline(never)]` attribute, since its content will - // be filled with that of `dec2flt`, which has #[inline(always)]. - // Since `dec2flt` is generic, a normal inline attribute on this function - // with `dec2flt` having no attributes results in heavily repeated - // generation of `dec2flt`, despite the fact only a maximum of 2 - // possible instances can ever exist. Adding #[inline(never)] avoids this. - #[inline(never)] - fn from_str(src: &str) -> Result { - dec2flt(src) - } - } - }; -} - -#[cfg(target_has_reliable_f16)] -from_str_float_impl!(f16); -from_str_float_impl!(f32); -from_str_float_impl!(f64); - -// FIXME(f16): A fallback is used when the backend+target does not support f16 well, in order -// to avoid ICEs. - -#[cfg(not(target_has_reliable_f16))] -impl FromStr for f16 { - type Err = ParseFloatError; - - #[inline] - fn from_str(_src: &str) -> Result { - unimplemented!("requires target_has_reliable_f16") - } -} - -/// An error which can be returned when parsing a float. -/// -/// This error is used as the error type for the [`FromStr`] implementation -/// for [`f32`] and [`f64`]. -/// -/// # Example -/// -/// ``` -/// use std::str::FromStr; -/// -/// if let Err(e) = f64::from_str("a.12") { -/// println!("Failed conversion to f64: {e}"); -/// } -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct ParseFloatError { - kind: FloatErrorKind, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -enum FloatErrorKind { - Empty, - Invalid, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Error for ParseFloatError {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for ParseFloatError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.kind { - FloatErrorKind::Empty => "cannot parse float from empty string", - FloatErrorKind::Invalid => "invalid float literal", - } - .fmt(f) - } -} - #[inline] pub(super) fn pfe_empty() -> ParseFloatError { ParseFloatError { kind: FloatErrorKind::Empty } diff --git a/library/core/src/num/dec2flt/parse.rs b/library/core/src/num/imp/dec2flt/parse.rs similarity index 98% rename from library/core/src/num/dec2flt/parse.rs rename to library/core/src/num/imp/dec2flt/parse.rs index e38fedc58bec..e4049bc164c6 100644 --- a/library/core/src/num/dec2flt/parse.rs +++ b/library/core/src/num/imp/dec2flt/parse.rs @@ -1,8 +1,10 @@ //! Functions to parse floating-point numbers. -use crate::num::dec2flt::common::{ByteSlice, is_8digits}; -use crate::num::dec2flt::decimal::Decimal; -use crate::num::dec2flt::float::RawFloat; +use dec2flt::common::{ByteSlice, is_8digits}; +use dec2flt::decimal::Decimal; +use dec2flt::float::RawFloat; + +use crate::num::imp::dec2flt; const MIN_19DIGIT_INT: u64 = 100_0000_0000_0000_0000; diff --git a/library/core/src/num/dec2flt/slow.rs b/library/core/src/num/imp/dec2flt/slow.rs similarity index 96% rename from library/core/src/num/dec2flt/slow.rs rename to library/core/src/num/imp/dec2flt/slow.rs index 3baed4265239..089b12f5be22 100644 --- a/library/core/src/num/dec2flt/slow.rs +++ b/library/core/src/num/imp/dec2flt/slow.rs @@ -1,8 +1,10 @@ //! Slow, fallback algorithm for cases the Eisel-Lemire algorithm cannot round. -use crate::num::dec2flt::common::BiasedFp; -use crate::num::dec2flt::decimal_seq::{DecimalSeq, parse_decimal_seq}; -use crate::num::dec2flt::float::RawFloat; +use dec2flt::common::BiasedFp; +use dec2flt::decimal_seq::{DecimalSeq, parse_decimal_seq}; +use dec2flt::float::RawFloat; + +use crate::num::imp::dec2flt; /// Parse the significant digits and biased, binary exponent of a float. /// diff --git a/library/core/src/num/dec2flt/table.rs b/library/core/src/num/imp/dec2flt/table.rs similarity index 100% rename from library/core/src/num/dec2flt/table.rs rename to library/core/src/num/imp/dec2flt/table.rs diff --git a/library/core/src/num/diy_float.rs b/library/core/src/num/imp/diy_float.rs similarity index 100% rename from library/core/src/num/diy_float.rs rename to library/core/src/num/imp/diy_float.rs diff --git a/library/core/src/num/flt2dec/decoder.rs b/library/core/src/num/imp/flt2dec/decoder.rs similarity index 98% rename from library/core/src/num/flt2dec/decoder.rs rename to library/core/src/num/imp/flt2dec/decoder.rs index bd6e2cdbafec..3d6ad6608efe 100644 --- a/library/core/src/num/flt2dec/decoder.rs +++ b/library/core/src/num/imp/flt2dec/decoder.rs @@ -1,7 +1,7 @@ //! Decodes a floating-point value into individual parts and error ranges. use crate::num::FpCategory; -use crate::num::dec2flt::float::RawFloat; +use crate::num::imp::dec2flt::float::RawFloat; /// Decoded unsigned finite value, such that: /// diff --git a/library/core/src/num/flt2dec/estimator.rs b/library/core/src/num/imp/flt2dec/estimator.rs similarity index 100% rename from library/core/src/num/flt2dec/estimator.rs rename to library/core/src/num/imp/flt2dec/estimator.rs diff --git a/library/core/src/num/flt2dec/mod.rs b/library/core/src/num/imp/flt2dec/mod.rs similarity index 100% rename from library/core/src/num/flt2dec/mod.rs rename to library/core/src/num/imp/flt2dec/mod.rs diff --git a/library/core/src/num/flt2dec/strategy/dragon.rs b/library/core/src/num/imp/flt2dec/strategy/dragon.rs similarity index 98% rename from library/core/src/num/flt2dec/strategy/dragon.rs rename to library/core/src/num/imp/flt2dec/strategy/dragon.rs index dd73e4b4846d..de19ca6bb664 100644 --- a/library/core/src/num/flt2dec/strategy/dragon.rs +++ b/library/core/src/num/imp/flt2dec/strategy/dragon.rs @@ -4,11 +4,13 @@ //! [^1]: Burger, R. G. and Dybvig, R. K. 1996. Printing floating-point numbers //! quickly and accurately. SIGPLAN Not. 31, 5 (May. 1996), 108-116. +use flt2dec::estimator::estimate_scaling_factor; +use flt2dec::{Decoded, MAX_SIG_DIGITS, round_up}; + use crate::cmp::Ordering; use crate::mem::MaybeUninit; -use crate::num::bignum::{Big32x40 as Big, Digit32 as Digit}; -use crate::num::flt2dec::estimator::estimate_scaling_factor; -use crate::num::flt2dec::{Decoded, MAX_SIG_DIGITS, round_up}; +use crate::num::imp::bignum::{Big32x40 as Big, Digit32 as Digit}; +use crate::num::imp::flt2dec; static POW10: [Digit; 10] = [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000]; diff --git a/library/core/src/num/flt2dec/strategy/grisu.rs b/library/core/src/num/imp/flt2dec/strategy/grisu.rs similarity index 99% rename from library/core/src/num/flt2dec/strategy/grisu.rs rename to library/core/src/num/imp/flt2dec/strategy/grisu.rs index d3bbb0934e0f..f7ee46582934 100644 --- a/library/core/src/num/flt2dec/strategy/grisu.rs +++ b/library/core/src/num/imp/flt2dec/strategy/grisu.rs @@ -5,9 +5,11 @@ //! [^1]: Florian Loitsch. 2010. Printing floating-point numbers quickly and //! accurately with integers. SIGPLAN Not. 45, 6 (June 2010), 233-243. +use flt2dec::{Decoded, MAX_SIG_DIGITS, round_up}; + use crate::mem::MaybeUninit; -use crate::num::diy_float::Fp; -use crate::num::flt2dec::{Decoded, MAX_SIG_DIGITS, round_up}; +use crate::num::imp::diy_float::Fp; +use crate::num::imp::flt2dec; // see the comments in `format_shortest_opt` for the rationale. #[doc(hidden)] @@ -455,7 +457,7 @@ pub fn format_shortest<'a>( d: &Decoded, buf: &'a mut [MaybeUninit], ) -> (/*digits*/ &'a [u8], /*exp*/ i16) { - use crate::num::flt2dec::strategy::dragon::format_shortest as fallback; + use flt2dec::strategy::dragon::format_shortest as fallback; // SAFETY: The borrow checker is not smart enough to let us use `buf` // in the second branch, so we launder the lifetime here. But we only re-use // `buf` if `format_shortest_opt` returned `None` so this is okay. @@ -765,7 +767,7 @@ pub fn format_exact<'a>( buf: &'a mut [MaybeUninit], limit: i16, ) -> (/*digits*/ &'a [u8], /*exp*/ i16) { - use crate::num::flt2dec::strategy::dragon::format_exact as fallback; + use flt2dec::strategy::dragon::format_exact as fallback; // SAFETY: The borrow checker is not smart enough to let us use `buf` // in the second branch, so we launder the lifetime here. But we only re-use // `buf` if `format_exact_opt` returned `None` so this is okay. diff --git a/library/core/src/num/fmt.rs b/library/core/src/num/imp/fmt.rs similarity index 100% rename from library/core/src/num/fmt.rs rename to library/core/src/num/imp/fmt.rs diff --git a/library/core/src/num/int_bits.rs b/library/core/src/num/imp/int_bits.rs similarity index 97% rename from library/core/src/num/int_bits.rs rename to library/core/src/num/imp/int_bits.rs index 7e5459192235..2bc95e66e7ef 100644 --- a/library/core/src/num/int_bits.rs +++ b/library/core/src/num/imp/int_bits.rs @@ -64,7 +64,7 @@ macro_rules! uint_impl { ($U:ident) => { - pub(super) mod $U { + pub(in crate::num) mod $U { const STAGES: usize = $U::BITS.ilog2() as usize; #[inline] const fn prepare(sparse: $U) -> [$U; STAGES] { @@ -100,7 +100,7 @@ pub(super) mod $U { } #[inline(always)] - pub(in super::super) const fn extract_impl(mut x: $U, sparse: $U) -> $U { + pub(in crate::num) const fn extract_impl(mut x: $U, sparse: $U) -> $U { let masks = prepare(sparse); x &= sparse; let mut stage = 0; @@ -131,7 +131,7 @@ pub(in super::super) const fn extract_impl(mut x: $U, sparse: $U) -> $U { x } #[inline(always)] - pub(in super::super) const fn deposit_impl(mut x: $U, sparse: $U) -> $U { + pub(in crate::num) const fn deposit_impl(mut x: $U, sparse: $U) -> $U { let masks = prepare(sparse); let mut stage = STAGES; while stage > 0 { diff --git a/library/core/src/num/int_log10.rs b/library/core/src/num/imp/int_log10.rs similarity index 94% rename from library/core/src/num/int_log10.rs rename to library/core/src/num/imp/int_log10.rs index af8e1f90968d..1da029c6a21c 100644 --- a/library/core/src/num/int_log10.rs +++ b/library/core/src/num/imp/int_log10.rs @@ -96,7 +96,7 @@ const fn u128_impl(mut val: u128) -> u32 { macro_rules! define_unsigned_ilog10 { ($($ty:ident => $impl_fn:ident,)*) => {$( #[inline] - pub(super) const fn $ty(val: NonZero<$ty>) -> u32 { + pub(in crate::num) const fn $ty(val: NonZero<$ty>) -> u32 { let result = $impl_fn(val.get()); // SAFETY: Integer logarithm is monotonic non-decreasing, so the computed `result` cannot @@ -117,7 +117,7 @@ pub(super) const fn $ty(val: NonZero<$ty>) -> u32 { } #[inline] -pub(super) const fn usize(val: NonZero) -> u32 { +pub(in crate::num) const fn usize(val: NonZero) -> u32 { #[cfg(target_pointer_width = "16")] let impl_fn = u16; @@ -136,7 +136,7 @@ macro_rules! define_signed_ilog10 { ($($ty:ident => $impl_fn:ident,)*) => {$( // 0 < val <= $ty::MAX #[inline] - pub(super) const fn $ty(val: $ty) -> Option { + pub(in crate::num) const fn $ty(val: $ty) -> Option { if val > 0 { let result = $impl_fn(val.cast_unsigned()); @@ -166,6 +166,6 @@ pub(super) const fn $ty(val: $ty) -> Option { /// on every single primitive type. #[cold] #[track_caller] -pub(super) const fn panic_for_nonpositive_argument() -> ! { +pub(in crate::num) const fn panic_for_nonpositive_argument() -> ! { panic!("argument of integer logarithm must be positive") } diff --git a/library/core/src/num/int_sqrt.rs b/library/core/src/num/imp/int_sqrt.rs similarity index 97% rename from library/core/src/num/int_sqrt.rs rename to library/core/src/num/imp/int_sqrt.rs index c7a322c08c13..152841793d56 100644 --- a/library/core/src/num/int_sqrt.rs +++ b/library/core/src/num/imp/int_sqrt.rs @@ -37,7 +37,7 @@ #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] -pub(super) const fn u8(n: u8) -> u8 { +pub(in crate::num) const fn u8(n: u8) -> u8 { U8_ISQRT_WITH_REMAINDER[n as usize].0 } @@ -58,7 +58,7 @@ macro_rules! signed_fn { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub(super) const unsafe fn $SignedT(n: $SignedT) -> $SignedT { + pub(in crate::num) const unsafe fn $SignedT(n: $SignedT) -> $SignedT { debug_assert!(n >= 0, "Negative input inside `isqrt`."); $UnsignedT(n as $UnsignedT) as $SignedT } @@ -83,7 +83,7 @@ macro_rules! unsigned_fn { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub(super) const fn $UnsignedT(mut n: $UnsignedT) -> $UnsignedT { + pub(in crate::num) const fn $UnsignedT(mut n: $UnsignedT) -> $UnsignedT { if n <= <$HalfBitsT>::MAX as $UnsignedT { $HalfBitsT(n as $HalfBitsT) as $UnsignedT } else { @@ -311,6 +311,6 @@ const fn u128_stages(n: u128) -> u128 { /// on every single primitive type. #[cold] #[track_caller] -pub(super) const fn panic_for_negative_argument() -> ! { +pub(in crate::num) const fn panic_for_negative_argument() -> ! { panic!("argument of integer square root cannot be negative") } diff --git a/library/core/src/num/libm.rs b/library/core/src/num/imp/libm.rs similarity index 100% rename from library/core/src/num/libm.rs rename to library/core/src/num/imp/libm.rs diff --git a/library/core/src/num/imp/mod.rs b/library/core/src/num/imp/mod.rs new file mode 100644 index 000000000000..d35409f91bde --- /dev/null +++ b/library/core/src/num/imp/mod.rs @@ -0,0 +1,18 @@ +//! Numeric routines, separate from API. + +// These modules are public only for testing. +#[cfg(not(no_fp_fmt_parse))] +pub mod bignum; +#[cfg(not(no_fp_fmt_parse))] +pub mod dec2flt; +#[cfg(not(no_fp_fmt_parse))] +pub mod diy_float; +#[cfg(not(no_fp_fmt_parse))] +pub mod flt2dec; +pub mod fmt; + +pub(crate) mod int_bits; +pub(crate) mod int_log10; +pub(crate) mod int_sqrt; +pub(crate) mod libm; +pub(crate) mod overflow_panic; diff --git a/library/core/src/num/overflow_panic.rs b/library/core/src/num/imp/overflow_panic.rs similarity index 68% rename from library/core/src/num/overflow_panic.rs rename to library/core/src/num/imp/overflow_panic.rs index e30573dd3f39..a9b91daba954 100644 --- a/library/core/src/num/overflow_panic.rs +++ b/library/core/src/num/imp/overflow_panic.rs @@ -4,48 +4,48 @@ #[cold] #[track_caller] -pub(super) const fn add() -> ! { +pub(in crate::num) const fn add() -> ! { panic!("attempt to add with overflow") } #[cold] #[track_caller] -pub(super) const fn sub() -> ! { +pub(in crate::num) const fn sub() -> ! { panic!("attempt to subtract with overflow") } #[cold] #[track_caller] -pub(super) const fn mul() -> ! { +pub(in crate::num) const fn mul() -> ! { panic!("attempt to multiply with overflow") } #[cold] #[track_caller] -pub(super) const fn div() -> ! { +pub(in crate::num) const fn div() -> ! { panic!("attempt to divide with overflow") } #[cold] #[track_caller] -pub(super) const fn rem() -> ! { +pub(in crate::num) const fn rem() -> ! { panic!("attempt to calculate the remainder with overflow") } #[cold] #[track_caller] -pub(super) const fn neg() -> ! { +pub(in crate::num) const fn neg() -> ! { panic!("attempt to negate with overflow") } #[cold] #[track_caller] -pub(super) const fn shr() -> ! { +pub(in crate::num) const fn shr() -> ! { panic!("attempt to shift right with overflow") } #[cold] #[track_caller] -pub(super) const fn shl() -> ! { +pub(in crate::num) const fn shl() -> ! { panic!("attempt to shift left with overflow") } diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index b21865a9ae54..aba0eaf8d484 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -553,7 +553,7 @@ pub const fn checked_add(self, rhs: Self) -> Option { #[track_caller] pub const fn strict_add(self, rhs: Self) -> Self { let (a, b) = self.overflowing_add(rhs); - if b { overflow_panic::add() } else { a } + if b { imp::overflow_panic::add() } else { a } } /// Unchecked integer addition. Computes `self + rhs`, assuming overflow @@ -643,7 +643,7 @@ pub const fn checked_add_unsigned(self, rhs: $UnsignedT) -> Option { #[track_caller] pub const fn strict_add_unsigned(self, rhs: $UnsignedT) -> Self { let (a, b) = self.overflowing_add_unsigned(rhs); - if b { overflow_panic::add() } else { a } + if b { imp::overflow_panic::add() } else { a } } /// Checked integer subtraction. Computes `self - rhs`, returning `None` if @@ -693,7 +693,7 @@ pub const fn checked_sub(self, rhs: Self) -> Option { #[track_caller] pub const fn strict_sub(self, rhs: Self) -> Self { let (a, b) = self.overflowing_sub(rhs); - if b { overflow_panic::sub() } else { a } + if b { imp::overflow_panic::sub() } else { a } } /// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow @@ -783,7 +783,7 @@ pub const fn checked_sub_unsigned(self, rhs: $UnsignedT) -> Option { #[track_caller] pub const fn strict_sub_unsigned(self, rhs: $UnsignedT) -> Self { let (a, b) = self.overflowing_sub_unsigned(rhs); - if b { overflow_panic::sub() } else { a } + if b { imp::overflow_panic::sub() } else { a } } /// Checked integer multiplication. Computes `self * rhs`, returning `None` if @@ -833,7 +833,7 @@ pub const fn checked_mul(self, rhs: Self) -> Option { #[track_caller] pub const fn strict_mul(self, rhs: Self) -> Self { let (a, b) = self.overflowing_mul(rhs); - if b { overflow_panic::mul() } else { a } + if b { imp::overflow_panic::mul() } else { a } } /// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow @@ -940,7 +940,7 @@ pub const fn checked_div(self, rhs: Self) -> Option { #[track_caller] pub const fn strict_div(self, rhs: Self) -> Self { let (a, b) = self.overflowing_div(rhs); - if b { overflow_panic::div() } else { a } + if b { imp::overflow_panic::div() } else { a } } /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, @@ -1007,7 +1007,7 @@ pub const fn checked_div_euclid(self, rhs: Self) -> Option { #[track_caller] pub const fn strict_div_euclid(self, rhs: Self) -> Self { let (a, b) = self.overflowing_div_euclid(rhs); - if b { overflow_panic::div() } else { a } + if b { imp::overflow_panic::div() } else { a } } /// Checked integer division without remainder. Computes `self / rhs`, @@ -1179,7 +1179,7 @@ pub const fn checked_rem(self, rhs: Self) -> Option { #[track_caller] pub const fn strict_rem(self, rhs: Self) -> Self { let (a, b) = self.overflowing_rem(rhs); - if b { overflow_panic::rem() } else { a } + if b { imp::overflow_panic::rem() } else { a } } /// Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None` @@ -1245,7 +1245,7 @@ pub const fn checked_rem_euclid(self, rhs: Self) -> Option { #[track_caller] pub const fn strict_rem_euclid(self, rhs: Self) -> Self { let (a, b) = self.overflowing_rem_euclid(rhs); - if b { overflow_panic::rem() } else { a } + if b { imp::overflow_panic::rem() } else { a } } /// Checked negation. Computes `-self`, returning `None` if `self == MIN`. @@ -1323,7 +1323,7 @@ pub const fn checked_neg(self) -> Option { #[track_caller] pub const fn strict_neg(self) -> Self { let (a, b) = self.overflowing_neg(); - if b { overflow_panic::neg() } else { a } + if b { imp::overflow_panic::neg() } else { a } } /// Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is larger @@ -1379,7 +1379,7 @@ pub const fn checked_shl(self, rhs: u32) -> Option { #[track_caller] pub const fn strict_shl(self, rhs: u32) -> Self { let (a, b) = self.overflowing_shl(rhs); - if b { overflow_panic::shl() } else { a } + if b { imp::overflow_panic::shl() } else { a } } /// Unchecked shift left. Computes `self << rhs`, assuming that @@ -1558,7 +1558,7 @@ pub const fn checked_shr(self, rhs: u32) -> Option { #[track_caller] pub const fn strict_shr(self, rhs: u32) -> Self { let (a, b) = self.overflowing_shr(rhs); - if b { overflow_panic::shr() } else { a } + if b { imp::overflow_panic::shr() } else { a } } /// Unchecked shift right. Computes `self >> rhs`, assuming that @@ -1847,7 +1847,7 @@ pub const fn checked_isqrt(self) -> Option { } else { // SAFETY: Input is nonnegative in this `else` branch. let result = unsafe { - crate::num::int_sqrt::$ActualT(self as $ActualT) as $SelfT + imp::int_sqrt::$ActualT(self as $ActualT) as $SelfT }; // Inform the optimizer what the range of outputs is. If @@ -1864,7 +1864,7 @@ pub const fn checked_isqrt(self) -> Option { unsafe { // SAFETY: `<$ActualT>::MAX` is nonnegative. const MAX_RESULT: $SelfT = unsafe { - crate::num::int_sqrt::$ActualT(<$ActualT>::MAX) as $SelfT + imp::int_sqrt::$ActualT(<$ActualT>::MAX) as $SelfT }; crate::hint::assert_unchecked(result >= 0); @@ -2481,7 +2481,8 @@ pub const fn wrapping_pow(self, mut exp: u32) -> Self { /// /// Returns a tuple of the addition along with a boolean indicating /// whether an arithmetic overflow would occur. If an overflow would have - /// occurred then the wrapped value is returned. + /// occurred then the wrapped value is returned (negative if overflowed + /// above [`MAX`](Self::MAX), non-negative if below [`MIN`](Self::MIN)). /// /// # Examples /// @@ -2516,6 +2517,9 @@ pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { /// The output boolean returned by this method is *not* a carry flag, /// and should *not* be added to a more significant word. /// + /// If overflow occurred, the wrapped value is returned (negative if overflowed + /// above [`MAX`](Self::MAX), non-negative if below [`MIN`](Self::MIN)). + /// /// If the input carry is false, this method is equivalent to /// [`overflowing_add`](Self::overflowing_add). /// @@ -2583,7 +2587,8 @@ pub const fn overflowing_add_unsigned(self, rhs: $UnsignedT) -> (Self, bool) { /// Calculates `self` - `rhs`. /// /// Returns a tuple of the subtraction along with a boolean indicating whether an arithmetic overflow - /// would occur. If an overflow would have occurred then the wrapped value is returned. + /// would occur. If an overflow would have occurred then the wrapped value is returned + /// (negative if overflowed above [`MAX`](Self::MAX), non-negative if below [`MIN`](Self::MIN)). /// /// # Examples /// @@ -2619,6 +2624,9 @@ pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { /// The output boolean returned by this method is *not* a borrow flag, /// and should *not* be subtracted from a more significant word. /// + /// If overflow occurred, the wrapped value is returned (negative if overflowed + /// above [`MAX`](Self::MAX), non-negative if below [`MIN`](Self::MIN)). + /// /// If the input borrow is false, this method is equivalent to /// [`overflowing_sub`](Self::overflowing_sub). /// @@ -3134,7 +3142,7 @@ pub const fn pow(self, mut exp: u32) -> Self { pub const fn isqrt(self) -> Self { match self.checked_isqrt() { Some(sqrt) => sqrt, - None => crate::num::int_sqrt::panic_for_negative_argument(), + None => imp::int_sqrt::panic_for_negative_argument(), } } @@ -3440,7 +3448,7 @@ pub const fn ilog(self, base: Self) -> u32 { if let Some(log) = self.checked_ilog(base) { log } else { - int_log10::panic_for_nonpositive_argument() + imp::int_log10::panic_for_nonpositive_argument() } } @@ -3465,7 +3473,7 @@ pub const fn ilog2(self) -> u32 { if let Some(log) = self.checked_ilog2() { log } else { - int_log10::panic_for_nonpositive_argument() + imp::int_log10::panic_for_nonpositive_argument() } } @@ -3490,7 +3498,7 @@ pub const fn ilog10(self) -> u32 { if let Some(log) = self.checked_ilog10() { log } else { - int_log10::panic_for_nonpositive_argument() + imp::int_log10::panic_for_nonpositive_argument() } } @@ -3562,7 +3570,7 @@ pub const fn checked_ilog2(self) -> Option { without modifying the original"] #[inline] pub const fn checked_ilog10(self) -> Option { - int_log10::$ActualT(self as $ActualT) + imp::int_log10::$ActualT(self as $ActualT) } /// Computes the absolute value of `self`. diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 839a6fbdc9b7..333e44649d8f 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -27,16 +27,14 @@ macro_rules! sign_dependent_expr { }; } -// All these modules are technically private and only exposed for coretests: -#[cfg(not(no_fp_fmt_parse))] -pub mod bignum; -#[cfg(not(no_fp_fmt_parse))] -pub mod dec2flt; -#[cfg(not(no_fp_fmt_parse))] -pub mod diy_float; -#[cfg(not(no_fp_fmt_parse))] -pub mod flt2dec; -pub mod fmt; +// These modules are public only for testing. +#[doc(hidden)] +#[unstable( + feature = "num_internals", + reason = "internal routines only exposed for testing", + issue = "none" +)] +pub mod imp; #[macro_use] mod int_macros; // import int_impl! @@ -44,12 +42,9 @@ macro_rules! sign_dependent_expr { mod uint_macros; // import uint_impl! mod error; -mod int_bits; -mod int_log10; -mod int_sqrt; -pub(crate) mod libm; +#[cfg(not(no_fp_fmt_parse))] +mod float_parse; mod nonzero; -mod overflow_panic; mod saturating; mod wrapping; @@ -57,15 +52,15 @@ macro_rules! sign_dependent_expr { #[doc(hidden)] pub mod niche_types; -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg(not(no_fp_fmt_parse))] -pub use dec2flt::ParseFloatError; #[stable(feature = "int_error_matching", since = "1.55.0")] pub use error::IntErrorKind; #[stable(feature = "rust1", since = "1.0.0")] pub use error::ParseIntError; #[stable(feature = "try_from", since = "1.34.0")] pub use error::TryFromIntError; +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(no_fp_fmt_parse))] +pub use float_parse::ParseFloatError; #[stable(feature = "generic_nonzero", since = "1.79.0")] pub use nonzero::NonZero; #[unstable( diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index f52438e4e62e..3415e1d435ce 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -5,6 +5,7 @@ use crate::cmp::Ordering; use crate::hash::{Hash, Hasher}; use crate::marker::{Destruct, Freeze, StructuralPartialEq}; +use crate::num::imp; use crate::ops::{BitOr, BitOrAssign, Div, DivAssign, Neg, Rem, RemAssign}; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::str::FromStr; @@ -1817,7 +1818,7 @@ pub const fn ilog2(self) -> u32 { without modifying the original"] #[inline] pub const fn ilog10(self) -> u32 { - super::int_log10::$Int(self) + imp::int_log10::$Int(self) } /// Calculates the midpoint (average) between `self` and `rhs`. diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index cf79635dcd87..ae8324c13f0c 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -17,8 +17,8 @@ macro_rules! uint_impl { fsh_op = $fsh_op:literal, fshl_result = $fshl_result:literal, fshr_result = $fshr_result:literal, - clmul_lhs = $clmul_rhs:literal, - clmul_rhs = $clmul_lhs:literal, + clmul_lhs = $clmul_lhs:literal, + clmul_rhs = $clmul_rhs:literal, clmul_result = $clmul_result:literal, swap_op = $swap_op:literal, swapped = $swapped:literal, @@ -487,8 +487,8 @@ pub const fn funnel_shr(self, rhs: Self, n: u32) -> Self { /// Performs a carry-less multiplication, returning the lower bits. /// - /// This operation is similar to long multiplication, except that exclusive or is used - /// instead of addition. The implementation is equivalent to: + /// This operation is similar to long multiplication in base 2, except that exclusive or is + /// used instead of addition. The implementation is equivalent to: /// /// ```no_run #[doc = concat!("pub fn carryless_mul(lhs: ", stringify!($SelfT), ", rhs: ", stringify!($SelfT), ") -> ", stringify!($SelfT), "{")] @@ -574,7 +574,7 @@ pub const fn swap_bytes(self) -> Self { without modifying the original"] #[inline] pub const fn extract_bits(self, mask: Self) -> Self { - crate::num::int_bits::$ActualT::extract_impl(self as $ActualT, mask as $ActualT) as $SelfT + imp::int_bits::$ActualT::extract_impl(self as $ActualT, mask as $ActualT) as $SelfT } /// Returns an integer with the least significant bits of `self` @@ -591,7 +591,7 @@ pub const fn extract_bits(self, mask: Self) -> Self { without modifying the original"] #[inline] pub const fn deposit_bits(self, mask: Self) -> Self { - crate::num::int_bits::$ActualT::deposit_impl(self as $ActualT, mask as $ActualT) as $SelfT + imp::int_bits::$ActualT::deposit_impl(self as $ActualT, mask as $ActualT) as $SelfT } /// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, @@ -802,7 +802,7 @@ pub const fn checked_add(self, rhs: Self) -> Option { #[track_caller] pub const fn strict_add(self, rhs: Self) -> Self { let (a, b) = self.overflowing_add(rhs); - if b { overflow_panic::add() } else { a } + if b { imp::overflow_panic::add() } else { a } } /// Unchecked integer addition. Computes `self + rhs`, assuming overflow @@ -897,7 +897,7 @@ pub const fn checked_add_signed(self, rhs: $SignedT) -> Option { #[track_caller] pub const fn strict_add_signed(self, rhs: $SignedT) -> Self { let (a, b) = self.overflowing_add_signed(rhs); - if b { overflow_panic::add() } else { a } + if b { imp::overflow_panic::add() } else { a } } /// Checked integer subtraction. Computes `self - rhs`, returning @@ -956,7 +956,7 @@ pub const fn checked_sub(self, rhs: Self) -> Option { #[track_caller] pub const fn strict_sub(self, rhs: Self) -> Self { let (a, b) = self.overflowing_sub(rhs); - if b { overflow_panic::sub() } else { a } + if b { imp::overflow_panic::sub() } else { a } } /// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow @@ -1081,7 +1081,7 @@ pub const fn checked_sub_signed(self, rhs: $SignedT) -> Option { #[track_caller] pub const fn strict_sub_signed(self, rhs: $SignedT) -> Self { let (a, b) = self.overflowing_sub_signed(rhs); - if b { overflow_panic::sub() } else { a } + if b { imp::overflow_panic::sub() } else { a } } #[doc = concat!( @@ -1190,7 +1190,7 @@ pub const fn checked_mul(self, rhs: Self) -> Option { #[track_caller] pub const fn strict_mul(self, rhs: Self) -> Self { let (a, b) = self.overflowing_mul(rhs); - if b { overflow_panic::mul() } else { a } + if b { imp::overflow_panic::mul() } else { a } } /// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow @@ -1615,7 +1615,7 @@ pub const fn ilog(self, base: Self) -> u32 { if let Some(log) = self.checked_ilog(base) { log } else { - int_log10::panic_for_nonpositive_argument() + imp::int_log10::panic_for_nonpositive_argument() } } @@ -1640,7 +1640,7 @@ pub const fn ilog2(self) -> u32 { if let Some(log) = self.checked_ilog2() { log } else { - int_log10::panic_for_nonpositive_argument() + imp::int_log10::panic_for_nonpositive_argument() } } @@ -1665,7 +1665,7 @@ pub const fn ilog10(self) -> u32 { if let Some(log) = self.checked_ilog10() { log } else { - int_log10::panic_for_nonpositive_argument() + imp::int_log10::panic_for_nonpositive_argument() } } @@ -1827,7 +1827,7 @@ pub const fn checked_neg(self) -> Option { #[track_caller] pub const fn strict_neg(self) -> Self { let (a, b) = self.overflowing_neg(); - if b { overflow_panic::neg() } else { a } + if b { imp::overflow_panic::neg() } else { a } } /// Checked shift left. Computes `self << rhs`, returning `None` @@ -1883,7 +1883,7 @@ pub const fn checked_shl(self, rhs: u32) -> Option { #[track_caller] pub const fn strict_shl(self, rhs: u32) -> Self { let (a, b) = self.overflowing_shl(rhs); - if b { overflow_panic::shl() } else { a } + if b { imp::overflow_panic::shl() } else { a } } /// Unchecked shift left. Computes `self << rhs`, assuming that @@ -2068,7 +2068,7 @@ pub const fn checked_shr(self, rhs: u32) -> Option { #[track_caller] pub const fn strict_shr(self, rhs: u32) -> Self { let (a, b) = self.overflowing_shr(rhs); - if b { overflow_panic::shr() } else { a } + if b { imp::overflow_panic::shr() } else { a } } /// Unchecked shift right. Computes `self >> rhs`, assuming that @@ -3541,7 +3541,7 @@ pub const fn pow(self, mut exp: u32) -> Self { without modifying the original"] #[inline] pub const fn isqrt(self) -> Self { - let result = crate::num::int_sqrt::$ActualT(self as $ActualT) as $SelfT; + let result = imp::int_sqrt::$ActualT(self as $ActualT) as $SelfT; // Inform the optimizer what the range of outputs is. If testing // `core` crashes with no panic message and a `num::int_sqrt::u*` @@ -3554,7 +3554,7 @@ pub const fn isqrt(self) -> Self { // integers is bounded by `[0, <$ActualT>::MAX]`, sqrt(n) will be // bounded by `[sqrt(0), sqrt(<$ActualT>::MAX)]`. unsafe { - const MAX_RESULT: $SelfT = crate::num::int_sqrt::$ActualT(<$ActualT>::MAX) as $SelfT; + const MAX_RESULT: $SelfT = imp::int_sqrt::$ActualT(<$ActualT>::MAX) as $SelfT; crate::hint::assert_unchecked(result <= MAX_RESULT); } diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index 84fc98cf73f1..b15712d3599c 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -151,7 +151,7 @@ impl ControlFlow { /// ``` #[inline] #[stable(feature = "control_flow_enum_is", since = "1.59.0")] - #[rustc_const_stable(feature = "min_const_control_flow", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "min_const_control_flow", since = "1.95.0")] pub const fn is_break(&self) -> bool { matches!(*self, ControlFlow::Break(_)) } @@ -168,7 +168,7 @@ pub const fn is_break(&self) -> bool { /// ``` #[inline] #[stable(feature = "control_flow_enum_is", since = "1.59.0")] - #[rustc_const_stable(feature = "min_const_control_flow", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "min_const_control_flow", since = "1.95.0")] pub const fn is_continue(&self) -> bool { matches!(*self, ControlFlow::Continue(_)) } @@ -197,14 +197,12 @@ pub const fn break_value(self) -> Option } } - /// Converts the `ControlFlow` into an `Result` which is `Ok` if the + /// Converts the `ControlFlow` into a `Result` which is `Ok` if the /// `ControlFlow` was `Break` and `Err` if otherwise. /// /// # Examples /// /// ``` - /// #![feature(control_flow_ok)] - /// /// use std::ops::ControlFlow; /// /// struct TreeNode { @@ -263,8 +261,9 @@ pub const fn break_value(self) -> Option /// assert_eq!(res, Ok(&5)); /// ``` #[inline] - #[unstable(feature = "control_flow_ok", issue = "140266")] - #[rustc_const_unstable(feature = "control_flow_ok", issue = "140266")] + #[stable(feature = "control_flow_ok", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "control_flow_ok", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] pub const fn break_ok(self) -> Result { match self { ControlFlow::Continue(c) => Err(c), @@ -311,14 +310,12 @@ pub const fn continue_value(self) -> Option } } - /// Converts the `ControlFlow` into an `Result` which is `Ok` if the + /// Converts the `ControlFlow` into a `Result` which is `Ok` if the /// `ControlFlow` was `Continue` and `Err` if otherwise. /// /// # Examples /// /// ``` - /// #![feature(control_flow_ok)] - /// /// use std::ops::ControlFlow; /// /// struct TreeNode { @@ -376,8 +373,9 @@ pub const fn continue_value(self) -> Option /// assert_eq!(res, Err("too big value detected")); /// ``` #[inline] - #[unstable(feature = "control_flow_ok", issue = "140266")] - #[rustc_const_unstable(feature = "control_flow_ok", issue = "140266")] + #[stable(feature = "control_flow_ok", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "control_flow_ok", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] pub const fn continue_ok(self) -> Result { match self { ControlFlow::Continue(c) => Ok(c), @@ -422,9 +420,9 @@ pub const fn into_value(self) -> T { } } -/// These are used only as part of implementing the iterator adapters. -/// They have mediocre names and non-obvious semantics, so aren't -/// currently on a path to potential stabilization. +// These are used only as part of implementing the iterator adapters. +// They have mediocre names and non-obvious semantics, so aren't +// currently on a path to potential stabilization. impl ControlFlow { /// Creates a `ControlFlow` from any type implementing `Try`. #[inline] diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index 354be271ff13..f2eb047d342b 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -80,7 +80,7 @@ mod panic {} #[doc(no_inline)] pub use self::ambiguous_macros_only::{env, panic}; -#[unstable(feature = "cfg_select", issue = "115585")] +#[stable(feature = "cfg_select", since = "1.95.0")] #[doc(no_inline)] pub use crate::cfg_select; diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 75777144dfb8..8b7b08bf8231 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -290,8 +290,8 @@ pub const fn to_raw_parts(self) -> (*const (), ::Metadata) /// assert_eq!(ptr.as_ref_unchecked(), &10); /// } /// ``` - #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ptr_as_ref_unchecked", since = "1.95.0")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "1.95.0")] #[inline] #[must_use] pub const unsafe fn as_ref_unchecked<'a>(self) -> &'a T { diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index ad74a8628c61..48e1e206a313 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -15,22 +15,19 @@ //! The precise rules for validity are not determined yet. The guarantees that are //! provided at this point are very minimal: //! -//! * For memory accesses of [size zero][zst], *every* pointer is valid, including the [null] -//! pointer. The following points are only concerned with non-zero-sized accesses. -//! * A [null] pointer is *never* valid. -//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer be -//! *dereferenceable*. The [provenance] of the pointer is used to determine which [allocation] -//! it is derived from; a pointer is dereferenceable if the memory range of the given size -//! starting at the pointer is entirely contained within the bounds of that allocation. Note +//! * A [null] pointer is *never* valid for reads/writes. +//! * For memory accesses of [size zero][zst], *every* non-null pointer is valid for reads/writes. +//! The following points are only concerned with non-zero-sized accesses. +//! * For a pointer to be valid for reads/writes, it is necessary, but not always sufficient, that +//! the pointer be *dereferenceable*. The [provenance] of the pointer is used to determine which +//! [allocation] it is derived from; a pointer is dereferenceable if the memory range of the given +//! size starting at the pointer is entirely contained within the bounds of that allocation. Note //! that in Rust, every (stack-allocated) variable is considered a separate allocation. //! * All accesses performed by functions in this module are *non-atomic* in the sense //! of [atomic operations] used to synchronize between threads. This means it is //! undefined behavior to perform two concurrent accesses to the same location from different -//! threads unless both accesses only read from memory. Notice that this explicitly -//! includes [`read_volatile`] and [`write_volatile`]: Volatile accesses cannot -//! be used for inter-thread synchronization, regardless of whether they are acting on -//! Rust memory or not. -//! * The result of casting a reference to a pointer is valid for as long as the +//! threads unless both accesses only read from memory. +//! * The result of casting a reference to a pointer is valid for reads/writes for as long as the //! underlying allocation is live and no reference (just raw pointers) is used to //! access the same memory. That is, reference and pointer accesses cannot be //! interleaved. @@ -41,6 +38,13 @@ //! information, see the [book] as well as the section in the reference devoted //! to [undefined behavior][ub]. //! +//! Note that some operations such as [`read`] and [`write`][`write()`] do allow null pointers if +//! the total size of the access is zero. However, other operations internally convert pointers into +//! references. Therefore, the general notion of "valid for reads/writes" excludes null pointers, +//! and the specific operations that permit null pointers mention that as an exception. Furthermore, +//! [`read_volatile`] and [`write_volatile`] can be used in even more situations; see their +//! documentation for details. +//! //! We say that a pointer is "dangling" if it is not valid for any non-zero-sized accesses. This //! means out-of-bounds pointers, pointers to freed memory, null pointers, and pointers created with //! [`NonNull::dangling`] are all dangling. @@ -450,9 +454,9 @@ /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `src` must be [valid] for reads of `count * size_of::()` bytes. +/// * `src` must be [valid] for reads of `count * size_of::()` bytes or that number must be 0. /// -/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. +/// * `dst` must be [valid] for writes of `count * size_of::()` bytes or that number must be 0. /// /// * Both `src` and `dst` must be properly aligned. /// @@ -568,11 +572,11 @@ /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `src` must be [valid] for reads of `count * size_of::()` bytes. +/// * `src` must be [valid] for reads of `count * size_of::()` bytes or that number must be 0. /// -/// * `dst` must be [valid] for writes of `count * size_of::()` bytes, and must remain valid even -/// when `src` is read for `count * size_of::()` bytes. (This means if the memory ranges -/// overlap, the `dst` pointer must not be invalidated by `src` reads.) +/// * `dst` must be [valid] for writes of `count * size_of::()` bytes or that number must be 0, +/// and `dst` must remain valid even when `src` is read for `count * size_of::()` bytes. (This +/// means if the memory ranges overlap, the `dst` pointer must not be invalidated by `src` reads.) /// /// * Both `src` and `dst` must be properly aligned. /// @@ -1508,7 +1512,7 @@ macro_rules! swap_prefix { /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `dst` must be [valid] for both reads and writes. +/// * `dst` must be [valid] for both reads and writes or `T` must be a ZST. /// /// * `dst` must be properly aligned. /// @@ -1543,7 +1547,7 @@ macro_rules! swap_prefix { // SAFETY: the caller must guarantee that `dst` is valid to be // cast to a mutable reference (valid for writes, aligned, initialized), // and cannot overlap `src` since `dst` must point to a distinct - // allocation. + // allocation. We are excluding null (with a ZST check) before creating a reference. unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, @@ -1554,6 +1558,12 @@ macro_rules! swap_prefix { is_zst: bool = T::IS_ZST, ) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst) ); + if T::IS_ZST { + // If `T` is a ZST, `dst` is allowed to be null. However, we also don't have to actually + // do anything since there isn't actually any data to be copied anyway. All values of + // type `T` are bit-identical, so we can just return `src` here. + return src; + } mem::replace(&mut *dst, src) } } @@ -1565,7 +1575,7 @@ macro_rules! swap_prefix { /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `src` must be [valid] for reads. +/// * `src` must be [valid] for reads or `T` must be a ZST. /// /// * `src` must be properly aligned. Use [`read_unaligned`] if this is not the /// case. @@ -1817,7 +1827,7 @@ macro_rules! swap_prefix { /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `dst` must be [valid] for writes. +/// * `dst` must be [valid] for writes or `T` must be a ZST. /// /// * `dst` must be properly aligned. Use [`write_unaligned`] if this is not the /// case. @@ -2040,8 +2050,8 @@ macro_rules! swap_prefix { /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `src` must be either [valid] for reads, or it must point to memory outside of all Rust -/// allocations and reading from that memory must: +/// * `src` must be either [valid] for reads, or `T` must be a ZST, or `src` must point to memory +/// outside of all Rust allocations and reading from that memory must: /// - not trap, and /// - not cause any memory inside a Rust allocation to be modified. /// @@ -2128,8 +2138,8 @@ pub unsafe fn read_volatile(src: *const T) -> T { /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `dst` must be either [valid] for writes, or it must point to memory outside of all Rust -/// allocations and writing to that memory must: +/// * `dst` must be either [valid] for writes, or `T` must be a ZST, or `dst` must point to memory +/// outside of all Rust allocations and writing to that memory must: /// - not trap, and /// - not cause any memory inside a Rust allocation to be modified. /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index f19a5d02b98d..289dd972f679 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -288,8 +288,8 @@ pub const fn to_raw_parts(self) -> (*mut (), ::Metadata) { /// println!("We got back the value: {}!", ptr.as_ref_unchecked()); /// } /// ``` - #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ptr_as_ref_unchecked", since = "1.95.0")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "1.95.0")] #[inline] #[must_use] pub const unsafe fn as_ref_unchecked<'a>(self) -> &'a T { @@ -611,8 +611,8 @@ pub fn mask(self, mask: usize) -> *mut T { /// # assert_eq!(s, [4, 2, 3]); /// println!("{s:?}"); // It'll print: "[4, 2, 3]". /// ``` - #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ptr_as_ref_unchecked", since = "1.95.0")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "1.95.0")] #[inline] #[must_use] pub const unsafe fn as_mut_unchecked<'a>(self) -> &'a mut T { diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 7b9e638289bf..8be7d3a9ae92 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1,7 +1,7 @@ use crate::clone::TrivialClone; use crate::cmp::Ordering; use crate::marker::{Destruct, PointeeSized, Unsize}; -use crate::mem::{MaybeUninit, SizedTypeProperties}; +use crate::mem::{MaybeUninit, SizedTypeProperties, transmute}; use crate::num::NonZero; use crate::ops::{CoerceUnsized, DispatchFromDyn}; use crate::pin::PinCoerceUnsized; @@ -100,9 +100,8 @@ impl NonNull { #[must_use] #[inline] pub const fn without_provenance(addr: NonZero) -> Self { - let pointer = crate::ptr::without_provenance(addr.get()); - // SAFETY: we know `addr` is non-zero. - unsafe { NonNull { pointer } } + // SAFETY: we know `addr` is non-zero and all nonzero integers are valid raw pointers. + unsafe { transmute(addr) } } /// Creates a new `NonNull` that is dangling, but well-aligned. @@ -239,7 +238,7 @@ impl NonNull { "NonNull::new_unchecked requires that the pointer is non-null", (ptr: *mut () = ptr as *mut ()) => !ptr.is_null() ); - NonNull { pointer: ptr as _ } + transmute(ptr) } } @@ -282,7 +281,7 @@ pub const fn new(ptr: *mut T) -> Option { #[inline] pub const fn from_ref(r: &T) -> Self { // SAFETY: A reference cannot be null. - unsafe { NonNull { pointer: r as *const T } } + unsafe { transmute(r as *const T) } } /// Converts a mutable reference to a `NonNull` pointer. @@ -291,7 +290,7 @@ pub const fn from_ref(r: &T) -> Self { #[inline] pub const fn from_mut(r: &mut T) -> Self { // SAFETY: A mutable reference cannot be null. - unsafe { NonNull { pointer: r as *mut T } } + unsafe { transmute(r as *mut T) } } /// Performs the same functionality as [`std::ptr::from_raw_parts`], except that a @@ -502,7 +501,7 @@ pub const fn as_ptr(self) -> *mut T { #[inline] pub const fn cast(self) -> NonNull { // SAFETY: `self` is a `NonNull` pointer which is necessarily non-null - unsafe { NonNull { pointer: self.as_ptr() as *mut U } } + unsafe { transmute(self.as_ptr() as *mut U) } } /// Try to cast to a pointer of another type by checking alignment. @@ -581,7 +580,7 @@ pub fn try_cast_aligned(self) -> Option> { // Additionally safety contract of `offset` guarantees that the resulting pointer is // pointing to an allocation, there can't be an allocation at null, thus it's safe to // construct `NonNull`. - unsafe { NonNull { pointer: intrinsics::offset(self.as_ptr(), count) } } + unsafe { transmute(intrinsics::offset(self.as_ptr(), count)) } } /// Calculates the offset from a pointer in bytes. @@ -605,7 +604,7 @@ pub fn try_cast_aligned(self) -> Option> { // Additionally safety contract of `offset` guarantees that the resulting pointer is // pointing to an allocation, there can't be an allocation at null, thus it's safe to // construct `NonNull`. - unsafe { NonNull { pointer: self.as_ptr().byte_offset(count) } } + unsafe { transmute(self.as_ptr().byte_offset(count)) } } /// Adds an offset to a pointer (convenience for `.offset(count as isize)`). @@ -657,7 +656,7 @@ pub fn try_cast_aligned(self) -> Option> { // Additionally safety contract of `offset` guarantees that the resulting pointer is // pointing to an allocation, there can't be an allocation at null, thus it's safe to // construct `NonNull`. - unsafe { NonNull { pointer: intrinsics::offset(self.as_ptr(), count) } } + unsafe { transmute(intrinsics::offset(self.as_ptr(), count)) } } /// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`). @@ -681,7 +680,7 @@ pub fn try_cast_aligned(self) -> Option> { // Additionally safety contract of `add` guarantees that the resulting pointer is pointing // to an allocation, there can't be an allocation at null, thus it's safe to construct // `NonNull`. - unsafe { NonNull { pointer: self.as_ptr().byte_add(count) } } + unsafe { transmute(self.as_ptr().byte_add(count)) } } /// Subtracts an offset from a pointer (convenience for @@ -763,7 +762,7 @@ pub fn try_cast_aligned(self) -> Option> { // Additionally safety contract of `sub` guarantees that the resulting pointer is pointing // to an allocation, there can't be an allocation at null, thus it's safe to construct // `NonNull`. - unsafe { NonNull { pointer: self.as_ptr().byte_sub(count) } } + unsafe { transmute(self.as_ptr().byte_sub(count)) } } /// Calculates the distance between two pointers within the same allocation. The returned value is in diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 0ef0d192a868..d12a4ef43e49 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -25,7 +25,7 @@ pub mod legacy; #[doc(inline)] -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] pub use iter::RangeInclusiveIter; #[doc(inline)] #[unstable(feature = "new_range_api", issue = "125687")] @@ -43,7 +43,7 @@ // pub use crate::ops::{Bound, IntoBounds, OneSidedRange, RangeBounds, RangeFull, RangeTo}; use crate::iter::Step; use crate::ops::Bound::{self, Excluded, Included, Unbounded}; -use crate::ops::{IntoBounds, RangeBounds}; +use crate::ops::{IntoBounds, OneSidedRange, OneSidedRangeBound, RangeBounds}; /// A (half-open) range bounded inclusively below and exclusively above /// (`start..end` in a future edition). @@ -245,17 +245,17 @@ fn from(value: legacy::Range) -> Self { /// ``` #[lang = "RangeInclusiveCopy"] #[derive(Clone, Copy, PartialEq, Eq, Hash)] -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] pub struct RangeInclusive { /// The lower bound of the range (inclusive). - #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_range_inclusive_api", since = "1.95.0")] pub start: Idx, /// The upper bound of the range (inclusive). - #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_range_inclusive_api", since = "1.95.0")] pub last: Idx, } -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] impl fmt::Debug for RangeInclusive { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { self.start.fmt(fmt)?; @@ -288,7 +288,7 @@ impl> RangeInclusive { /// assert!(!RangeInclusive::from(f32::NAN..=1.0).contains(&1.0)); /// ``` #[inline] - #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_range_inclusive_api", since = "1.95.0")] #[rustc_const_unstable(feature = "const_range", issue = "none")] pub const fn contains(&self, item: &U) -> bool where @@ -319,7 +319,7 @@ pub const fn contains(&self, item: &U) -> bool /// assert!( RangeInclusive::from(3.0..=f32::NAN).is_empty()); /// assert!( RangeInclusive::from(f32::NAN..=5.0).is_empty()); /// ``` - #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_range_inclusive_api", since = "1.95.0")] #[inline] #[rustc_const_unstable(feature = "const_range", issue = "none")] pub const fn is_empty(&self) -> bool @@ -345,14 +345,14 @@ impl RangeInclusive { /// assert_eq!(i.next(), Some(16)); /// assert_eq!(i.next(), Some(25)); /// ``` - #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_range_inclusive_api", since = "1.95.0")] #[inline] pub fn iter(&self) -> RangeInclusiveIter { self.clone().into_iter() } } -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const RangeBounds for RangeInclusive { fn start_bound(&self) -> Bound<&T> { @@ -369,7 +369,7 @@ fn end_bound(&self) -> Bound<&T> { /// If you need to use this implementation where `T` is unsized, /// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], /// i.e. replace `start..=end` with `(Bound::Included(start), Bound::Included(end))`. -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const RangeBounds for RangeInclusive<&T> { fn start_bound(&self) -> Bound<&T> { @@ -380,7 +380,7 @@ fn end_bound(&self) -> Bound<&T> { } } -// #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +// #[stable(feature = "new_range_inclusive_api", since = "1.95.0")] #[unstable(feature = "range_into_bounds", issue = "136903")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const IntoBounds for RangeInclusive { @@ -389,7 +389,7 @@ fn into_bounds(self) -> (Bound, Bound) { } } -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From> for legacy::RangeInclusive { #[inline] @@ -397,7 +397,7 @@ fn from(value: RangeInclusive) -> Self { Self::new(value.start, value.last) } } -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From> for RangeInclusive { #[inline] @@ -546,6 +546,18 @@ fn into_bounds(self) -> (Bound, Bound) { } } +#[unstable(feature = "one_sided_range", issue = "69780")] +// #[unstable(feature = "new_range_api", issue = "125687")] +#[rustc_const_unstable(feature = "const_range", issue = "none")] +impl const OneSidedRange for RangeFrom +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::StartInclusive, self.start) + } +} + #[unstable(feature = "new_range_api", issue = "125687")] #[rustc_const_unstable(feature = "const_index", issue = "143775")] impl const From> for legacy::RangeFrom { @@ -573,9 +585,8 @@ fn from(value: legacy::RangeFrom) -> Self { /// The `..=last` syntax is a `RangeToInclusive`: /// /// ``` -/// #![feature(new_range_api)] /// #![feature(new_range)] -/// assert_eq!((..=5), std::range::RangeToInclusive{ last: 5 }); +/// assert_eq!((..=5), std::range::RangeToInclusive { last: 5 }); /// ``` /// /// It does not have an [`IntoIterator`] implementation, so you can't use it in a @@ -606,14 +617,14 @@ fn from(value: legacy::RangeFrom) -> Self { #[lang = "RangeToInclusiveCopy"] #[doc(alias = "..=")] #[derive(Copy, Clone, PartialEq, Eq, Hash)] -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] pub struct RangeToInclusive { /// The upper bound of the range (inclusive) - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] pub last: Idx, } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] impl fmt::Debug for RangeToInclusive { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "..=")?; @@ -637,7 +648,7 @@ impl> RangeToInclusive { /// assert!(!(..=f32::NAN).contains(&0.5)); /// ``` #[inline] - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_range", issue = "none")] pub const fn contains(&self, item: &U) -> bool where @@ -648,13 +659,13 @@ pub const fn contains(&self, item: &U) -> bool } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] impl From> for RangeToInclusive { fn from(value: legacy::RangeToInclusive) -> Self { Self { last: value.end } } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] impl From> for legacy::RangeToInclusive { fn from(value: RangeToInclusive) -> Self { Self { end: value.last } @@ -664,7 +675,7 @@ fn from(value: RangeToInclusive) -> Self { // RangeToInclusive cannot impl From> // because underflow would be possible with (..0).into() -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const RangeBounds for RangeToInclusive { fn start_bound(&self) -> Bound<&T> { @@ -675,6 +686,18 @@ fn end_bound(&self) -> Bound<&T> { } } +#[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_unstable(feature = "const_range", issue = "none")] +impl const RangeBounds for RangeToInclusive<&T> { + fn start_bound(&self) -> Bound<&T> { + Unbounded + } + fn end_bound(&self) -> Bound<&T> { + Included(self.last) + } +} + +// #[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[unstable(feature = "range_into_bounds", issue = "136903")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const IntoBounds for RangeToInclusive { @@ -682,3 +705,15 @@ fn into_bounds(self) -> (Bound, Bound) { (Unbounded, Included(self.last)) } } + +// #[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[unstable(feature = "one_sided_range", issue = "69780")] +#[rustc_const_unstable(feature = "const_range", issue = "none")] +impl const OneSidedRange for RangeToInclusive +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::EndInclusive, self.last) + } +} diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index e722b9fa33c5..c1d4fbbd23ad 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -153,7 +153,7 @@ fn into_iter(self) -> Self::IntoIter { } /// By-value [`RangeInclusive`] iterator. -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] #[derive(Debug, Clone)] pub struct RangeInclusiveIter(legacy::RangeInclusive); @@ -161,7 +161,7 @@ impl RangeInclusiveIter { /// Returns the remainder of the range being iterated over. /// /// If the iterator is exhausted or empty, returns `None`. - #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_range_inclusive_api", since = "1.95.0")] pub fn remainder(self) -> Option> { if self.0.is_empty() { return None; @@ -171,7 +171,7 @@ pub fn remainder(self) -> Option> { } } -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] impl Iterator for RangeInclusiveIter { type Item = A; @@ -227,7 +227,7 @@ fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { } } -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] impl DoubleEndedIterator for RangeInclusiveIter { #[inline] fn next_back(&mut self) -> Option { @@ -248,10 +248,10 @@ fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for RangeInclusiveIter {} -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] impl FusedIterator for RangeInclusiveIter {} -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] impl IntoIterator for RangeInclusive { type Item = A; type IntoIter = RangeInclusiveIter; @@ -278,7 +278,7 @@ impl ExactSizeIterator for RangeIter<$t> { } macro_rules! range_incl_exact_iter_impl { ($($t:ty)*) => ($( - #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_range_inclusive_api", since = "1.95.0")] impl ExactSizeIterator for RangeInclusiveIter<$t> { } )*) } diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 3a76c098bb53..1709bc7c0984 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -127,9 +127,9 @@ impl Sealed for (ops::Bound, ops::Bound) {} #[unstable(feature = "new_range_api", issue = "125687")] impl Sealed for range::Range {} - #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_range_inclusive_api", since = "1.95.0")] impl Sealed for range::RangeInclusive {} - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] impl Sealed for range::RangeToInclusive {} #[unstable(feature = "new_range_api", issue = "125687")] impl Sealed for range::RangeFrom {} @@ -723,7 +723,7 @@ fn index_mut(self, slice: &mut [T]) -> &mut [T] { } } -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] #[rustc_const_unstable(feature = "const_index", issue = "143775")] unsafe impl const SliceIndex<[T]> for range::RangeInclusive { type Output = [T]; @@ -801,7 +801,7 @@ fn index_mut(self, slice: &mut [T]) -> &mut [T] { } /// The methods `index` and `index_mut` panic if the end of the range is out of bounds. -#[stable(feature = "inclusive_range", since = "1.26.0")] +#[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_index", issue = "143775")] unsafe impl const SliceIndex<[T]> for range::RangeToInclusive { type Output = [T]; diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index d2985d8a1866..283aa9a6a73a 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -99,9 +99,6 @@ fn advance_by(&mut self, mut remainder: usize) -> Result<(), NonZero> { #[inline] fn size_hint(&self) -> (usize, Option) { let len = self.iter.len(); - // `(len + 3)` can't overflow, because we know that the `slice::Iter` - // belongs to a slice in memory which has a maximum length of - // `isize::MAX` (that's well below `usize::MAX`). (len.div_ceil(4), Some(len)) } @@ -1528,9 +1525,6 @@ fn size_hint(&self) -> (usize, Option) { // is therefore determined by assuming the remaining bytes contain as // many 3-byte sequences as possible. The highest bytes:code units // ratio is for 1-byte sequences, so use this for the upper bound. - // `(len + 2)` can't overflow, because we know that the `slice::Iter` - // belongs to a slice in memory which has a maximum length of - // `isize::MAX` (that's well below `usize::MAX`) if self.extra == 0 { (len.div_ceil(3), Some(len)) } else { diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index b54522fcc886..25202ffd6731 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -997,7 +997,8 @@ fn is_contained_in(self, haystack: &str) -> bool { #[cfg(any( all(target_arch = "x86_64", target_feature = "sse2"), - all(target_arch = "loongarch64", target_feature = "lsx") + all(target_arch = "loongarch64", target_feature = "lsx"), + all(target_arch = "aarch64", target_feature = "neon") ))] if self.len() <= 32 { if let Some(result) = simd_contains(self, haystack) { @@ -1782,7 +1783,8 @@ fn matching(a: usize, b: usize) -> Self::Output { /// [0]: http://0x80.pl/articles/simd-strfind.html#sse-avx2 #[cfg(any( all(target_arch = "x86_64", target_feature = "sse2"), - all(target_arch = "loongarch64", target_feature = "lsx") + all(target_arch = "loongarch64", target_feature = "lsx"), + all(target_arch = "aarch64", target_feature = "neon") ))] #[inline] fn simd_contains(needle: &str, haystack: &str) -> Option { @@ -1917,7 +1919,8 @@ fn simd_contains(needle: &str, haystack: &str) -> Option { /// Both slices must have the same length. #[cfg(any( all(target_arch = "x86_64", target_feature = "sse2"), - all(target_arch = "loongarch64", target_feature = "lsx") + all(target_arch = "loongarch64", target_feature = "lsx"), + all(target_arch = "aarch64", target_feature = "neon") ))] #[inline] unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool { diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index edf07c0c16f4..88a8a9764cbc 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -685,7 +685,7 @@ fn index_mut(self, slice: &mut str) -> &mut Self::Output { } } -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] #[rustc_const_unstable(feature = "const_index", issue = "143775")] unsafe impl const SliceIndex for range::RangeInclusive { type Output = str; @@ -763,6 +763,52 @@ fn index_mut(self, slice: &mut str) -> &mut Self::Output { } } +/// Implements substring slicing with syntax `&self[..= last]` or `&mut +/// self[..= last]`. +/// +/// Returns a slice of the given string from the byte range \[0, `last`\]. +/// Equivalent to `&self [0 .. last + 1]`, except if `last` has the maximum +/// value for `usize`. +/// +/// This operation is *O*(1). +/// +/// # Panics +/// +/// Panics if `last` does not point to the ending byte offset of a character +/// (`last + 1` is either a starting byte offset as defined by +/// `is_char_boundary`, or equal to `len`), or if `last >= len`. +#[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex for range::RangeToInclusive { + type Output = str; + #[inline] + fn get(self, slice: &str) -> Option<&Self::Output> { + (0..=self.last).get(slice) + } + #[inline] + fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { + (0..=self.last).get_mut(slice) + } + #[inline] + unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { + // SAFETY: the caller must uphold the safety contract for `get_unchecked`. + unsafe { (0..=self.last).get_unchecked(slice) } + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { + // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`. + unsafe { (0..=self.last).get_unchecked_mut(slice) } + } + #[inline] + fn index(self, slice: &str) -> &Self::Output { + (0..=self.last).index(slice) + } + #[inline] + fn index_mut(self, slice: &mut str) -> &mut Self::Output { + (0..=self.last).index_mut(slice) + } +} + /// Parse a value from a string /// /// `FromStr`'s [`from_str`] method is often used implicitly, through diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index adc2bbcde51b..0077b5b9d31b 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -238,7 +238,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![cfg_attr(not(target_has_atomic_load_store = "8"), allow(dead_code))] #![cfg_attr(not(target_has_atomic_load_store = "8"), allow(unused_imports))] -#![rustc_diagnostic_item = "atomic_mod"] // Clippy complains about the pattern of "safe function calling unsafe function taking pointers". // This happens with AtomicPtr intrinsics but is fine, as the pointers clippy is concerned about // are just normal values that get loaded/stored, but not dereferenced. @@ -248,40 +247,60 @@ use crate::cell::UnsafeCell; use crate::hint::spin_loop; use crate::intrinsics::AtomicOrdering as AO; +use crate::mem::transmute; use crate::{fmt, intrinsics}; -trait Sealed {} +#[unstable( + feature = "atomic_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" +)] +#[expect(missing_debug_implementations)] +mod private { + pub(super) trait Sealed {} + + #[cfg(target_has_atomic_load_store = "8")] + #[repr(C, align(1))] + pub struct Align1(T); + #[cfg(target_has_atomic_load_store = "16")] + #[repr(C, align(2))] + pub struct Align2(T); + #[cfg(target_has_atomic_load_store = "32")] + #[repr(C, align(4))] + pub struct Align4(T); + #[cfg(target_has_atomic_load_store = "64")] + #[repr(C, align(8))] + pub struct Align8(T); + #[cfg(target_has_atomic_load_store = "128")] + #[repr(C, align(16))] + pub struct Align16(T); +} /// A marker trait for primitive types which can be modified atomically. /// /// This is an implementation detail for [Atomic]\ which may disappear or be replaced at any time. -/// -/// # Safety -/// -/// Types implementing this trait must be primitives that can be modified atomically. -/// -/// The associated `Self::AtomicInner` type must have the same size and bit validity as `Self`, -/// but may have a higher alignment requirement, so the following `transmute`s are sound: -/// -/// - `&mut Self::AtomicInner` as `&mut Self` -/// - `Self` as `Self::AtomicInner` or the reverse +// +// # Safety +// +// Types implementing this trait must be primitives that can be modified atomically. +// +// The associated `Self::Storage` type must have the same size, but may have fewer validity +// invariants or a higher alignment requirement than `Self`. #[unstable( feature = "atomic_internals", reason = "implementation detail which may disappear or be replaced at any time", issue = "none" )] #[expect(private_bounds)] -pub unsafe trait AtomicPrimitive: Sized + Copy + Sealed { +pub unsafe trait AtomicPrimitive: Sized + Copy + private::Sealed { /// Temporary implementation detail. - type AtomicInner: Sized; + type Storage: Sized; } macro impl_atomic_primitive( - $Atom:ident $(<$T:ident>)? ($Primitive:ty), - size($size:literal), - align($align:literal) $(,)? + [$($T:ident)?] $Primitive:ty as $Storage:ident<$Operand:ty>, size($size:literal) ) { - impl $(<$T>)? Sealed for $Primitive {} + impl $(<$T>)? private::Sealed for $Primitive {} #[unstable( feature = "atomic_internals", @@ -290,42 +309,42 @@ impl $(<$T>)? Sealed for $Primitive {} )] #[cfg(target_has_atomic_load_store = $size)] unsafe impl $(<$T>)? AtomicPrimitive for $Primitive { - type AtomicInner = $Atom $(<$T>)?; + type Storage = private::$Storage<$Operand>; } } -impl_atomic_primitive!(AtomicBool(bool), size("8"), align(1)); -impl_atomic_primitive!(AtomicI8(i8), size("8"), align(1)); -impl_atomic_primitive!(AtomicU8(u8), size("8"), align(1)); -impl_atomic_primitive!(AtomicI16(i16), size("16"), align(2)); -impl_atomic_primitive!(AtomicU16(u16), size("16"), align(2)); -impl_atomic_primitive!(AtomicI32(i32), size("32"), align(4)); -impl_atomic_primitive!(AtomicU32(u32), size("32"), align(4)); -impl_atomic_primitive!(AtomicI64(i64), size("64"), align(8)); -impl_atomic_primitive!(AtomicU64(u64), size("64"), align(8)); -impl_atomic_primitive!(AtomicI128(i128), size("128"), align(16)); -impl_atomic_primitive!(AtomicU128(u128), size("128"), align(16)); +impl_atomic_primitive!([] bool as Align1, size("8")); +impl_atomic_primitive!([] i8 as Align1, size("8")); +impl_atomic_primitive!([] u8 as Align1, size("8")); +impl_atomic_primitive!([] i16 as Align2, size("16")); +impl_atomic_primitive!([] u16 as Align2, size("16")); +impl_atomic_primitive!([] i32 as Align4, size("32")); +impl_atomic_primitive!([] u32 as Align4, size("32")); +impl_atomic_primitive!([] i64 as Align8, size("64")); +impl_atomic_primitive!([] u64 as Align8, size("64")); +impl_atomic_primitive!([] i128 as Align16, size("128")); +impl_atomic_primitive!([] u128 as Align16, size("128")); #[cfg(target_pointer_width = "16")] -impl_atomic_primitive!(AtomicIsize(isize), size("ptr"), align(2)); +impl_atomic_primitive!([] isize as Align2, size("ptr")); #[cfg(target_pointer_width = "32")] -impl_atomic_primitive!(AtomicIsize(isize), size("ptr"), align(4)); +impl_atomic_primitive!([] isize as Align4, size("ptr")); #[cfg(target_pointer_width = "64")] -impl_atomic_primitive!(AtomicIsize(isize), size("ptr"), align(8)); +impl_atomic_primitive!([] isize as Align8, size("ptr")); #[cfg(target_pointer_width = "16")] -impl_atomic_primitive!(AtomicUsize(usize), size("ptr"), align(2)); +impl_atomic_primitive!([] usize as Align2, size("ptr")); #[cfg(target_pointer_width = "32")] -impl_atomic_primitive!(AtomicUsize(usize), size("ptr"), align(4)); +impl_atomic_primitive!([] usize as Align4, size("ptr")); #[cfg(target_pointer_width = "64")] -impl_atomic_primitive!(AtomicUsize(usize), size("ptr"), align(8)); +impl_atomic_primitive!([] usize as Align8, size("ptr")); #[cfg(target_pointer_width = "16")] -impl_atomic_primitive!(AtomicPtr(*mut T), size("ptr"), align(2)); +impl_atomic_primitive!([T] *mut T as Align2<*mut T>, size("ptr")); #[cfg(target_pointer_width = "32")] -impl_atomic_primitive!(AtomicPtr(*mut T), size("ptr"), align(4)); +impl_atomic_primitive!([T] *mut T as Align4<*mut T>, size("ptr")); #[cfg(target_pointer_width = "64")] -impl_atomic_primitive!(AtomicPtr(*mut T), size("ptr"), align(8)); +impl_atomic_primitive!([T] *mut T as Align8<*mut T>, size("ptr")); /// A memory location which can be safely modified from multiple threads. /// @@ -342,7 +361,16 @@ unsafe impl $(<$T>)? AtomicPrimitive for $Primitive { /// /// [module-level documentation]: crate::sync::atomic #[unstable(feature = "generic_atomic", issue = "130539")] -pub type Atomic = ::AtomicInner; +#[repr(C)] +#[rustc_diagnostic_item = "Atomic"] +pub struct Atomic { + v: UnsafeCell, +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Atomic {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for Atomic {} // Some architectures don't have byte-sized atomics, which results in LLVM // emulating them using a LL/SC loop. However for AtomicBool we can take @@ -367,11 +395,7 @@ unsafe impl $(<$T>)? AtomicPrimitive for $Primitive { /// loads and stores of `u8`. #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_diagnostic_item = "AtomicBool"] -#[repr(C, align(1))] -pub struct AtomicBool { - v: UnsafeCell, -} +pub type AtomicBool = Atomic; #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "rust1", since = "1.0.0")] @@ -383,11 +407,6 @@ fn default() -> Self { } } -// Send is implicitly implemented for AtomicBool. -#[cfg(target_has_atomic_load_store = "8")] -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for AtomicBool {} - /// A raw pointer type which can be safely shared between threads. /// /// This type has the same size and bit validity as a `*mut T`. @@ -396,13 +415,7 @@ unsafe impl Sync for AtomicBool {} /// loads and stores of pointers. Its size depends on the target pointer's size. #[cfg(target_has_atomic_load_store = "ptr")] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_diagnostic_item = "AtomicPtr"] -#[cfg_attr(target_pointer_width = "16", repr(C, align(2)))] -#[cfg_attr(target_pointer_width = "32", repr(C, align(4)))] -#[cfg_attr(target_pointer_width = "64", repr(C, align(8)))] -pub struct AtomicPtr { - p: UnsafeCell<*mut T>, -} +pub type AtomicPtr = Atomic<*mut T>; #[cfg(target_has_atomic_load_store = "ptr")] #[stable(feature = "rust1", since = "1.0.0")] @@ -413,13 +426,6 @@ fn default() -> AtomicPtr { } } -#[cfg(target_has_atomic_load_store = "ptr")] -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for AtomicPtr {} -#[cfg(target_has_atomic_load_store = "ptr")] -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for AtomicPtr {} - /// Atomic memory orderings /// /// Memory orderings specify the way atomic operations synchronize memory. @@ -528,7 +534,9 @@ impl AtomicBool { #[rustc_const_stable(feature = "const_atomic_new", since = "1.24.0")] #[must_use] pub const fn new(v: bool) -> AtomicBool { - AtomicBool { v: UnsafeCell::new(v as u8) } + // SAFETY: + // `Atomic` is essentially a transparent wrapper around `T`. + unsafe { transmute(v) } } /// Creates a new `AtomicBool` from a pointer. @@ -597,7 +605,7 @@ pub const fn new(v: bool) -> AtomicBool { #[stable(feature = "atomic_access", since = "1.15.0")] pub fn get_mut(&mut self) -> &mut bool { // SAFETY: the mutable reference guarantees unique ownership. - unsafe { &mut *(self.v.get() as *mut bool) } + unsafe { &mut *self.as_ptr() } } /// Gets atomic access to a `&mut bool`. @@ -699,7 +707,11 @@ pub fn from_mut_slice(v: &mut [bool]) -> &mut [Self] { #[stable(feature = "atomic_access", since = "1.15.0")] #[rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0")] pub const fn into_inner(self) -> bool { - self.v.into_inner() != 0 + // SAFETY: + // * `Atomic` is essentially a transparent wrapper around `T`. + // * all operations on `Atomic` ensure that `T::Storage` remains + // a valid `bool`. + unsafe { transmute(self) } } /// Loads a value from the bool. @@ -726,7 +738,7 @@ pub const fn into_inner(self) -> bool { pub fn load(&self, order: Ordering) -> bool { // SAFETY: any data races are prevented by atomic intrinsics and the raw // pointer passed in is valid because we got it from a reference. - unsafe { atomic_load(self.v.get(), order) != 0 } + unsafe { atomic_load(self.v.get().cast::(), order) != 0 } } /// Stores a value into the bool. @@ -756,7 +768,7 @@ pub fn store(&self, val: bool, order: Ordering) { // SAFETY: any data races are prevented by atomic intrinsics and the raw // pointer passed in is valid because we got it from a reference. unsafe { - atomic_store(self.v.get(), val as u8, order); + atomic_store(self.v.get().cast::(), val as u8, order); } } @@ -790,7 +802,7 @@ pub fn swap(&self, val: bool, order: Ordering) -> bool { if val { self.fetch_or(true, order) } else { self.fetch_and(false, order) } } else { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_swap(self.v.get(), val as u8, order) != 0 } + unsafe { atomic_swap(self.v.get().cast::(), val as u8, order) != 0 } } } @@ -950,7 +962,13 @@ pub fn compare_exchange( } else { // SAFETY: data races are prevented by atomic intrinsics. match unsafe { - atomic_compare_exchange(self.v.get(), current as u8, new as u8, success, failure) + atomic_compare_exchange( + self.v.get().cast::(), + current as u8, + new as u8, + success, + failure, + ) } { Ok(x) => Ok(x != 0), Err(x) => Err(x != 0), @@ -1024,7 +1042,13 @@ pub fn compare_exchange_weak( // SAFETY: data races are prevented by atomic intrinsics. match unsafe { - atomic_compare_exchange_weak(self.v.get(), current as u8, new as u8, success, failure) + atomic_compare_exchange_weak( + self.v.get().cast::(), + current as u8, + new as u8, + success, + failure, + ) } { Ok(x) => Ok(x != 0), Err(x) => Err(x != 0), @@ -1070,7 +1094,7 @@ pub fn compare_exchange_weak( #[rustc_should_not_be_called_on_const_items] pub fn fetch_and(&self, val: bool, order: Ordering) -> bool { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_and(self.v.get(), val as u8, order) != 0 } + unsafe { atomic_and(self.v.get().cast::(), val as u8, order) != 0 } } /// Logical "nand" with a boolean value. @@ -1166,7 +1190,7 @@ pub fn fetch_nand(&self, val: bool, order: Ordering) -> bool { #[rustc_should_not_be_called_on_const_items] pub fn fetch_or(&self, val: bool, order: Ordering) -> bool { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_or(self.v.get(), val as u8, order) != 0 } + unsafe { atomic_or(self.v.get().cast::(), val as u8, order) != 0 } } /// Logical "xor" with a boolean value. @@ -1208,7 +1232,7 @@ pub fn fetch_or(&self, val: bool, order: Ordering) -> bool { #[rustc_should_not_be_called_on_const_items] pub fn fetch_xor(&self, val: bool, order: Ordering) -> bool { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_xor(self.v.get(), val as u8, order) != 0 } + unsafe { atomic_xor(self.v.get().cast::(), val as u8, order) != 0 } } /// Logical "not" with a boolean value. @@ -1358,7 +1382,7 @@ pub fn fetch_update( /// assert_eq!(x.load(Ordering::SeqCst), false); /// ``` #[inline] - #[stable(feature = "atomic_try_update", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "atomic_try_update", since = "1.95.0")] #[cfg(target_has_atomic = "8")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] @@ -1421,7 +1445,7 @@ pub fn try_update( /// assert_eq!(x.load(Ordering::SeqCst), false); /// ``` #[inline] - #[stable(feature = "atomic_try_update", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "atomic_try_update", since = "1.95.0")] #[cfg(target_has_atomic = "8")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] @@ -1457,7 +1481,9 @@ impl AtomicPtr { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_atomic_new", since = "1.24.0")] pub const fn new(p: *mut T) -> AtomicPtr { - AtomicPtr { p: UnsafeCell::new(p) } + // SAFETY: + // `Atomic` is essentially a transparent wrapper around `T`. + unsafe { transmute(p) } } /// Creates a new `AtomicPtr` from a pointer. @@ -1544,7 +1570,9 @@ pub const fn null() -> AtomicPtr { #[inline] #[stable(feature = "atomic_access", since = "1.15.0")] pub fn get_mut(&mut self) -> &mut *mut T { - self.p.get_mut() + // SAFETY: + // `Atomic` is essentially a transparent wrapper around `T`. + unsafe { &mut *self.as_ptr() } } /// Gets atomic access to a pointer. @@ -1672,7 +1700,9 @@ pub fn from_mut_slice(v: &mut [*mut T]) -> &mut [Self] { #[stable(feature = "atomic_access", since = "1.15.0")] #[rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0")] pub const fn into_inner(self) -> *mut T { - self.p.into_inner() + // SAFETY: + // `Atomic` is essentially a transparent wrapper around `T`. + unsafe { transmute(self) } } /// Loads a value from the pointer. @@ -1699,7 +1729,7 @@ pub const fn into_inner(self) -> *mut T { #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn load(&self, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_load(self.p.get(), order) } + unsafe { atomic_load(self.as_ptr(), order) } } /// Stores a value into the pointer. @@ -1730,7 +1760,7 @@ pub fn load(&self, order: Ordering) -> *mut T { pub fn store(&self, ptr: *mut T, order: Ordering) { // SAFETY: data races are prevented by atomic intrinsics. unsafe { - atomic_store(self.p.get(), ptr, order); + atomic_store(self.as_ptr(), ptr, order); } } @@ -1763,7 +1793,7 @@ pub fn store(&self, ptr: *mut T, order: Ordering) { #[rustc_should_not_be_called_on_const_items] pub fn swap(&self, ptr: *mut T, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_swap(self.p.get(), ptr, order) } + unsafe { atomic_swap(self.as_ptr(), ptr, order) } } /// Stores a value into the pointer if the current value is the same as the `current` value. @@ -1887,7 +1917,7 @@ pub fn compare_exchange( failure: Ordering, ) -> Result<*mut T, *mut T> { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_compare_exchange(self.p.get(), current, new, success, failure) } + unsafe { atomic_compare_exchange(self.as_ptr(), current, new, success, failure) } } /// Stores a value into the pointer if the current value is the same as the `current` value. @@ -1954,7 +1984,7 @@ pub fn compare_exchange_weak( // but we know for sure that the pointer is valid (we just got it from // an `UnsafeCell` that we have by reference) and the atomic operation // itself allows us to safely mutate the `UnsafeCell` contents. - unsafe { atomic_compare_exchange_weak(self.p.get(), current, new, success, failure) } + unsafe { atomic_compare_exchange_weak(self.as_ptr(), current, new, success, failure) } } /// An alias for [`AtomicPtr::try_update`]. @@ -2037,7 +2067,7 @@ pub fn fetch_update( /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); /// ``` #[inline] - #[stable(feature = "atomic_try_update", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "atomic_try_update", since = "1.95.0")] #[cfg(target_has_atomic = "ptr")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] @@ -2105,7 +2135,7 @@ pub fn try_update( /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); /// ``` #[inline] - #[stable(feature = "atomic_try_update", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "atomic_try_update", since = "1.95.0")] #[cfg(target_has_atomic = "8")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] @@ -2243,7 +2273,7 @@ pub fn fetch_ptr_sub(&self, val: usize, order: Ordering) -> *mut T { #[rustc_should_not_be_called_on_const_items] pub fn fetch_byte_add(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_add(self.p.get(), val, order).cast() } + unsafe { atomic_add(self.as_ptr(), val, order).cast() } } /// Offsets the pointer's address by subtracting `val` *bytes*, returning the @@ -2279,7 +2309,7 @@ pub fn fetch_byte_add(&self, val: usize, order: Ordering) -> *mut T { #[rustc_should_not_be_called_on_const_items] pub fn fetch_byte_sub(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_sub(self.p.get(), val, order).cast() } + unsafe { atomic_sub(self.as_ptr(), val, order).cast() } } /// Performs a bitwise "or" operation on the address of the current pointer, @@ -2330,7 +2360,7 @@ pub fn fetch_byte_sub(&self, val: usize, order: Ordering) -> *mut T { #[rustc_should_not_be_called_on_const_items] pub fn fetch_or(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_or(self.p.get(), val, order).cast() } + unsafe { atomic_or(self.as_ptr(), val, order).cast() } } /// Performs a bitwise "and" operation on the address of the current @@ -2380,7 +2410,7 @@ pub fn fetch_or(&self, val: usize, order: Ordering) -> *mut T { #[rustc_should_not_be_called_on_const_items] pub fn fetch_and(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_and(self.p.get(), val, order).cast() } + unsafe { atomic_and(self.as_ptr(), val, order).cast() } } /// Performs a bitwise "xor" operation on the address of the current @@ -2428,7 +2458,7 @@ pub fn fetch_and(&self, val: usize, order: Ordering) -> *mut T { #[rustc_should_not_be_called_on_const_items] pub fn fetch_xor(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_xor(self.p.get(), val, order).cast() } + unsafe { atomic_xor(self.as_ptr(), val, order).cast() } } /// Returns a mutable pointer to the underlying pointer. @@ -2467,7 +2497,7 @@ pub fn fetch_xor(&self, val: usize, order: Ordering) -> *mut T { #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")] #[rustc_never_returns_null_ptr] pub const fn as_ptr(&self) -> *mut *mut T { - self.p.get() + self.v.get().cast() } } @@ -2520,7 +2550,6 @@ macro_rules! atomic_int { $stable_nand:meta, $const_stable_new:meta, $const_stable_into_inner:meta, - $diagnostic_item:meta, $s_int_type:literal, $extra_feature:expr, $min_fn:ident, $max_fn:ident, @@ -2557,11 +2586,7 @@ macro_rules! atomic_int { /// /// [module-level documentation]: crate::sync::atomic #[$stable] - #[$diagnostic_item] - #[repr(C, align($align))] - pub struct $atomic_type { - v: UnsafeCell<$int_type>, - } + pub type $atomic_type = Atomic<$int_type>; #[$stable] impl Default for $atomic_type { @@ -2586,10 +2611,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } - // Send is implicitly implemented. - #[$stable] - unsafe impl Sync for $atomic_type {} - impl $atomic_type { /// Creates a new atomic integer. /// @@ -2605,7 +2626,9 @@ impl $atomic_type { #[$const_stable_new] #[must_use] pub const fn new(v: $int_type) -> Self { - Self {v: UnsafeCell::new(v)} + // SAFETY: + // `Atomic` is essentially a transparent wrapper around `T`. + unsafe { transmute(v) } } /// Creates a new reference to an atomic integer from a pointer. @@ -2667,7 +2690,6 @@ pub const fn new(v: $int_type) -> Self { unsafe { &*ptr.cast() } } - /// Returns a mutable reference to the underlying integer. /// /// This is safe because the mutable reference guarantees that no other threads are @@ -2686,7 +2708,9 @@ pub const fn new(v: $int_type) -> Self { #[inline] #[$stable_access] pub fn get_mut(&mut self) -> &mut $int_type { - self.v.get_mut() + // SAFETY: + // `Atomic` is essentially a transparent wrapper around `T`. + unsafe { &mut *self.as_ptr() } } #[doc = concat!("Get atomic access to a `&mut ", stringify!($int_type), "`.")] @@ -2815,7 +2839,9 @@ pub fn from_mut_slice(v: &mut [$int_type]) -> &mut [Self] { #[$stable_access] #[$const_stable_into_inner] pub const fn into_inner(self) -> $int_type { - self.v.into_inner() + // SAFETY: + // `Atomic` is essentially a transparent wrapper around `T`. + unsafe { transmute(self) } } /// Loads a value from the atomic integer. @@ -2841,7 +2867,7 @@ pub const fn into_inner(self) -> $int_type { #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn load(&self, order: Ordering) -> $int_type { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_load(self.v.get(), order) } + unsafe { atomic_load(self.as_ptr(), order) } } /// Stores a value into the atomic integer. @@ -2869,7 +2895,7 @@ pub fn load(&self, order: Ordering) -> $int_type { #[rustc_should_not_be_called_on_const_items] pub fn store(&self, val: $int_type, order: Ordering) { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_store(self.v.get(), val, order); } + unsafe { atomic_store(self.as_ptr(), val, order); } } /// Stores a value into the atomic integer, returning the previous value. @@ -2898,7 +2924,7 @@ pub fn store(&self, val: $int_type, order: Ordering) { #[rustc_should_not_be_called_on_const_items] pub fn swap(&self, val: $int_type, order: Ordering) -> $int_type { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_swap(self.v.get(), val, order) } + unsafe { atomic_swap(self.as_ptr(), val, order) } } /// Stores a value into the atomic integer if the current value is the same as @@ -3036,7 +3062,7 @@ pub fn compare_exchange(&self, success: Ordering, failure: Ordering) -> Result<$int_type, $int_type> { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_compare_exchange(self.v.get(), current, new, success, failure) } + unsafe { atomic_compare_exchange(self.as_ptr(), current, new, success, failure) } } /// Stores a value into the atomic integer if the current value is the same as @@ -3101,7 +3127,7 @@ pub fn compare_exchange_weak(&self, failure: Ordering) -> Result<$int_type, $int_type> { // SAFETY: data races are prevented by atomic intrinsics. unsafe { - atomic_compare_exchange_weak(self.v.get(), current, new, success, failure) + atomic_compare_exchange_weak(self.as_ptr(), current, new, success, failure) } } @@ -3133,7 +3159,7 @@ pub fn compare_exchange_weak(&self, #[rustc_should_not_be_called_on_const_items] pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_add(self.v.get(), val, order) } + unsafe { atomic_add(self.as_ptr(), val, order) } } /// Subtracts from the current value, returning the previous value. @@ -3164,7 +3190,7 @@ pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type { #[rustc_should_not_be_called_on_const_items] pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_sub(self.v.get(), val, order) } + unsafe { atomic_sub(self.as_ptr(), val, order) } } /// Bitwise "and" with the current value. @@ -3198,7 +3224,7 @@ pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type { #[rustc_should_not_be_called_on_const_items] pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_and(self.v.get(), val, order) } + unsafe { atomic_and(self.as_ptr(), val, order) } } /// Bitwise "nand" with the current value. @@ -3232,7 +3258,7 @@ pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type { #[rustc_should_not_be_called_on_const_items] pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_nand(self.v.get(), val, order) } + unsafe { atomic_nand(self.as_ptr(), val, order) } } /// Bitwise "or" with the current value. @@ -3266,7 +3292,7 @@ pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type { #[rustc_should_not_be_called_on_const_items] pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_or(self.v.get(), val, order) } + unsafe { atomic_or(self.as_ptr(), val, order) } } /// Bitwise "xor" with the current value. @@ -3300,7 +3326,7 @@ pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type { #[rustc_should_not_be_called_on_const_items] pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_xor(self.v.get(), val, order) } + unsafe { atomic_xor(self.as_ptr(), val, order) } } /// An alias for @@ -3372,7 +3398,7 @@ pub fn fetch_update(&self, /// assert_eq!(x.load(Ordering::SeqCst), 9); /// ``` #[inline] - #[stable(feature = "atomic_try_update", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "atomic_try_update", since = "1.95.0")] #[$cfg_cas] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] @@ -3438,7 +3464,7 @@ pub fn try_update( /// assert_eq!(x.load(Ordering::SeqCst), 9); /// ``` #[inline] - #[stable(feature = "atomic_try_update", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "atomic_try_update", since = "1.95.0")] #[$cfg_cas] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] @@ -3499,7 +3525,7 @@ pub fn update( #[rustc_should_not_be_called_on_const_items] pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { $max_fn(self.v.get(), val, order) } + unsafe { $max_fn(self.as_ptr(), val, order) } } /// Minimum with the current value. @@ -3546,7 +3572,7 @@ pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type { #[rustc_should_not_be_called_on_const_items] pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type { // SAFETY: data races are prevented by atomic intrinsics. - unsafe { $min_fn(self.v.get(), val, order) } + unsafe { $min_fn(self.as_ptr(), val, order) } } /// Returns a mutable pointer to the underlying integer. @@ -3586,7 +3612,7 @@ pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type { #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")] #[rustc_never_returns_null_ptr] pub const fn as_ptr(&self) -> *mut $int_type { - self.v.get() + self.v.get().cast() } } } @@ -3604,7 +3630,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - rustc_diagnostic_item = "AtomicI8", "i8", "", atomic_min, atomic_max, @@ -3623,7 +3648,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - rustc_diagnostic_item = "AtomicU8", "u8", "", atomic_umin, atomic_umax, @@ -3642,7 +3666,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - rustc_diagnostic_item = "AtomicI16", "i16", "", atomic_min, atomic_max, @@ -3661,7 +3684,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - rustc_diagnostic_item = "AtomicU16", "u16", "", atomic_umin, atomic_umax, @@ -3680,7 +3702,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - rustc_diagnostic_item = "AtomicI32", "i32", "", atomic_min, atomic_max, @@ -3699,7 +3720,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - rustc_diagnostic_item = "AtomicU32", "u32", "", atomic_umin, atomic_umax, @@ -3718,7 +3738,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - rustc_diagnostic_item = "AtomicI64", "i64", "", atomic_min, atomic_max, @@ -3737,7 +3756,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - rustc_diagnostic_item = "AtomicU64", "u64", "", atomic_umin, atomic_umax, @@ -3756,7 +3774,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { unstable(feature = "integer_atomics", issue = "99069"), rustc_const_unstable(feature = "integer_atomics", issue = "99069"), rustc_const_unstable(feature = "integer_atomics", issue = "99069"), - rustc_diagnostic_item = "AtomicI128", "i128", "#![feature(integer_atomics)]\n\n", atomic_min, atomic_max, @@ -3775,7 +3792,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { unstable(feature = "integer_atomics", issue = "99069"), rustc_const_unstable(feature = "integer_atomics", issue = "99069"), rustc_const_unstable(feature = "integer_atomics", issue = "99069"), - rustc_diagnostic_item = "AtomicU128", "u128", "#![feature(integer_atomics)]\n\n", atomic_umin, atomic_umax, @@ -3798,7 +3814,6 @@ macro_rules! atomic_int_ptr_sized { stable(feature = "atomic_nand", since = "1.27.0"), rustc_const_stable(feature = "const_ptr_sized_atomics", since = "1.24.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - rustc_diagnostic_item = "AtomicIsize", "isize", "", atomic_min, atomic_max, @@ -3817,7 +3832,6 @@ macro_rules! atomic_int_ptr_sized { stable(feature = "atomic_nand", since = "1.27.0"), rustc_const_stable(feature = "const_ptr_sized_atomics", since = "1.24.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - rustc_diagnostic_item = "AtomicUsize", "usize", "", atomic_umin, atomic_umax, diff --git a/library/coretests/benches/num/flt2dec/mod.rs b/library/coretests/benches/num/flt2dec/mod.rs index 428d0bbbbfbf..0cd94ec5e1cf 100644 --- a/library/coretests/benches/num/flt2dec/mod.rs +++ b/library/coretests/benches/num/flt2dec/mod.rs @@ -3,7 +3,7 @@ mod strategy { mod grisu; } -use core::num::flt2dec::{DecodableFloat, Decoded, FullDecoded, MAX_SIG_DIGITS, decode}; +use core::num::imp::flt2dec::{DecodableFloat, Decoded, FullDecoded, MAX_SIG_DIGITS, decode}; use std::io::Write; use test::{Bencher, black_box}; diff --git a/library/coretests/benches/num/flt2dec/strategy/dragon.rs b/library/coretests/benches/num/flt2dec/strategy/dragon.rs index 452669714036..d98b3df9b2f3 100644 --- a/library/coretests/benches/num/flt2dec/strategy/dragon.rs +++ b/library/coretests/benches/num/flt2dec/strategy/dragon.rs @@ -1,4 +1,4 @@ -use core::num::flt2dec::strategy::dragon::*; +use core::num::imp::flt2dec::strategy::dragon::*; use std::mem::MaybeUninit; use super::super::*; diff --git a/library/coretests/benches/num/flt2dec/strategy/grisu.rs b/library/coretests/benches/num/flt2dec/strategy/grisu.rs index d20f9b02f7e7..5f73fdebf5e7 100644 --- a/library/coretests/benches/num/flt2dec/strategy/grisu.rs +++ b/library/coretests/benches/num/flt2dec/strategy/grisu.rs @@ -1,4 +1,4 @@ -use core::num::flt2dec::strategy::grisu::*; +use core::num::imp::flt2dec::strategy::grisu::*; use std::mem::MaybeUninit; use super::super::*; diff --git a/library/coretests/tests/cmp.rs b/library/coretests/tests/cmp.rs index 55e35a4a7250..0a14470060c3 100644 --- a/library/coretests/tests/cmp.rs +++ b/library/coretests/tests/cmp.rs @@ -1,5 +1,5 @@ +use core::cmp; use core::cmp::Ordering::{self, *}; -use core::cmp::{self}; #[test] fn test_int_totalord() { diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 06fc3c96eafc..3e39528dfbf4 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -2,19 +2,37 @@ use std::ops::{Add, Div, Mul, Rem, Sub}; trait TestableFloat: Sized { + const BITS: u32; /// Unsigned int with the same size, for converting to/from bits. type Int; + /// Signed int with the same size. + type SInt; /// Set the default tolerance for float comparison based on the type. const APPROX: Self; /// Allow looser tolerance for f32 on miri const POWI_APPROX: Self = Self::APPROX; + /// Tolerance for `powf` tests; some types need looser bounds + const POWF_APPROX: Self = Self::APPROX; /// Allow looser tolerance for f16 const _180_TO_RADIANS_APPROX: Self = Self::APPROX; /// Allow for looser tolerance for f16 const PI_TO_DEGREES_APPROX: Self = Self::APPROX; + /// Tolerance for math tests + const EXP_APPROX: Self = Self::APPROX; + const LN_APPROX: Self = Self::APPROX; + const LOG_APPROX: Self = Self::APPROX; + const LOG2_APPROX: Self = Self::APPROX; + const LOG10_APPROX: Self = Self::APPROX; + const ASINH_APPROX: Self = Self::APPROX; + const ACOSH_APPROX: Self = Self::APPROX; + const ATANH_APPROX: Self = Self::APPROX; + const GAMMA_APPROX: Self = Self::APPROX; + const GAMMA_APPROX_LOOSE: Self = Self::APPROX; + const LNGAMMA_APPROX: Self = Self::APPROX; + const LNGAMMA_APPROX_LOOSE: Self = Self::APPROX; const ZERO: Self; const ONE: Self; - const PI: Self; + const MIN_POSITIVE_NORMAL: Self; const MAX_SUBNORMAL: Self; /// Smallest number @@ -43,13 +61,27 @@ trait TestableFloat: Sized { } impl TestableFloat for f16 { + const BITS: u32 = 16; type Int = u16; + type SInt = i16; const APPROX: Self = 1e-3; + const POWF_APPROX: Self = 5e-1; const _180_TO_RADIANS_APPROX: Self = 1e-2; const PI_TO_DEGREES_APPROX: Self = 0.125; + const EXP_APPROX: Self = 1e-2; + const LN_APPROX: Self = 1e-2; + const LOG_APPROX: Self = 1e-2; + const LOG2_APPROX: Self = 1e-2; + const LOG10_APPROX: Self = 1e-2; + const ASINH_APPROX: Self = 1e-2; + const ACOSH_APPROX: Self = 1e-2; + const ATANH_APPROX: Self = 1e-2; + const GAMMA_APPROX: Self = 1e-2; + const GAMMA_APPROX_LOOSE: Self = 1e-1; + const LNGAMMA_APPROX: Self = 1e-2; + const LNGAMMA_APPROX_LOOSE: Self = 1e-1; const ZERO: Self = 0.0; const ONE: Self = 1.0; - const PI: Self = std::f16::consts::PI; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); const TINY: Self = Self::from_bits(0x1); @@ -70,15 +102,29 @@ impl TestableFloat for f16 { } impl TestableFloat for f32 { + const BITS: u32 = 32; type Int = u32; + type SInt = i32; const APPROX: Self = 1e-6; /// Miri adds some extra errors to float functions; make sure the tests still pass. /// These values are purely used as a canary to test against and are thus not a stable guarantee Rust provides. /// They serve as a way to get an idea of the real precision of floating point operations on different platforms. const POWI_APPROX: Self = if cfg!(miri) { 1e-4 } else { Self::APPROX }; + const POWF_APPROX: Self = if cfg!(miri) { 1e-3 } else { 1e-4 }; + const EXP_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const LN_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const LOG_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const LOG2_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const LOG10_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const ASINH_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const ACOSH_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const ATANH_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const GAMMA_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const GAMMA_APPROX_LOOSE: Self = if cfg!(miri) { 1e-2 } else { 1e-4 }; + const LNGAMMA_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const LNGAMMA_APPROX_LOOSE: Self = if cfg!(miri) { 1e-2 } else { 1e-4 }; const ZERO: Self = 0.0; const ONE: Self = 1.0; - const PI: Self = std::f32::consts::PI; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); const TINY: Self = Self::from_bits(0x1); @@ -99,11 +145,14 @@ impl TestableFloat for f32 { } impl TestableFloat for f64 { + const BITS: u32 = 64; type Int = u64; + type SInt = i64; const APPROX: Self = 1e-6; + const GAMMA_APPROX_LOOSE: Self = 1e-4; + const LNGAMMA_APPROX_LOOSE: Self = 1e-4; const ZERO: Self = 0.0; const ONE: Self = 1.0; - const PI: Self = std::f64::consts::PI; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); const TINY: Self = Self::from_bits(0x1); @@ -124,11 +173,24 @@ impl TestableFloat for f64 { } impl TestableFloat for f128 { + const BITS: u32 = 128; type Int = u128; + type SInt = i128; const APPROX: Self = 1e-9; + const EXP_APPROX: Self = 1e-12; + const LN_APPROX: Self = 1e-12; + const LOG_APPROX: Self = 1e-12; + const LOG2_APPROX: Self = 1e-12; + const LOG10_APPROX: Self = 1e-12; + const ASINH_APPROX: Self = 1e-10; + const ACOSH_APPROX: Self = 1e-10; + const ATANH_APPROX: Self = 1e-10; + const GAMMA_APPROX: Self = 1e-12; + const GAMMA_APPROX_LOOSE: Self = 1e-10; + const LNGAMMA_APPROX: Self = 1e-12; + const LNGAMMA_APPROX_LOOSE: Self = 1e-10; const ZERO: Self = 0.0; const ONE: Self = 1.0; - const PI: Self = std::f128::consts::PI; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); const TINY: Self = Self::from_bits(0x1); @@ -156,31 +218,28 @@ const fn lim_for_ty(_x: T) -> T { // We have runtime ("rt") and const versions of these macros. /// Verify that floats are within a tolerance of each other. -macro_rules! assert_approx_eq_rt { - ($a:expr, $b:expr) => {{ assert_approx_eq_rt!($a, $b, $crate::floats::lim_for_ty($a)) }}; +macro_rules! assert_approx_eq { + ($a:expr, $b:expr $(,)?) => {{ assert_approx_eq!($a, $b, $crate::floats::lim_for_ty($a)) }}; ($a:expr, $b:expr, $lim:expr) => {{ let (a, b) = (&$a, &$b); let diff = (*a - *b).abs(); - assert!( + core::panic::const_assert!( diff <= $lim, + "actual value is not approximately equal to expected value", "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", - lim = $lim + // We rely on the `Float` type being brought in scope by the macros below. + a: Float = *a, + b: Float = *b, + diff: Float = diff, + lim: Float = $lim, ); }}; } -macro_rules! assert_approx_eq_const { - ($a:expr, $b:expr) => {{ assert_approx_eq_const!($a, $b, $crate::floats::lim_for_ty($a)) }}; - ($a:expr, $b:expr, $lim:expr) => {{ - let (a, b) = (&$a, &$b); - let diff = (*a - *b).abs(); - assert!(diff <= $lim); - }}; -} /// Verify that floats have the same bitwise representation. Used to avoid the default `0.0 == -0.0` /// behavior, as well as to ensure exact NaN bitpatterns. -macro_rules! assert_biteq_rt { - (@inner $left:expr, $right:expr, $msg_sep:literal, $($tt:tt)*) => {{ +macro_rules! assert_biteq { + ($left:expr, $right:expr $(,)?) => {{ let l = $left; let r = $right; @@ -190,62 +249,20 @@ macro_rules! assert_biteq_rt { // Hack to get the width from a value let bits = (l.to_bits() - l.to_bits()).leading_zeros(); - assert!( + core::panic::const_assert!( l.to_bits() == r.to_bits(), - "{msg}{nl}l: {l:?} ({lb:#0width$x})\nr: {r:?} ({rb:#0width$x})", - msg = format_args!($($tt)*), - nl = $msg_sep, - lb = l.to_bits(), - rb = r.to_bits(), - width = ((bits / 4) + 2) as usize, + "actual value and expected value are not bit-equal", + "actual value and expected value are not bit-equal\n\ + l: {l:?} ({lb:#0width$x})\nr: {r:?} ({rb:#0width$x})", + // We rely on the `Float` type being brought in scope by the macros below. + l: Float = l, + r: Float = r, + lb: ::Int = l.to_bits(), + rb: ::Int = r.to_bits(), + width: usize = ((bits / 4) + 2) as usize, ); - - if !l.is_nan() && !r.is_nan() { - // Also check that standard equality holds, since most tests use `assert_biteq` rather - // than `assert_eq`. - assert_eq!(l, r); - } }}; - ($left:expr, $right:expr , $($tt:tt)*) => { - assert_biteq_rt!(@inner $left, $right, "\n", $($tt)*) - }; - ($left:expr, $right:expr $(,)?) => { - assert_biteq_rt!(@inner $left, $right, "", "") - }; } -macro_rules! assert_biteq_const { - (@inner $left:expr, $right:expr, $msg_sep:literal, $($tt:tt)*) => {{ - let l = $left; - let r = $right; - - // Hack to coerce left and right to the same type - let mut _eq_ty = l; - _eq_ty = r; - - assert!(l.to_bits() == r.to_bits()); - - if !l.is_nan() && !r.is_nan() { - // Also check that standard equality holds, since most tests use `assert_biteq` rather - // than `assert_eq`. - assert!(l == r); - } - }}; - ($left:expr, $right:expr , $($tt:tt)*) => { - assert_biteq_const!(@inner $left, $right, "\n", $($tt)*) - }; - ($left:expr, $right:expr $(,)?) => { - assert_biteq_const!(@inner $left, $right, "", "") - }; -} - -// Use the runtime version by default. -// This way, they can be shadowed by the const versions. -pub(crate) use {assert_approx_eq_rt as assert_approx_eq, assert_biteq_rt as assert_biteq}; - -// Also make the const version available for re-exports. -#[rustfmt::skip] -pub(crate) use assert_biteq_const; -pub(crate) use assert_approx_eq_const; /// Generate float tests for all our float types, for compile-time and run-time behavior. /// @@ -279,7 +296,7 @@ macro_rules! float_test { $(f128: #[ $($f128_meta:meta),+ ] ,)? $(const f128: #[ $($f128_const_meta:meta),+ ] ,)? }, - test<$fty:ident> $test:block + test $test:block ) => { mod $name { use super::*; @@ -287,87 +304,92 @@ mod $name { #[test] $( $( #[$f16_meta] )+ )? fn test_f16() { - type $fty = f16; + #[allow(unused_imports)] + use core::f16::consts; + type Float = f16; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } $test } #[test] $( $( #[$f32_meta] )+ )? fn test_f32() { - type $fty = f32; + #[allow(unused_imports)] + use core::f32::consts; + type Float = f32; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } $test } #[test] $( $( #[$f64_meta] )+ )? fn test_f64() { - type $fty = f64; + #[allow(unused_imports)] + use core::f64::consts; + type Float = f64; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } $test } #[test] $( $( #[$f128_meta] )+ )? fn test_f128() { - type $fty = f128; + #[allow(unused_imports)] + use core::f128::consts; + type Float = f128; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } $test } $( $( #[$const_meta] )+ )? mod const_ { - #[allow(unused)] - use super::TestableFloat; - #[allow(unused)] - use std::num::FpCategory as Fp; - #[allow(unused)] - use std::ops::{Add, Div, Mul, Rem, Sub}; - // Shadow the runtime versions of the macro with const-compatible versions. - #[allow(unused)] - use $crate::floats::{ - assert_approx_eq_const as assert_approx_eq, - assert_biteq_const as assert_biteq, - }; + use super::*; #[test] $( $( #[$f16_const_meta] )+ )? fn test_f16() { - type $fty = f16; + #[allow(unused_imports)] + use core::f16::consts; + type Float = f16; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } const { $test } } #[test] $( $( #[$f32_const_meta] )+ )? fn test_f32() { - type $fty = f32; + #[allow(unused_imports)] + use core::f32::consts; + type Float = f32; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } const { $test } } #[test] $( $( #[$f64_const_meta] )+ )? fn test_f64() { - type $fty = f64; + #[allow(unused_imports)] + use core::f64::consts; + type Float = f64; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } const { $test } } #[test] $( $( #[$f128_const_meta] )+ )? fn test_f128() { - type $fty = f128; + #[allow(unused_imports)] + use core::f128::consts; + type Float = f128; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } const { $test } } } @@ -381,7 +403,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let two: Float = 2.0; let ten: Float = 10.0; assert_biteq!(ten.add(two), ten + two); @@ -399,7 +421,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { let two: Float = 2.0; let ten: Float = 10.0; assert_biteq!(ten.rem(two), ten % two); @@ -412,7 +434,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let nan: Float = Float::NAN; assert!(nan.is_nan()); assert!(!nan.is_infinite()); @@ -432,7 +454,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let inf: Float = Float::INFINITY; assert!(inf.is_infinite()); assert!(!inf.is_finite()); @@ -450,7 +472,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let neg_inf: Float = Float::NEG_INFINITY; assert!(neg_inf.is_infinite()); assert!(!neg_inf.is_finite()); @@ -468,7 +490,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(0.0, Float::ZERO); assert!(!Float::ZERO.is_infinite()); assert!(Float::ZERO.is_finite()); @@ -486,7 +508,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let neg_zero: Float = -0.0; assert!(0.0 == neg_zero); assert_biteq!(-0.0, neg_zero); @@ -506,7 +528,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(1.0, Float::ONE); assert!(!Float::ONE.is_infinite()); assert!(Float::ONE.is_finite()); @@ -524,7 +546,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -545,7 +567,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -566,7 +588,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -587,7 +609,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -608,7 +630,7 @@ const fn flt (x: $fty) -> $fty { x } attrs: { f16: #[cfg(any(miri, target_has_reliable_f16))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -630,26 +652,26 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((0.0 as Float).min(0.0), 0.0); - assert_biteq!((-0.0 as Float).min(-0.0), -0.0); - assert_biteq!((9.0 as Float).min(9.0), 9.0); - assert_biteq!((-9.0 as Float).min(0.0), -9.0); - assert_biteq!((0.0 as Float).min(9.0), 0.0); - assert_biteq!((-0.0 as Float).min(9.0), -0.0); - assert_biteq!((-0.0 as Float).min(-9.0), -9.0); + test { + assert_biteq!(flt(0.0).min(0.0), 0.0); + assert_biteq!(flt(-0.0).min(-0.0), -0.0); + assert_biteq!(flt(9.0).min(9.0), 9.0); + assert_biteq!(flt(-9.0).min(0.0), -9.0); + assert_biteq!(flt(0.0).min(9.0), 0.0); + assert_biteq!(flt(-0.0).min(9.0), -0.0); + assert_biteq!(flt(-0.0).min(-9.0), -9.0); assert_biteq!(Float::INFINITY.min(9.0), 9.0); - assert_biteq!((9.0 as Float).min(Float::INFINITY), 9.0); + assert_biteq!(flt(9.0).min(Float::INFINITY), 9.0); assert_biteq!(Float::INFINITY.min(-9.0), -9.0); - assert_biteq!((-9.0 as Float).min(Float::INFINITY), -9.0); + assert_biteq!(flt(-9.0).min(Float::INFINITY), -9.0); assert_biteq!(Float::NEG_INFINITY.min(9.0), Float::NEG_INFINITY); - assert_biteq!((9.0 as Float).min(Float::NEG_INFINITY), Float::NEG_INFINITY); + assert_biteq!(flt(9.0).min(Float::NEG_INFINITY), Float::NEG_INFINITY); assert_biteq!(Float::NEG_INFINITY.min(-9.0), Float::NEG_INFINITY); - assert_biteq!((-9.0 as Float).min(Float::NEG_INFINITY), Float::NEG_INFINITY); + assert_biteq!(flt(-9.0).min(Float::NEG_INFINITY), Float::NEG_INFINITY); assert_biteq!(Float::NAN.min(9.0), 9.0); assert_biteq!(Float::NAN.min(-9.0), -9.0); - assert_biteq!((9.0 as Float).min(Float::NAN), 9.0); - assert_biteq!((-9.0 as Float).min(Float::NAN), -9.0); + assert_biteq!(flt(9.0).min(Float::NAN), 9.0); + assert_biteq!(flt(-9.0).min(Float::NAN), -9.0); assert!(Float::NAN.min(Float::NAN).is_nan()); } } @@ -660,27 +682,27 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((0.0 as Float).max(0.0), 0.0); - assert_biteq!((-0.0 as Float).max(-0.0), -0.0); - assert_biteq!((9.0 as Float).max(9.0), 9.0); - assert_biteq!((-9.0 as Float).max(0.0), 0.0); - assert_biteq!((-9.0 as Float).max(-0.0), -0.0); - assert_biteq!((0.0 as Float).max(9.0), 9.0); - assert_biteq!((0.0 as Float).max(-9.0), 0.0); - assert_biteq!((-0.0 as Float).max(-9.0), -0.0); + test { + assert_biteq!(flt(0.0).max(0.0), 0.0); + assert_biteq!(flt(-0.0).max(-0.0), -0.0); + assert_biteq!(flt(9.0).max(9.0), 9.0); + assert_biteq!(flt(-9.0).max(0.0), 0.0); + assert_biteq!(flt(-9.0).max(-0.0), -0.0); + assert_biteq!(flt(0.0).max(9.0), 9.0); + assert_biteq!(flt(0.0).max(-9.0), 0.0); + assert_biteq!(flt(-0.0).max(-9.0), -0.0); assert_biteq!(Float::INFINITY.max(9.0), Float::INFINITY); - assert_biteq!((9.0 as Float).max(Float::INFINITY), Float::INFINITY); + assert_biteq!(flt(9.0).max(Float::INFINITY), Float::INFINITY); assert_biteq!(Float::INFINITY.max(-9.0), Float::INFINITY); - assert_biteq!((-9.0 as Float).max(Float::INFINITY), Float::INFINITY); + assert_biteq!(flt(-9.0).max(Float::INFINITY), Float::INFINITY); assert_biteq!(Float::NEG_INFINITY.max(9.0), 9.0); - assert_biteq!((9.0 as Float).max(Float::NEG_INFINITY), 9.0); + assert_biteq!(flt(9.0).max(Float::NEG_INFINITY), 9.0); assert_biteq!(Float::NEG_INFINITY.max(-9.0), -9.0); - assert_biteq!((-9.0 as Float).max(Float::NEG_INFINITY), -9.0); + assert_biteq!(flt(-9.0).max(Float::NEG_INFINITY), -9.0); assert_biteq!(Float::NAN.max(9.0), 9.0); assert_biteq!(Float::NAN.max(-9.0), -9.0); - assert_biteq!((9.0 as Float).max(Float::NAN), 9.0); - assert_biteq!((-9.0 as Float).max(Float::NAN), -9.0); + assert_biteq!(flt(9.0).max(Float::NAN), 9.0); + assert_biteq!(flt(-9.0).max(Float::NAN), -9.0); assert!(Float::NAN.max(Float::NAN).is_nan()); } } @@ -691,27 +713,27 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((0.0 as Float).minimum(0.0), 0.0); - assert_biteq!((-0.0 as Float).minimum(0.0), -0.0); - assert_biteq!((-0.0 as Float).minimum(-0.0), -0.0); - assert_biteq!((9.0 as Float).minimum(9.0), 9.0); - assert_biteq!((-9.0 as Float).minimum(0.0), -9.0); - assert_biteq!((0.0 as Float).minimum(9.0), 0.0); - assert_biteq!((-0.0 as Float).minimum(9.0), -0.0); - assert_biteq!((-0.0 as Float).minimum(-9.0), -9.0); + test { + assert_biteq!(flt(0.0).minimum(0.0), 0.0); + assert_biteq!(flt(-0.0).minimum(0.0), -0.0); + assert_biteq!(flt(-0.0).minimum(-0.0), -0.0); + assert_biteq!(flt(9.0).minimum(9.0), 9.0); + assert_biteq!(flt(-9.0).minimum(0.0), -9.0); + assert_biteq!(flt(0.0).minimum(9.0), 0.0); + assert_biteq!(flt(-0.0).minimum(9.0), -0.0); + assert_biteq!(flt(-0.0).minimum(-9.0), -9.0); assert_biteq!(Float::INFINITY.minimum(9.0), 9.0); - assert_biteq!((9.0 as Float).minimum(Float::INFINITY), 9.0); + assert_biteq!(flt(9.0).minimum(Float::INFINITY), 9.0); assert_biteq!(Float::INFINITY.minimum(-9.0), -9.0); - assert_biteq!((-9.0 as Float).minimum(Float::INFINITY), -9.0); + assert_biteq!(flt(-9.0).minimum(Float::INFINITY), -9.0); assert_biteq!(Float::NEG_INFINITY.minimum(9.0), Float::NEG_INFINITY); - assert_biteq!((9.0 as Float).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY); + assert_biteq!(flt(9.0).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY); assert_biteq!(Float::NEG_INFINITY.minimum(-9.0), Float::NEG_INFINITY); - assert_biteq!((-9.0 as Float).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY); + assert_biteq!(flt(-9.0).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY); assert!(Float::NAN.minimum(9.0).is_nan()); assert!(Float::NAN.minimum(-9.0).is_nan()); - assert!((9.0 as Float).minimum(Float::NAN).is_nan()); - assert!((-9.0 as Float).minimum(Float::NAN).is_nan()); + assert!(flt(9.0).minimum(Float::NAN).is_nan()); + assert!(flt(-9.0).minimum(Float::NAN).is_nan()); assert!(Float::NAN.minimum(Float::NAN).is_nan()); } } @@ -722,28 +744,28 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((0.0 as Float).maximum(0.0), 0.0); - assert_biteq!((-0.0 as Float).maximum(0.0), 0.0); - assert_biteq!((-0.0 as Float).maximum(-0.0), -0.0); - assert_biteq!((9.0 as Float).maximum(9.0), 9.0); - assert_biteq!((-9.0 as Float).maximum(0.0), 0.0); - assert_biteq!((-9.0 as Float).maximum(-0.0), -0.0); - assert_biteq!((0.0 as Float).maximum(9.0), 9.0); - assert_biteq!((0.0 as Float).maximum(-9.0), 0.0); - assert_biteq!((-0.0 as Float).maximum(-9.0), -0.0); + test { + assert_biteq!(flt(0.0).maximum(0.0), 0.0); + assert_biteq!(flt(-0.0).maximum(0.0), 0.0); + assert_biteq!(flt(-0.0).maximum(-0.0), -0.0); + assert_biteq!(flt(9.0).maximum(9.0), 9.0); + assert_biteq!(flt(-9.0).maximum(0.0), 0.0); + assert_biteq!(flt(-9.0).maximum(-0.0), -0.0); + assert_biteq!(flt(0.0).maximum(9.0), 9.0); + assert_biteq!(flt(0.0).maximum(-9.0), 0.0); + assert_biteq!(flt(-0.0).maximum(-9.0), -0.0); assert_biteq!(Float::INFINITY.maximum(9.0), Float::INFINITY); - assert_biteq!((9.0 as Float).maximum(Float::INFINITY), Float::INFINITY); + assert_biteq!(flt(9.0).maximum(Float::INFINITY), Float::INFINITY); assert_biteq!(Float::INFINITY.maximum(-9.0), Float::INFINITY); - assert_biteq!((-9.0 as Float).maximum(Float::INFINITY), Float::INFINITY); + assert_biteq!(flt(-9.0).maximum(Float::INFINITY), Float::INFINITY); assert_biteq!(Float::NEG_INFINITY.maximum(9.0), 9.0); - assert_biteq!((9.0 as Float).maximum(Float::NEG_INFINITY), 9.0); + assert_biteq!(flt(9.0).maximum(Float::NEG_INFINITY), 9.0); assert_biteq!(Float::NEG_INFINITY.maximum(-9.0), -9.0); - assert_biteq!((-9.0 as Float).maximum(Float::NEG_INFINITY), -9.0); + assert_biteq!(flt(-9.0).maximum(Float::NEG_INFINITY), -9.0); assert!(Float::NAN.maximum(9.0).is_nan()); assert!(Float::NAN.maximum(-9.0).is_nan()); - assert!((9.0 as Float).maximum(Float::NAN).is_nan()); - assert!((-9.0 as Float).maximum(Float::NAN).is_nan()); + assert!(flt(9.0).maximum(Float::NAN).is_nan()); + assert!(flt(-9.0).maximum(Float::NAN).is_nan()); assert!(Float::NAN.maximum(Float::NAN).is_nan()); } } @@ -754,16 +776,16 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((0.5 as Float).midpoint(0.5), 0.5); - assert_biteq!((0.5 as Float).midpoint(2.5), 1.5); - assert_biteq!((3.0 as Float).midpoint(4.0), 3.5); - assert_biteq!((-3.0 as Float).midpoint(4.0), 0.5); - assert_biteq!((3.0 as Float).midpoint(-4.0), -0.5); - assert_biteq!((-3.0 as Float).midpoint(-4.0), -3.5); - assert_biteq!((0.0 as Float).midpoint(0.0), 0.0); - assert_biteq!((-0.0 as Float).midpoint(-0.0), -0.0); - assert_biteq!((-5.0 as Float).midpoint(5.0), 0.0); + test { + assert_biteq!(flt(0.5).midpoint(0.5), 0.5); + assert_biteq!(flt(0.5).midpoint(2.5), 1.5); + assert_biteq!(flt(3.0).midpoint(4.0), 3.5); + assert_biteq!(flt(-3.0).midpoint(4.0), 0.5); + assert_biteq!(flt(3.0).midpoint(-4.0), -0.5); + assert_biteq!(flt(-3.0).midpoint(-4.0), -3.5); + assert_biteq!(flt(0.0).midpoint(0.0), 0.0); + assert_biteq!(flt(-0.0).midpoint(-0.0), -0.0); + assert_biteq!(flt(-5.0).midpoint(5.0), 0.0); assert_biteq!(Float::MAX.midpoint(Float::MIN), 0.0); assert_biteq!(Float::MIN.midpoint(Float::MAX), 0.0); assert_biteq!(Float::MAX.midpoint(Float::MIN_POSITIVE), Float::MAX / 2.); @@ -793,7 +815,7 @@ const fn flt (x: $fty) -> $fty { x } assert!(Float::NEG_INFINITY.midpoint(Float::INFINITY).is_nan()); assert!(Float::INFINITY.midpoint(Float::NEG_INFINITY).is_nan()); assert!(Float::NAN.midpoint(1.0).is_nan()); - assert!((1.0 as Float).midpoint(Float::NAN).is_nan()); + assert!(flt(1.0).midpoint(Float::NAN).is_nan()); assert!(Float::NAN.midpoint(Float::NAN).is_nan()); } } @@ -807,7 +829,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { // test if large differences in magnitude are still correctly computed. // NOTE: that because of how small x and y are, x + y can never overflow // so (x + y) / 2.0 is always correct @@ -815,10 +837,10 @@ const fn flt (x: $fty) -> $fty { x } // be safely doubled, while j is significantly smaller. for i in Float::MAX_EXP.saturating_sub(64)..Float::MAX_EXP { for j in 0..64u8 { - let large = (2.0 as Float).powi(i); + let large = flt(2.0).powi(i); // a much smaller number, such that there is no chance of overflow to test // potential double rounding in midpoint's implementation. - let small = (2.0 as Float).powi(Float::MAX_EXP - 1) + let small = flt(2.0).powi(Float::MAX_EXP - 1) * Float::EPSILON * Float::from(j); @@ -837,7 +859,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(Float::INFINITY.abs(), Float::INFINITY); assert_biteq!(Float::ONE.abs(), Float::ONE); assert_biteq!(Float::ZERO.abs(), Float::ZERO); @@ -855,9 +877,9 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((1.0 as Float).copysign(-2.0), -1.0); - assert_biteq!((-1.0 as Float).copysign(2.0), 1.0); + test { + assert_biteq!(flt(1.0).copysign(-2.0), -1.0); + assert_biteq!(flt(-1.0).copysign(2.0), 1.0); assert_biteq!(Float::INFINITY.copysign(-0.0), Float::NEG_INFINITY); assert_biteq!(Float::NEG_INFINITY.copysign(0.0), Float::INFINITY); } @@ -870,10 +892,10 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert!(Float::INFINITY.rem_euclid(42.0 as Float).is_nan()); - assert_biteq!((42.0 as Float).rem_euclid(Float::INFINITY), 42.0 as Float); - assert!((42.0 as Float).rem_euclid(Float::NAN).is_nan()); + test { + assert!(Float::INFINITY.rem_euclid(42.0).is_nan()); + assert_biteq!(flt(42.0).rem_euclid(Float::INFINITY), 42.0); + assert!(flt(42.0).rem_euclid(Float::NAN).is_nan()); assert!(Float::INFINITY.rem_euclid(Float::INFINITY).is_nan()); assert!(Float::INFINITY.rem_euclid(Float::NAN).is_nan()); assert!(Float::NAN.rem_euclid(Float::INFINITY).is_nan()); @@ -887,9 +909,9 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((42.0 as Float).div_euclid(Float::INFINITY), 0.0); - assert!((42.0 as Float).div_euclid(Float::NAN).is_nan()); + test { + assert_biteq!(flt(42.0).div_euclid(Float::INFINITY), 0.0); + assert!(flt(42.0).div_euclid(Float::NAN).is_nan()); assert!(Float::INFINITY.div_euclid(Float::INFINITY).is_nan()); assert!(Float::INFINITY.div_euclid(Float::NAN).is_nan()); assert!(Float::NAN.div_euclid(Float::INFINITY).is_nan()); @@ -902,19 +924,19 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((1.0 as Float).floor(), 1.0); - assert_biteq!((1.3 as Float).floor(), 1.0); - assert_biteq!((1.5 as Float).floor(), 1.0); - assert_biteq!((1.7 as Float).floor(), 1.0); - assert_biteq!((0.5 as Float).floor(), 0.0); - assert_biteq!((0.0 as Float).floor(), 0.0); - assert_biteq!((-0.0 as Float).floor(), -0.0); - assert_biteq!((-0.5 as Float).floor(), -1.0); - assert_biteq!((-1.0 as Float).floor(), -1.0); - assert_biteq!((-1.3 as Float).floor(), -2.0); - assert_biteq!((-1.5 as Float).floor(), -2.0); - assert_biteq!((-1.7 as Float).floor(), -2.0); + test { + assert_biteq!(flt(1.0).floor(), 1.0); + assert_biteq!(flt(1.3).floor(), 1.0); + assert_biteq!(flt(1.5).floor(), 1.0); + assert_biteq!(flt(1.7).floor(), 1.0); + assert_biteq!(flt(0.5).floor(), 0.0); + assert_biteq!(flt(0.0).floor(), 0.0); + assert_biteq!(flt(-0.0).floor(), -0.0); + assert_biteq!(flt(-0.5).floor(), -1.0); + assert_biteq!(flt(-1.0).floor(), -1.0); + assert_biteq!(flt(-1.3).floor(), -2.0); + assert_biteq!(flt(-1.5).floor(), -2.0); + assert_biteq!(flt(-1.7).floor(), -2.0); assert_biteq!(Float::MAX.floor(), Float::MAX); assert_biteq!(Float::MIN.floor(), Float::MIN); assert_biteq!(Float::MIN_POSITIVE.floor(), 0.0); @@ -931,19 +953,19 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((1.0 as Float).ceil(), 1.0); - assert_biteq!((1.3 as Float).ceil(), 2.0); - assert_biteq!((1.5 as Float).ceil(), 2.0); - assert_biteq!((1.7 as Float).ceil(), 2.0); - assert_biteq!((0.5 as Float).ceil(), 1.0); - assert_biteq!((0.0 as Float).ceil(), 0.0); - assert_biteq!((-0.0 as Float).ceil(), -0.0); - assert_biteq!((-0.5 as Float).ceil(), -0.0); - assert_biteq!((-1.0 as Float).ceil(), -1.0); - assert_biteq!((-1.3 as Float).ceil(), -1.0); - assert_biteq!((-1.5 as Float).ceil(), -1.0); - assert_biteq!((-1.7 as Float).ceil(), -1.0); + test { + assert_biteq!(flt(1.0).ceil(), 1.0); + assert_biteq!(flt(1.3).ceil(), 2.0); + assert_biteq!(flt(1.5).ceil(), 2.0); + assert_biteq!(flt(1.7).ceil(), 2.0); + assert_biteq!(flt(0.5).ceil(), 1.0); + assert_biteq!(flt(0.0).ceil(), 0.0); + assert_biteq!(flt(-0.0).ceil(), -0.0); + assert_biteq!(flt(-0.5).ceil(), -0.0); + assert_biteq!(flt(-1.0).ceil(), -1.0); + assert_biteq!(flt(-1.3).ceil(), -1.0); + assert_biteq!(flt(-1.5).ceil(), -1.0); + assert_biteq!(flt(-1.7).ceil(), -1.0); assert_biteq!(Float::MAX.ceil(), Float::MAX); assert_biteq!(Float::MIN.ceil(), Float::MIN); assert_biteq!(Float::MIN_POSITIVE.ceil(), 1.0); @@ -960,20 +982,20 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((2.5 as Float).round(), 3.0); - assert_biteq!((1.0 as Float).round(), 1.0); - assert_biteq!((1.3 as Float).round(), 1.0); - assert_biteq!((1.5 as Float).round(), 2.0); - assert_biteq!((1.7 as Float).round(), 2.0); - assert_biteq!((0.5 as Float).round(), 1.0); - assert_biteq!((0.0 as Float).round(), 0.0); - assert_biteq!((-0.0 as Float).round(), -0.0); - assert_biteq!((-0.5 as Float).round(), -1.0); - assert_biteq!((-1.0 as Float).round(), -1.0); - assert_biteq!((-1.3 as Float).round(), -1.0); - assert_biteq!((-1.5 as Float).round(), -2.0); - assert_biteq!((-1.7 as Float).round(), -2.0); + test { + assert_biteq!(flt(2.5).round(), 3.0); + assert_biteq!(flt(1.0).round(), 1.0); + assert_biteq!(flt(1.3).round(), 1.0); + assert_biteq!(flt(1.5).round(), 2.0); + assert_biteq!(flt(1.7).round(), 2.0); + assert_biteq!(flt(0.5).round(), 1.0); + assert_biteq!(flt(0.0).round(), 0.0); + assert_biteq!(flt(-0.0).round(), -0.0); + assert_biteq!(flt(-0.5).round(), -1.0); + assert_biteq!(flt(-1.0).round(), -1.0); + assert_biteq!(flt(-1.3).round(), -1.0); + assert_biteq!(flt(-1.5).round(), -2.0); + assert_biteq!(flt(-1.7).round(), -2.0); assert_biteq!(Float::MAX.round(), Float::MAX); assert_biteq!(Float::MIN.round(), Float::MIN); assert_biteq!(Float::MIN_POSITIVE.round(), 0.0); @@ -990,20 +1012,20 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((2.5 as Float).round_ties_even(), 2.0); - assert_biteq!((1.0 as Float).round_ties_even(), 1.0); - assert_biteq!((1.3 as Float).round_ties_even(), 1.0); - assert_biteq!((1.5 as Float).round_ties_even(), 2.0); - assert_biteq!((1.7 as Float).round_ties_even(), 2.0); - assert_biteq!((0.5 as Float).round_ties_even(), 0.0); - assert_biteq!((0.0 as Float).round_ties_even(), 0.0); - assert_biteq!((-0.0 as Float).round_ties_even(), -0.0); - assert_biteq!((-0.5 as Float).round_ties_even(), -0.0); - assert_biteq!((-1.0 as Float).round_ties_even(), -1.0); - assert_biteq!((-1.3 as Float).round_ties_even(), -1.0); - assert_biteq!((-1.5 as Float).round_ties_even(), -2.0); - assert_biteq!((-1.7 as Float).round_ties_even(), -2.0); + test { + assert_biteq!(flt(2.5).round_ties_even(), 2.0); + assert_biteq!(flt(1.0).round_ties_even(), 1.0); + assert_biteq!(flt(1.3).round_ties_even(), 1.0); + assert_biteq!(flt(1.5).round_ties_even(), 2.0); + assert_biteq!(flt(1.7).round_ties_even(), 2.0); + assert_biteq!(flt(0.5).round_ties_even(), 0.0); + assert_biteq!(flt(0.0).round_ties_even(), 0.0); + assert_biteq!(flt(-0.0).round_ties_even(), -0.0); + assert_biteq!(flt(-0.5).round_ties_even(), -0.0); + assert_biteq!(flt(-1.0).round_ties_even(), -1.0); + assert_biteq!(flt(-1.3).round_ties_even(), -1.0); + assert_biteq!(flt(-1.5).round_ties_even(), -2.0); + assert_biteq!(flt(-1.7).round_ties_even(), -2.0); assert_biteq!(Float::MAX.round_ties_even(), Float::MAX); assert_biteq!(Float::MIN.round_ties_even(), Float::MIN); assert_biteq!(Float::MIN_POSITIVE.round_ties_even(), 0.0); @@ -1020,19 +1042,19 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((1.0 as Float).trunc(), 1.0); - assert_biteq!((1.3 as Float).trunc(), 1.0); - assert_biteq!((1.5 as Float).trunc(), 1.0); - assert_biteq!((1.7 as Float).trunc(), 1.0); - assert_biteq!((0.5 as Float).trunc(), 0.0); - assert_biteq!((0.0 as Float).trunc(), 0.0); - assert_biteq!((-0.0 as Float).trunc(), -0.0); - assert_biteq!((-0.5 as Float).trunc(), -0.0); - assert_biteq!((-1.0 as Float).trunc(), -1.0); - assert_biteq!((-1.3 as Float).trunc(), -1.0); - assert_biteq!((-1.5 as Float).trunc(), -1.0); - assert_biteq!((-1.7 as Float).trunc(), -1.0); + test { + assert_biteq!(flt(1.0).trunc(), 1.0); + assert_biteq!(flt(1.3).trunc(), 1.0); + assert_biteq!(flt(1.5).trunc(), 1.0); + assert_biteq!(flt(1.7).trunc(), 1.0); + assert_biteq!(flt(0.5).trunc(), 0.0); + assert_biteq!(flt(0.0).trunc(), 0.0); + assert_biteq!(flt(-0.0).trunc(), -0.0); + assert_biteq!(flt(-0.5).trunc(), -0.0); + assert_biteq!(flt(-1.0).trunc(), -1.0); + assert_biteq!(flt(-1.3).trunc(), -1.0); + assert_biteq!(flt(-1.5).trunc(), -1.0); + assert_biteq!(flt(-1.7).trunc(), -1.0); assert_biteq!(Float::MAX.trunc(), Float::MAX); assert_biteq!(Float::MIN.trunc(), Float::MIN); assert_biteq!(Float::MIN_POSITIVE.trunc(), 0.0); @@ -1049,19 +1071,19 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { - assert_biteq!((1.0 as Float).fract(), 0.0); - assert_approx_eq!((1.3 as Float).fract(), 0.3); // rounding differs between float types - assert_biteq!((1.5 as Float).fract(), 0.5); - assert_approx_eq!((1.7 as Float).fract(), 0.7); - assert_biteq!((0.5 as Float).fract(), 0.5); - assert_biteq!((0.0 as Float).fract(), 0.0); - assert_biteq!((-0.0 as Float).fract(), 0.0); - assert_biteq!((-0.5 as Float).fract(), -0.5); - assert_biteq!((-1.0 as Float).fract(), 0.0); - assert_approx_eq!((-1.3 as Float).fract(), -0.3); // rounding differs between float types - assert_biteq!((-1.5 as Float).fract(), -0.5); - assert_approx_eq!((-1.7 as Float).fract(), -0.7); + test { + assert_biteq!(flt(1.0).fract(), 0.0); + assert_approx_eq!(flt(1.3).fract(), 0.3); // rounding differs between float types + assert_biteq!(flt(1.5).fract(), 0.5); + assert_approx_eq!(flt(1.7).fract(), 0.7); + assert_biteq!(flt(0.5).fract(), 0.5); + assert_biteq!(flt(0.0).fract(), 0.0); + assert_biteq!(flt(-0.0).fract(), 0.0); + assert_biteq!(flt(-0.5).fract(), -0.5); + assert_biteq!(flt(-1.0).fract(), 0.0); + assert_approx_eq!(flt(-1.3).fract(), -0.3); // rounding differs between float types + assert_biteq!(flt(-1.5).fract(), -0.5); + assert_approx_eq!(flt(-1.7).fract(), -0.7); assert_biteq!(Float::MAX.fract(), 0.0); assert_biteq!(Float::MIN.fract(), 0.0); assert_biteq!(Float::MIN_POSITIVE.fract(), Float::MIN_POSITIVE); @@ -1080,7 +1102,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(Float::INFINITY.signum(), Float::ONE); assert_biteq!(Float::ONE.signum(), Float::ONE); assert_biteq!(Float::ZERO.signum(), Float::ONE); @@ -1098,7 +1120,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert!(Float::INFINITY.is_sign_positive()); assert!(Float::ONE.is_sign_positive()); assert!(Float::ZERO.is_sign_positive()); @@ -1117,7 +1139,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert!(!Float::INFINITY.is_sign_negative()); assert!(!Float::ONE.is_sign_negative()); assert!(!Float::ZERO.is_sign_negative()); @@ -1136,7 +1158,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(Float::NEG_INFINITY.next_up(), Float::MIN); assert_biteq!(Float::MIN.next_up(), -Float::MAX_DOWN); assert_biteq!((-Float::ONE - Float::EPSILON).next_up(), -Float::ONE); @@ -1167,7 +1189,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(Float::NEG_INFINITY.next_down(), Float::NEG_INFINITY); assert_biteq!(Float::MIN.next_down(), Float::NEG_INFINITY); assert_biteq!((-Float::MAX_DOWN).next_down(), Float::MIN); @@ -1200,7 +1222,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert!(Float::NAN.sqrt().is_nan()); assert!(Float::NEG_INFINITY.sqrt().is_nan()); assert!((-Float::ONE).sqrt().is_nan()); @@ -1220,7 +1242,7 @@ const fn flt (x: $fty) -> $fty { x } f64: #[should_panic], f128: #[should_panic, cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let _ = Float::ONE.clamp(3.0, 1.0); } } @@ -1234,7 +1256,7 @@ const fn flt (x: $fty) -> $fty { x } f64: #[should_panic], f128: #[should_panic, cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let _ = Float::ONE.clamp(Float::NAN, 1.0); } } @@ -1248,7 +1270,7 @@ const fn flt (x: $fty) -> $fty { x } f64: #[should_panic], f128: #[should_panic, cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let _ = Float::ONE.clamp(3.0, Float::NAN); } } @@ -1259,7 +1281,7 @@ const fn flt (x: $fty) -> $fty { x } f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { use core::cmp::Ordering; const fn quiet_bit_mask() -> ::Int { @@ -1364,7 +1386,7 @@ const fn q_nan() -> Float { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { use core::cmp::Ordering; fn quiet_bit_mask() -> ::Int { @@ -1420,15 +1442,15 @@ fn s_nan() -> Float { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; let max: Float = Float::MAX; - assert_biteq!((1.0 as Float).recip(), 1.0); - assert_biteq!((2.0 as Float).recip(), 0.5); - assert_biteq!((-0.4 as Float).recip(), -2.5); - assert_biteq!((0.0 as Float).recip(), inf); + assert_biteq!(flt(1.0).recip(), 1.0); + assert_biteq!(flt(2.0).recip(), 0.5); + assert_biteq!(flt(-0.4).recip(), -2.5); + assert_biteq!(flt(0.0).recip(), inf); assert!(nan.recip().is_nan()); assert_biteq!(inf.recip(), 0.0); assert_biteq!(neg_inf.recip(), -0.0); @@ -1444,38 +1466,337 @@ fn s_nan() -> Float { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; assert_approx_eq!(Float::ONE.powi(1), Float::ONE); - assert_approx_eq!((-3.1 as Float).powi(2), 9.6100000000000005506706202140776519387, Float::POWI_APPROX); - assert_approx_eq!((5.9 as Float).powi(-2), 0.028727377190462507313100483690639638451); - assert_biteq!((8.3 as Float).powi(0), Float::ONE); + assert_approx_eq!(flt(-3.1).powi(2), 9.6100000000000005506706202140776519387, Float::POWI_APPROX); + assert_approx_eq!(flt(5.9).powi(-2), 0.028727377190462507313100483690639638451); + assert_biteq!(flt(8.3).powi(0), Float::ONE); assert!(nan.powi(2).is_nan()); assert_biteq!(inf.powi(3), inf); assert_biteq!(neg_inf.powi(2), inf); } } +float_test! { + name: powf, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let nan: Float = Float::NAN; + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_biteq!(flt(1.0).powf(1.0), 1.0); + assert_approx_eq!(flt(3.4).powf(4.5), 246.40818323761892815995637964326426756, Float::POWF_APPROX); + assert_approx_eq!(flt(2.7).powf(-3.2), 0.041652009108526178281070304373500889273, Float::POWF_APPROX); + assert_approx_eq!(flt(-3.1).powf(2.0), 9.6100000000000005506706202140776519387, Float::POWF_APPROX); + assert_approx_eq!(flt(5.9).powf(-2.0), 0.028727377190462507313100483690639638451, Float::POWF_APPROX); + assert_biteq!(flt(8.3).powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_biteq!(inf.powf(2.0), inf); + assert_biteq!(neg_inf.powf(3.0), neg_inf); + } +} + +float_test! { + name: exp, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_biteq!(1.0, flt(0.0).exp()); + assert_approx_eq!(consts::E, flt(1.0).exp(), Float::EXP_APPROX); + assert_approx_eq!(148.41315910257660342111558004055227962348775, flt(5.0).exp(), Float::EXP_APPROX); + + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + let nan: Float = Float::NAN; + assert_biteq!(inf, inf.exp()); + assert_biteq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); + } +} + +float_test! { + name: exp2, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_approx_eq!(32.0, flt(5.0).exp2(), Float::EXP_APPROX); + assert_biteq!(1.0, flt(0.0).exp2()); + + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + let nan: Float = Float::NAN; + assert_biteq!(inf, inf.exp2()); + assert_biteq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); + } +} + +float_test! { + name: ln, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let nan: Float = Float::NAN; + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_approx_eq!(flt(1.0).exp().ln(), 1.0, Float::LN_APPROX); + assert!(nan.ln().is_nan()); + assert_biteq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!(flt(-2.3).ln().is_nan()); + assert_biteq!(flt(-0.0).ln(), neg_inf); + assert_biteq!(flt(0.0).ln(), neg_inf); + assert_approx_eq!(flt(4.0).ln(), 1.3862943611198906188344642429163531366, Float::LN_APPROX); + } +} + +float_test! { + name: log, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let nan: Float = Float::NAN; + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_approx_eq!(flt(10.0).log(10.0), 1.0, Float::LOG_APPROX); + assert_approx_eq!(flt(2.3).log(3.5), 0.66485771361478710036766645911922010272, Float::LOG_APPROX); + assert_approx_eq!(flt(1.0).exp().log(flt(1.0).exp()), 1.0, Float::LOG_APPROX); + assert!(flt(1.0).log(1.0).is_nan()); + assert!(flt(1.0).log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_biteq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!(flt(-2.3).log(0.1).is_nan()); + assert_biteq!(flt(-0.0).log(2.0), neg_inf); + assert_biteq!(flt(0.0).log(7.0), neg_inf); + } +} + +float_test! { + name: log2, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let nan: Float = Float::NAN; + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_approx_eq!(flt(10.0).log2(), 3.32192809488736234787031942948939017, Float::LOG2_APPROX); + assert_approx_eq!(flt(2.3).log2(), 1.2016338611696504130002982471978765921, Float::LOG2_APPROX); + assert_approx_eq!(flt(1.0).exp().log2(), 1.4426950408889634073599246810018921381, Float::LOG2_APPROX); + assert!(nan.log2().is_nan()); + assert_biteq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!(flt(-2.3).log2().is_nan()); + assert_biteq!(flt(-0.0).log2(), neg_inf); + assert_biteq!(flt(0.0).log2(), neg_inf); + } +} + +float_test! { + name: log10, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let nan: Float = Float::NAN; + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_approx_eq!(flt(10.0).log10(), 1.0, Float::LOG10_APPROX); + assert_approx_eq!(flt(2.3).log10(), 0.36172783601759284532595218865859309898, Float::LOG10_APPROX); + assert_approx_eq!(flt(1.0).exp().log10(), 0.43429448190325182765112891891660508222, Float::LOG10_APPROX); + assert_biteq!(flt(1.0).log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_biteq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!(flt(-2.3).log10().is_nan()); + assert_biteq!(flt(-0.0).log10(), neg_inf); + assert_biteq!(flt(0.0).log10(), neg_inf); + } +} + +float_test! { + name: asinh, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_biteq!(flt(0.0).asinh(), 0.0); + assert_biteq!(flt(-0.0).asinh(), -0.0); + + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + let nan: Float = Float::NAN; + assert_biteq!(inf.asinh(), inf); + assert_biteq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!(flt(-0.0).asinh().is_sign_negative()); + + // issue 63271 + assert_approx_eq!(flt(2.0).asinh(), 1.443635475178810342493276740273105, Float::ASINH_APPROX); + assert_approx_eq!(flt(-2.0).asinh(), -1.443635475178810342493276740273105, Float::ASINH_APPROX); + + assert_approx_eq!(flt(-200.0).asinh(), -5.991470797049389, Float::ASINH_APPROX); + + #[allow(overflowing_literals)] + if Float::MAX > flt(66000.0) { + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!(flt(-67452098.07139316).asinh(), -18.720075426274544393985484294000831757220, Float::ASINH_APPROX); + } + } +} + +float_test! { + name: acosh, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_biteq!(flt(1.0).acosh(), 0.0); + assert!(flt(0.999).acosh().is_nan()); + + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + let nan: Float = Float::NAN; + assert_biteq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(flt(2.0).acosh(), 1.31695789692481670862504634730796844, Float::ACOSH_APPROX); + assert_approx_eq!(flt(3.0).acosh(), 1.76274717403908605046521864995958461, Float::ACOSH_APPROX); + + #[allow(overflowing_literals)] + if Float::MAX > flt(66000.0) { + // test for low accuracy from issue 104548 + assert_approx_eq!(flt(60.0), flt(60.0).cosh().acosh(), Float::ACOSH_APPROX); + } + } +} + +float_test! { + name: atanh, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_biteq!(flt(0.0).atanh(), 0.0); + assert_biteq!(flt(-0.0).atanh(), -0.0); + + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_biteq!(flt(1.0).atanh(), inf); + assert_biteq!(flt(-1.0).atanh(), neg_inf); + + let nan: Float = Float::NAN; + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + + assert_approx_eq!(flt(0.5).atanh(), 0.54930614433405484569762261846126285, Float::ATANH_APPROX); + assert_approx_eq!(flt(-0.5).atanh(), -0.54930614433405484569762261846126285, Float::ATANH_APPROX); + } +} + +float_test! { + name: gamma, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_approx_eq!(flt(1.0).gamma(), 1.0, Float::GAMMA_APPROX); + assert_approx_eq!(flt(2.0).gamma(), 1.0, Float::GAMMA_APPROX); + assert_approx_eq!(flt(3.0).gamma(), 2.0, Float::GAMMA_APPROX); + assert_approx_eq!(flt(4.0).gamma(), 6.0, Float::GAMMA_APPROX); + assert_approx_eq!(flt(5.0).gamma(), 24.0, Float::GAMMA_APPROX_LOOSE); + assert_approx_eq!(flt(0.5).gamma(), consts::PI.sqrt(), Float::GAMMA_APPROX); + assert_approx_eq!(flt(-0.5).gamma(), flt(-2.0) * consts::PI.sqrt(), Float::GAMMA_APPROX_LOOSE); + assert_biteq!(flt(0.0).gamma(), Float::INFINITY); + assert_biteq!(flt(-0.0).gamma(), Float::NEG_INFINITY); + assert!(flt(-1.0).gamma().is_nan()); + assert!(flt(-2.0).gamma().is_nan()); + assert!(Float::NAN.gamma().is_nan()); + assert!(Float::NEG_INFINITY.gamma().is_nan()); + assert_biteq!(Float::INFINITY.gamma(), Float::INFINITY); + + // FIXME: there is a bug in the MinGW gamma implementation that causes this to + // return NaN. https://sourceforge.net/p/mingw-w64/bugs/517/ + if !cfg!(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm"))) { + assert_biteq!(flt(1760.9).gamma(), Float::INFINITY); + } + + if ::BITS <= 64 { + assert_biteq!(flt(171.71).gamma(), Float::INFINITY); + } + } +} + +float_test! { + name: ln_gamma, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_approx_eq!(flt(1.0).ln_gamma().0, 0.0, Float::LNGAMMA_APPROX); + assert_eq!(flt(1.0).ln_gamma().1, 1); + assert_approx_eq!(flt(2.0).ln_gamma().0, 0.0, Float::LNGAMMA_APPROX); + assert_eq!(flt(2.0).ln_gamma().1, 1); + assert_approx_eq!(flt(3.0).ln_gamma().0, flt(2.0).ln(), Float::LNGAMMA_APPROX); + assert_eq!(flt(3.0).ln_gamma().1, 1); + assert_approx_eq!(flt(-0.5).ln_gamma().0, (flt(2.0) * consts::PI.sqrt()).ln(), Float::LNGAMMA_APPROX_LOOSE); + assert_eq!(flt(-0.5).ln_gamma().1, -1); + } +} + float_test! { name: to_degrees, attrs: { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { - let pi: Float = Float::PI; + test { + let pi: Float = consts::PI; let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; - assert_biteq!((0.0 as Float).to_degrees(), 0.0); - assert_approx_eq!((-5.8 as Float).to_degrees(), -332.31552117587745090765431723855668471); + assert_biteq!(flt(0.0).to_degrees(), 0.0); + assert_approx_eq!(flt(-5.8).to_degrees(), -332.31552117587745090765431723855668471); assert_approx_eq!(pi.to_degrees(), 180.0, Float::PI_TO_DEGREES_APPROX); assert!(nan.to_degrees().is_nan()); assert_biteq!(inf.to_degrees(), inf); assert_biteq!(neg_inf.to_degrees(), neg_inf); - assert_biteq!((1.0 as Float).to_degrees(), 57.2957795130823208767981548141051703); + assert_biteq!(flt(1.0).to_degrees(), 57.2957795130823208767981548141051703); } } @@ -1485,15 +1806,15 @@ fn s_nan() -> Float { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { - let pi: Float = Float::PI; + test { + let pi: Float = consts::PI; let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; - assert_biteq!((0.0 as Float).to_radians(), 0.0); - assert_approx_eq!((154.6 as Float).to_radians(), 2.6982790235832334267135442069489767804); - assert_approx_eq!((-332.31 as Float).to_radians(), -5.7999036373023566567593094812182763013); - assert_approx_eq!((180.0 as Float).to_radians(), pi, Float::_180_TO_RADIANS_APPROX); + assert_biteq!(flt(0.0).to_radians(), 0.0); + assert_approx_eq!(flt(154.6).to_radians(), 2.6982790235832334267135442069489767804); + assert_approx_eq!(flt(-332.31).to_radians(), -5.7999036373023566567593094812182763013); + assert_approx_eq!(flt(180.0).to_radians(), pi, Float::_180_TO_RADIANS_APPROX); assert!(nan.to_radians().is_nan()); assert_biteq!(inf.to_radians(), inf); assert_biteq!(neg_inf.to_radians(), neg_inf); @@ -1506,9 +1827,9 @@ fn s_nan() -> Float { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { - let a: Float = 123.0; - let b: Float = 456.0; + test { + let a: Float = flt(123.0); + let b: Float = flt(456.0); // Check that individual operations match their primitive counterparts. // @@ -1530,7 +1851,7 @@ fn s_nan() -> Float { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(flt(1.0), Float::RAW_1); assert_biteq!(flt(12.5), Float::RAW_12_DOT_5); assert_biteq!(flt(1337.0), Float::RAW_1337); @@ -1560,12 +1881,12 @@ fn s_nan() -> Float { f64: #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; - assert_biteq!(flt(12.3).mul_add(4.5, 6.7), Float::MUL_ADD_RESULT); - assert_biteq!((flt(-12.3)).mul_add(-4.5, -6.7), Float::NEG_MUL_ADD_RESULT); + assert_biteq!(flt(12.3).mul_add(flt(4.5), flt(6.7)), Float::MUL_ADD_RESULT); + assert_biteq!((flt(-12.3)).mul_add(flt(-4.5), flt(-6.7)), Float::NEG_MUL_ADD_RESULT); assert_biteq!(flt(0.0).mul_add(8.9, 1.2), 1.2); assert_biteq!(flt(3.4).mul_add(-0.0, 5.6), 5.6); assert!(nan.mul_add(7.8, 9.0).is_nan()); @@ -1582,7 +1903,7 @@ fn s_nan() -> Float { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(Float::from(false), Float::ZERO); assert_biteq!(Float::from(true), Float::ONE); @@ -1603,7 +1924,7 @@ fn s_nan() -> Float { const f16: #[cfg(false)], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(Float::from(u16::MIN), Float::ZERO); assert_biteq!(Float::from(42_u16), 42.0); assert_biteq!(Float::from(u16::MAX), 65535.0); @@ -1622,7 +1943,7 @@ fn s_nan() -> Float { const f32: #[cfg(false)], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(Float::from(u32::MIN), Float::ZERO); assert_biteq!(Float::from(42_u32), 42.0); assert_biteq!(Float::from(u32::MAX), 4294967295.0); @@ -1632,6 +1953,93 @@ fn s_nan() -> Float { } } +// Test the `float_exact_integer_constants` feature +float_test! { + name: max_exact_integer_constant, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + // The maximum integer that converts to a unique floating point + // value. + const MAX_EXACT_INTEGER: ::SInt = Float::MAX_EXACT_INTEGER; + + let max_minus_one = (MAX_EXACT_INTEGER - 1) as Float as ::SInt; + let max_plus_one = (MAX_EXACT_INTEGER + 1) as Float as ::SInt; + let max_plus_two = (MAX_EXACT_INTEGER + 2) as Float as ::SInt; + + // This does an extra round trip back to float for the second operand in + // order to print the results if there is a mismatch + assert_biteq!((MAX_EXACT_INTEGER - 1) as Float, max_minus_one as Float); + assert_biteq!(MAX_EXACT_INTEGER as Float, MAX_EXACT_INTEGER as Float as ::SInt as Float); + assert_biteq!((MAX_EXACT_INTEGER + 1) as Float, max_plus_one as Float); + // The first non-unique conversion, where `max_plus_two` roundtrips to + // `max_plus_one` + assert_biteq!((MAX_EXACT_INTEGER + 1) as Float, (MAX_EXACT_INTEGER + 2) as Float); + assert_biteq!((MAX_EXACT_INTEGER + 2) as Float, max_plus_one as Float); + assert_biteq!((MAX_EXACT_INTEGER + 2) as Float, max_plus_two as Float); + + // Lossless roundtrips, for integers + assert!(MAX_EXACT_INTEGER - 1 == max_minus_one); + assert!(MAX_EXACT_INTEGER == MAX_EXACT_INTEGER as Float as ::SInt); + assert!(MAX_EXACT_INTEGER + 1 == max_plus_one); + // The first non-unique conversion, where `max_plus_two` roundtrips to + // one less than the starting value + assert!(MAX_EXACT_INTEGER + 2 != max_plus_two); + + // max-1 | max+0 | max+1 | max+2 + // After roundtripping, +1 and +2 will equal each other + assert!(max_minus_one != MAX_EXACT_INTEGER); + assert!(MAX_EXACT_INTEGER != max_plus_one); + assert!(max_plus_one == max_plus_two); + } +} + +float_test! { + name: min_exact_integer_constant, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + // The minimum integer that converts to a unique floating point + // value. + const MIN_EXACT_INTEGER: ::SInt = Float::MIN_EXACT_INTEGER; + + // Same logic as the `max` test, but we work our way leftward + // across the number line from (min_exact + 1) to (min_exact - 2). + let min_plus_one = (MIN_EXACT_INTEGER + 1) as Float as ::SInt; + let min_minus_one = (MIN_EXACT_INTEGER - 1) as Float as ::SInt; + let min_minus_two = (MIN_EXACT_INTEGER - 2) as Float as ::SInt; + + // This does an extra round trip back to float for the second operand in + // order to print the results if there is a mismatch + assert_biteq!((MIN_EXACT_INTEGER + 1) as Float, min_plus_one as Float); + assert_biteq!(MIN_EXACT_INTEGER as Float, MIN_EXACT_INTEGER as Float as ::SInt as Float); + assert_biteq!((MIN_EXACT_INTEGER - 1) as Float, min_minus_one as Float); + // The first non-unique conversion, which roundtrips to one + // greater than the starting value. + assert_biteq!((MIN_EXACT_INTEGER - 1) as Float, (MIN_EXACT_INTEGER - 2) as Float); + assert_biteq!((MIN_EXACT_INTEGER - 2) as Float, min_minus_one as Float); + assert_biteq!((MIN_EXACT_INTEGER - 2) as Float, min_minus_two as Float); + + // Lossless roundtrips, for integers + assert!(MIN_EXACT_INTEGER + 1 == min_plus_one); + assert!(MIN_EXACT_INTEGER == MIN_EXACT_INTEGER as Float as ::SInt); + assert!(MIN_EXACT_INTEGER - 1 == min_minus_one); + // The first non-unique conversion, which roundtrips to one + // greater than the starting value. + assert!(MIN_EXACT_INTEGER - 2 != min_minus_two); + + // min-2 | min-1 | min | min+1 + // After roundtripping, -2 and -1 will equal each other. + assert!(min_plus_one != MIN_EXACT_INTEGER); + assert!(MIN_EXACT_INTEGER != min_minus_one); + assert!(min_minus_one == min_minus_two); + } +} + // FIXME(f128): Uncomment and adapt these tests once the From<{u64,i64}> impls are added. // float_test! { // name: from_u64_i64, @@ -1641,7 +2049,7 @@ fn s_nan() -> Float { // f64: #[cfg(false)], // f128: #[cfg(any(miri, target_has_reliable_f128))], // }, -// test { +// 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); @@ -1650,3 +2058,30 @@ fn s_nan() -> Float { // assert_biteq!(Float::from(i64::MAX), 9223372036854775807.0); // } // } + +float_test! { + name: real_consts, + attrs: { + // FIXME(f16_f128): add math tests when available + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let pi: Float = consts::PI; + assert_approx_eq!(consts::FRAC_PI_2, pi / 2.0); + assert_approx_eq!(consts::FRAC_PI_3, pi / 3.0, Float::APPROX); + assert_approx_eq!(consts::FRAC_PI_4, pi / 4.0); + assert_approx_eq!(consts::FRAC_PI_6, pi / 6.0); + assert_approx_eq!(consts::FRAC_PI_8, pi / 8.0); + assert_approx_eq!(consts::FRAC_1_PI, 1.0 / pi); + assert_approx_eq!(consts::FRAC_2_PI, 2.0 / pi); + assert_approx_eq!(consts::FRAC_2_SQRT_PI, 2.0 / pi.sqrt()); + assert_approx_eq!(consts::SQRT_2, flt(2.0).sqrt()); + assert_approx_eq!(consts::FRAC_1_SQRT_2, 1.0 / flt(2.0).sqrt()); + assert_approx_eq!(consts::LOG2_E, consts::E.log2()); + assert_approx_eq!(consts::LOG10_E, consts::E.log10()); + assert_approx_eq!(consts::LN_2, flt(2.0).ln()); + assert_approx_eq!(consts::LN_10, flt(10.0).ln(), Float::APPROX); + } +} diff --git a/library/coretests/tests/intrinsics.rs b/library/coretests/tests/intrinsics.rs index c6d841b8383a..e562f49b0a2f 100644 --- a/library/coretests/tests/intrinsics.rs +++ b/library/coretests/tests/intrinsics.rs @@ -1,6 +1,7 @@ use core::any::TypeId; -use core::intrinsics::{assume, vtable_for}; +use core::intrinsics::assume; use std::fmt::Debug; +use std::intrinsics::type_id_vtable; use std::option::Option; use std::ptr::DynMetadata; @@ -198,15 +199,17 @@ fn carrying_mul_add_fallback_i128() { } #[test] -fn test_vtable_for() { +fn test_type_id_vtable() { #[derive(Debug)] struct A {} struct B {} - const A_VTABLE: Option> = vtable_for::(); + const A_VTABLE: Option> = + type_id_vtable(TypeId::of::(), TypeId::of::()); assert!(A_VTABLE.is_some()); - const B_VTABLE: Option> = vtable_for::(); + const B_VTABLE: Option> = + type_id_vtable(TypeId::of::(), TypeId::of::()); assert!(B_VTABLE.is_none()); } diff --git a/library/coretests/tests/iter/adapters/array_chunks.rs b/library/coretests/tests/iter/adapters/array_chunks.rs index e6e279b14e62..480d3138bdb3 100644 --- a/library/coretests/tests/iter/adapters/array_chunks.rs +++ b/library/coretests/tests/iter/adapters/array_chunks.rs @@ -1,4 +1,4 @@ -use core::iter::{self}; +use core::iter; use super::*; diff --git a/library/coretests/tests/iter/adapters/intersperse.rs b/library/coretests/tests/iter/adapters/intersperse.rs index 72ae59b6b2f5..df6be79308be 100644 --- a/library/coretests/tests/iter/adapters/intersperse.rs +++ b/library/coretests/tests/iter/adapters/intersperse.rs @@ -152,3 +152,203 @@ fn test_try_fold_specialization_intersperse_err() { iter.try_for_each(|item| if item == "b" { None } else { Some(()) }); assert_eq!(iter.next(), None); } + +// FIXME(iter_intersperse): `intersperse` current behavior may change for +// non-fused iterators, so this test will likely have to +// be adjusted; see PR #152855 and issue #79524 +// if `intersperse` doesn't change, remove this FIXME. +#[test] +fn test_non_fused_iterator_intersperse() { + #[derive(Debug)] + struct TestCounter { + counter: usize, + } + + /// Given a counter of 0, this produces: + /// `None` -> `Some(2)` -> `None` -> `Some(4)` -> `None` -> `Some(6)` + /// -> and then `None` endlessly + impl Iterator for TestCounter { + type Item = usize; + fn next(&mut self) -> Option { + if self.counter > 6 { + None + } else if self.counter % 2 == 0 { + self.counter += 1; + None + } else { + self.counter += 1; + Some(self.counter) + } + } + } + + let counter = 0; + // places a 2 between `Some(_)` items + let non_fused_iter = TestCounter { counter }; + let mut intersperse_iter = non_fused_iter.intersperse(2); + // Since `intersperse` currently transforms the original + // iterator into a fused iterator, this intersperse_iter + // should always have `None` + for _ in 0..counter + 6 { + assert_eq!(intersperse_iter.next(), None); + } + + // Extra check to make sure it is `None` after processing 6 items + assert_eq!(intersperse_iter.next(), None); +} + +// FIXME(iter_intersperse): `intersperse` current behavior may change for +// non-fused iterators, so this test will likely have to +// be adjusted; see PR #152855 and issue #79524 +// if `intersperse` doesn't change, remove this FIXME. +#[test] +fn test_non_fused_iterator_intersperse_2() { + #[derive(Debug)] + struct TestCounter { + counter: usize, + } + + // Given a counter of 0, this produces: + // `Some(1)` -> `Some(2)` -> `None` -> `Some(4)` -> `Some(5)` -> + // and then `None` endlessly + impl Iterator for TestCounter { + type Item = usize; + fn next(&mut self) -> Option { + if self.counter < 5 { + self.counter += 1; + if self.counter % 3 != 0 { + return Some(self.counter); + } else { + return None; + } + } + self.counter += 1; + None + } + } + + let counter = 0; + // places a 2 between `Some(_)` items + let non_fused_iter = TestCounter { counter }; + let mut intersperse_iter = non_fused_iter.intersperse(2); + // Since `intersperse` currently transforms the original + // iterator into a fused iterator, this interspersed iter + // will be `Some(1)` -> `Some(2)` -> `Some(2)` -> and then + // `None` endlessly + let mut items_processed = 0; + for num in 0..counter + 6 { + if num < 3 { + if num % 2 != 0 { + assert_eq!(intersperse_iter.next(), Some(2)); + } else { + items_processed += 1; + assert_eq!(intersperse_iter.next(), Some(items_processed)); + } + } else { + assert_eq!(intersperse_iter.next(), None); + } + } + + // Extra check to make sure it is `None` after processing 6 items + assert_eq!(intersperse_iter.next(), None); +} + +// FIXME(iter_intersperse): `intersperse_with` current behavior may change for +// non-fused iterators, so this test will likely have to +// be adjusted; see PR #152855 and issue #79524 +// if `intersperse_with` doesn't change, remove this FIXME. +#[test] +fn test_non_fused_iterator_intersperse_with() { + #[derive(Debug)] + struct TestCounter { + counter: usize, + } + + // Given a counter of 0, this produces: + // `None` -> `Some(2)` -> `None` -> `Some(4)` -> `None` -> `Some(6)` + // -> and then `None` endlessly + impl Iterator for TestCounter { + type Item = usize; + fn next(&mut self) -> Option { + if self.counter > 6 { + None + } else if self.counter % 2 == 0 { + self.counter += 1; + None + } else { + self.counter += 1; + Some(self.counter) + } + } + } + + let counter = 0; + let non_fused_iter = TestCounter { counter }; + // places a 2 between `Some(_)` items + let mut intersperse_iter = non_fused_iter.intersperse_with(|| 2); + // Since `intersperse` currently transforms the original + // iterator into a fused iterator, this intersperse_iter + // should always have `None` + for _ in 0..counter + 6 { + assert_eq!(intersperse_iter.next(), None); + } + + // Extra check to make sure it is `None` after processing 6 items + assert_eq!(intersperse_iter.next(), None); +} + +// FIXME(iter_intersperse): `intersperse_with` current behavior may change for +// non-fused iterators, so this test will likely have to +// be adjusted; see PR #152855 and issue #79524 +// if `intersperse_with` doesn't change, remove this FIXME. +#[test] +fn test_non_fused_iterator_intersperse_with_2() { + #[derive(Debug)] + struct TestCounter { + counter: usize, + } + + // Given a counter of 0, this produces: + // `Some(1)` -> `Some(2)` -> `None` -> `Some(4)` -> `Some(5)` -> + // and then `None` endlessly + impl Iterator for TestCounter { + type Item = usize; + fn next(&mut self) -> Option { + if self.counter < 5 { + self.counter += 1; + if self.counter % 3 != 0 { + return Some(self.counter); + } else { + return None; + } + } + self.counter += 1; + None + } + } + + let counter = 0; + // places a 2 between `Some(_)` items + let non_fused_iter = TestCounter { counter }; + let mut intersperse_iter = non_fused_iter.intersperse(2); + // Since `intersperse` currently transforms the original + // iterator into a fused iterator, this interspersed iter + // will be `Some(1)` -> `Some(2)` -> `Some(2)` -> and then + // `None` endlessly + let mut items_processed = 0; + for num in 0..counter + 6 { + if num < 3 { + if num % 2 != 0 { + assert_eq!(intersperse_iter.next(), Some(2)); + } else { + items_processed += 1; + assert_eq!(intersperse_iter.next(), Some(items_processed)); + } + } else { + assert_eq!(intersperse_iter.next(), None); + } + } + + // Extra check to make sure it is `None` after processing 6 items + assert_eq!(intersperse_iter.next(), None); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index b8702ee20cbb..d04b9a2d6e13 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -1,6 +1,5 @@ // tidy-alphabetical-start #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] -#![cfg_attr(test, feature(cfg_select))] #![feature(array_ptr_get)] #![feature(array_try_from_fn)] #![feature(array_try_map)] @@ -33,7 +32,6 @@ #![feature(const_select_unpredictable)] #![feature(const_trait_impl)] #![feature(const_unsigned_bigint_helpers)] -#![feature(control_flow_ok)] #![feature(core_intrinsics)] #![feature(core_intrinsics_fallbacks)] #![feature(core_io_borrowed_buf)] @@ -52,6 +50,8 @@ #![feature(f16)] #![feature(f128)] #![feature(float_algebraic)] +#![feature(float_exact_integer_constants)] +#![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(flt2dec)] #![feature(fmt_internals)] @@ -90,11 +90,13 @@ #![feature(nonzero_from_str_radix)] #![feature(numfmt)] #![feature(one_sided_range)] +#![feature(panic_internals)] #![feature(pattern)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] #![feature(result_option_map_or_default)] +#![feature(rustc_attrs)] #![feature(signed_bigint_helpers)] #![feature(slice_from_ptr_range)] #![feature(slice_index_methods)] diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs index 193d5416b06a..b95e6e13063f 100644 --- a/library/coretests/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -1,3 +1,5 @@ +mod fn_ptr; +mod trait_info_of; mod type_info; use core::mem::*; diff --git a/library/coretests/tests/mem/fn_ptr.rs b/library/coretests/tests/mem/fn_ptr.rs new file mode 100644 index 000000000000..1d50a2552a19 --- /dev/null +++ b/library/coretests/tests/mem/fn_ptr.rs @@ -0,0 +1,169 @@ +use std::any::TypeId; +use std::mem::type_info::{Abi, FnPtr, Type, TypeKind}; + +const STRING_TY: TypeId = const { TypeId::of::() }; +const U8_TY: TypeId = const { TypeId::of::() }; +const _U8_REF_TY: TypeId = const { TypeId::of::<&u8>() }; +const UNIT_TY: TypeId = const { TypeId::of::<()>() }; + +#[test] +fn test_fn_ptrs() { + let TypeKind::FnPtr(FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: &[], + output, + variadic: false, + }) = (const { Type::of::().kind }) + else { + panic!(); + }; + assert_eq!(output, UNIT_TY); +} +#[test] +fn test_ref() { + const { + // references are tricky because the lifetimes give the references different type ids + // so we check the pointees instead + let TypeKind::FnPtr(FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: &[ty1, ty2], + output, + variadic: false, + }) = (const { Type::of::().kind }) + else { + panic!(); + }; + if output != UNIT_TY { + panic!(); + } + let TypeKind::Reference(reference) = ty1.info().kind else { + panic!(); + }; + if reference.pointee != U8_TY { + panic!(); + } + let TypeKind::Reference(reference) = ty2.info().kind else { + panic!(); + }; + if reference.pointee != U8_TY { + panic!(); + } + } +} + +#[test] +fn test_unsafe() { + let TypeKind::FnPtr(FnPtr { + unsafety: true, + abi: Abi::ExternRust, + inputs: &[], + output, + variadic: false, + }) = (const { Type::of::().kind }) + else { + panic!(); + }; + assert_eq!(output, UNIT_TY); +} +#[test] +fn test_abi() { + let TypeKind::FnPtr(FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: &[], + output, + variadic: false, + }) = (const { Type::of::().kind }) + else { + panic!(); + }; + assert_eq!(output, UNIT_TY); + + let TypeKind::FnPtr(FnPtr { + unsafety: false, + abi: Abi::ExternC, + inputs: &[], + output, + variadic: false, + }) = (const { Type::of::().kind }) + else { + panic!(); + }; + assert_eq!(output, UNIT_TY); + + let TypeKind::FnPtr(FnPtr { + unsafety: true, + abi: Abi::Named("system"), + inputs: &[], + output, + variadic: false, + }) = (const { Type::of::().kind }) + else { + panic!(); + }; + assert_eq!(output, UNIT_TY); +} + +#[test] +fn test_inputs() { + let TypeKind::FnPtr(FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: &[ty1, ty2], + output, + variadic: false, + }) = (const { Type::of::().kind }) + else { + panic!(); + }; + assert_eq!(output, UNIT_TY); + assert_eq!(ty1, STRING_TY); + assert_eq!(ty2, U8_TY); + + let TypeKind::FnPtr(FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: &[ty1, ty2], + output, + variadic: false, + }) = (const { Type::of::().kind }) + else { + panic!(); + }; + assert_eq!(output, UNIT_TY); + assert_eq!(ty1, STRING_TY); + assert_eq!(ty2, U8_TY); +} + +#[test] +fn test_output() { + let TypeKind::FnPtr(FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: &[], + output, + variadic: false, + }) = (const { Type::of:: u8>().kind }) + else { + panic!(); + }; + assert_eq!(output, U8_TY); +} + +#[test] +fn test_variadic() { + let TypeKind::FnPtr(FnPtr { + unsafety: false, + abi: Abi::ExternC, + inputs: [ty1], + output, + variadic: true, + }) = &(const { Type::of::().kind }) + else { + panic!(); + }; + assert_eq!(output, &UNIT_TY); + assert_eq!(*ty1, U8_TY); +} diff --git a/library/coretests/tests/mem/trait_info_of.rs b/library/coretests/tests/mem/trait_info_of.rs new file mode 100644 index 000000000000..c723a9609581 --- /dev/null +++ b/library/coretests/tests/mem/trait_info_of.rs @@ -0,0 +1,70 @@ +use std::any::TypeId; +use std::ptr::DynMetadata; + +struct Garlic(i32); +trait Blah { + fn get_truth(&self) -> i32; +} +impl Blah for Garlic { + fn get_truth(&self) -> i32 { + self.0 * 21 + } +} + +#[test] +fn test_implements_trait() { + const { + assert!(TypeId::of::().trait_info_of::().is_some()); + assert!(TypeId::of::().trait_info_of::().is_some()); + assert!(TypeId::of::<*const Box>().trait_info_of::().is_none()); + assert!(TypeId::of::().trait_info_of_trait_type_id(TypeId::of::()).is_none()); + } +} + +#[test] +fn test_dyn_creation() { + let garlic = Garlic(2); + unsafe { + assert_eq!( + std::ptr::from_raw_parts::( + &raw const garlic, + const { TypeId::of::().trait_info_of::() }.unwrap().get_vtable() + ) + .as_ref() + .unwrap() + .get_truth(), + 42 + ); + } + + assert_eq!( + const { + TypeId::of::() + .trait_info_of_trait_type_id(TypeId::of::()) + .unwrap() + }.get_vtable(), + unsafe { + crate::mem::transmute::<_, DynMetadata<*const ()>>( + const { + TypeId::of::().trait_info_of::() + }.unwrap().get_vtable(), + ) + } + ); +} + +#[test] +fn test_incorrect_use() { + assert_eq!( + const { TypeId::of::().trait_info_of_trait_type_id(TypeId::of::()) }, + None + ); +} + +trait DstTrait {} +impl DstTrait for [i32] {} + +#[test] +fn dst_ice() { + assert!(const { TypeId::of::<[i32]>().trait_info_of::() }.is_none()); +} diff --git a/library/coretests/tests/num/bignum.rs b/library/coretests/tests/num/bignum.rs index 6dfa496e018b..2b4c30cc261c 100644 --- a/library/coretests/tests/num/bignum.rs +++ b/library/coretests/tests/num/bignum.rs @@ -1,5 +1,5 @@ -use core::num::bignum::Big32x40; -use core::num::bignum::tests::Big8x3 as Big; +use core::num::imp::bignum::Big32x40; +use core::num::imp::bignum::tests::Big8x3 as Big; #[test] #[should_panic] diff --git a/library/coretests/tests/num/dec2flt/decimal.rs b/library/coretests/tests/num/dec2flt/decimal.rs index f5ecc604a99a..8611310850be 100644 --- a/library/coretests/tests/num/dec2flt/decimal.rs +++ b/library/coretests/tests/num/dec2flt/decimal.rs @@ -1,4 +1,4 @@ -use core::num::dec2flt::decimal::Decimal; +use core::num::imp::dec2flt::decimal::Decimal; type FPath = ((i64, u64, bool, bool), Option); diff --git a/library/coretests/tests/num/dec2flt/decimal_seq.rs b/library/coretests/tests/num/dec2flt/decimal_seq.rs index f46ba7c465a6..f2fee3a0d8e8 100644 --- a/library/coretests/tests/num/dec2flt/decimal_seq.rs +++ b/library/coretests/tests/num/dec2flt/decimal_seq.rs @@ -1,4 +1,4 @@ -use core::num::dec2flt::decimal_seq::{DecimalSeq, parse_decimal_seq}; +use core::num::imp::dec2flt::decimal_seq::{DecimalSeq, parse_decimal_seq}; #[test] fn test_trim() { diff --git a/library/coretests/tests/num/dec2flt/float.rs b/library/coretests/tests/num/dec2flt/float.rs index 734cb7e4f7db..25e12435b472 100644 --- a/library/coretests/tests/num/dec2flt/float.rs +++ b/library/coretests/tests/num/dec2flt/float.rs @@ -1,4 +1,4 @@ -use core::num::dec2flt::float::RawFloat; +use core::num::imp::dec2flt::float::RawFloat; use crate::num::{ldexp_f32, ldexp_f64}; diff --git a/library/coretests/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs index ba359a0495fe..e4ce533ba44d 100644 --- a/library/coretests/tests/num/dec2flt/lemire.rs +++ b/library/coretests/tests/num/dec2flt/lemire.rs @@ -1,5 +1,7 @@ -use core::num::dec2flt::float::RawFloat; -use core::num::dec2flt::lemire::compute_float; +use core::num::imp::dec2flt; + +use dec2flt::float::RawFloat; +use dec2flt::lemire::compute_float; #[cfg(target_has_reliable_f16)] fn compute_float16(q: i64, w: u64) -> (i32, u64) { diff --git a/library/coretests/tests/num/dec2flt/parse.rs b/library/coretests/tests/num/dec2flt/parse.rs index 65f1289d531b..a63cdb387376 100644 --- a/library/coretests/tests/num/dec2flt/parse.rs +++ b/library/coretests/tests/num/dec2flt/parse.rs @@ -1,6 +1,8 @@ -use core::num::dec2flt::decimal::Decimal; -use core::num::dec2flt::parse::parse_number; -use core::num::dec2flt::{dec2flt, pfe_invalid}; +use core::num::imp::dec2flt; + +use dec2flt::decimal::Decimal; +use dec2flt::parse::parse_number; +use dec2flt::{dec2flt, pfe_invalid}; fn new_dec(e: i64, m: u64) -> Decimal { Decimal { exponent: e, mantissa: m, negative: false, many_digits: false } diff --git a/library/coretests/tests/num/flt2dec/estimator.rs b/library/coretests/tests/num/flt2dec/estimator.rs index f53282611f68..1e24048fa515 100644 --- a/library/coretests/tests/num/flt2dec/estimator.rs +++ b/library/coretests/tests/num/flt2dec/estimator.rs @@ -1,4 +1,4 @@ -use core::num::flt2dec::estimator::*; +use core::num::imp::flt2dec::estimator::*; use crate::num::ldexp_f64; diff --git a/library/coretests/tests/num/flt2dec/mod.rs b/library/coretests/tests/num/flt2dec/mod.rs index be1bc6ac460b..4888d9d1de06 100644 --- a/library/coretests/tests/num/flt2dec/mod.rs +++ b/library/coretests/tests/num/flt2dec/mod.rs @@ -1,11 +1,13 @@ -use core::num::flt2dec::{ +use core::num::imp::flt2dec::{ DecodableFloat, Decoded, FullDecoded, MAX_SIG_DIGITS, Sign, decode, round_up, to_exact_exp_str, to_exact_fixed_str, to_shortest_exp_str, to_shortest_str, }; -use core::num::fmt::{Formatted, Part}; +use core::num::imp::fmt::{Formatted, Part}; use std::mem::MaybeUninit; use std::{fmt, str}; +use Sign::{Minus, MinusPlus}; + use crate::num::{ldexp_f32, ldexp_f64}; mod estimator; @@ -562,8 +564,6 @@ pub fn to_shortest_str_test(mut f_: F) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { - use core::num::flt2dec::Sign::*; - fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String where T: DecodableFloat, @@ -672,8 +672,6 @@ pub fn to_shortest_exp_str_test(mut f_: F) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { - use core::num::flt2dec::Sign::*; - fn to_string(f: &mut F, v: T, sign: Sign, exp_bounds: (i16, i16), upper: bool) -> String where T: DecodableFloat, @@ -789,8 +787,6 @@ pub fn to_exact_exp_str_test(mut f_: F) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), { - use core::num::flt2dec::Sign::*; - fn to_string(f: &mut F, v: T, sign: Sign, ndigits: usize, upper: bool) -> String where T: DecodableFloat, @@ -1065,8 +1061,6 @@ pub fn to_exact_fixed_str_test(mut f_: F) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), { - use core::num::flt2dec::Sign::*; - fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String where T: DecodableFloat, diff --git a/library/coretests/tests/num/flt2dec/random.rs b/library/coretests/tests/num/flt2dec/random.rs index 7386139aaced..e47aa6d37a80 100644 --- a/library/coretests/tests/num/flt2dec/random.rs +++ b/library/coretests/tests/num/flt2dec/random.rs @@ -1,10 +1,11 @@ #![cfg(not(target_arch = "wasm32"))] -use core::num::flt2dec::strategy::grisu::{format_exact_opt, format_shortest_opt}; -use core::num::flt2dec::{DecodableFloat, Decoded, FullDecoded, MAX_SIG_DIGITS, decode}; +use core::num::imp::flt2dec; use std::mem::MaybeUninit; use std::str; +use flt2dec::strategy::grisu::{format_exact_opt, format_shortest_opt}; +use flt2dec::{DecodableFloat, Decoded, FullDecoded, MAX_SIG_DIGITS, decode}; use rand::distr::{Distribution, Uniform}; pub fn decode_finite(v: T) -> Decoded { @@ -159,7 +160,7 @@ pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) #[test] fn shortest_random_equivalence_test() { - use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + use flt2dec::strategy::dragon::format_shortest as fallback; // Miri is too slow let n = if cfg!(miri) { 10 } else { 10_000 }; @@ -174,7 +175,7 @@ fn shortest_random_equivalence_test() { #[cfg(target_has_reliable_f16)] fn shortest_f16_exhaustive_equivalence_test() { // see the f32 version - use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + use flt2dec::strategy::dragon::format_shortest as fallback; f16_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); } @@ -188,7 +189,7 @@ fn shortest_f32_exhaustive_equivalence_test() { // with `--nocapture` (and plenty of time and appropriate rustc flags), this should print: // `done, ignored=17643158 passed=2121451881 failed=0`. - use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + use flt2dec::strategy::dragon::format_shortest as fallback; f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); } @@ -197,14 +198,14 @@ fn shortest_f32_exhaustive_equivalence_test() { fn shortest_f64_hard_random_equivalence_test() { // this again probably has to use appropriate rustc flags. - use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + use flt2dec::strategy::dragon::format_shortest as fallback; f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 100_000_000); } #[test] #[cfg(target_has_reliable_f16)] fn exact_f16_random_equivalence_test() { - use core::num::flt2dec::strategy::dragon::format_exact as fallback; + use flt2dec::strategy::dragon::format_exact as fallback; // Miri is too slow let n = if cfg!(miri) { 3 } else { 1_000 }; @@ -220,7 +221,7 @@ fn exact_f16_random_equivalence_test() { #[test] fn exact_f32_random_equivalence_test() { - use core::num::flt2dec::strategy::dragon::format_exact as fallback; + use flt2dec::strategy::dragon::format_exact as fallback; // Miri is too slow let n = if cfg!(miri) { 3 } else { 1_000 }; @@ -236,7 +237,7 @@ fn exact_f32_random_equivalence_test() { #[test] fn exact_f64_random_equivalence_test() { - use core::num::flt2dec::strategy::dragon::format_exact as fallback; + use flt2dec::strategy::dragon::format_exact as fallback; // Miri is too slow let n = if cfg!(miri) { 2 } else { 1_000 }; diff --git a/library/coretests/tests/num/flt2dec/strategy/dragon.rs b/library/coretests/tests/num/flt2dec/strategy/dragon.rs index 43bb6024f9ce..886f50064a50 100644 --- a/library/coretests/tests/num/flt2dec/strategy/dragon.rs +++ b/library/coretests/tests/num/flt2dec/strategy/dragon.rs @@ -1,5 +1,5 @@ -use core::num::bignum::Big32x40 as Big; -use core::num::flt2dec::strategy::dragon::*; +use core::num::imp::bignum::Big32x40 as Big; +use core::num::imp::flt2dec::strategy::dragon::*; use super::super::*; diff --git a/library/coretests/tests/num/flt2dec/strategy/grisu.rs b/library/coretests/tests/num/flt2dec/strategy/grisu.rs index 117191e0c8fd..835048033338 100644 --- a/library/coretests/tests/num/flt2dec/strategy/grisu.rs +++ b/library/coretests/tests/num/flt2dec/strategy/grisu.rs @@ -1,4 +1,4 @@ -use core::num::flt2dec::strategy::grisu::*; +use core::num::imp::flt2dec::strategy::grisu::*; use super::super::*; diff --git a/library/coretests/tests/time.rs b/library/coretests/tests/time.rs index ff80ff680943..5877f662b7dd 100644 --- a/library/coretests/tests/time.rs +++ b/library/coretests/tests/time.rs @@ -598,3 +598,100 @@ fn from_neg_zero() { assert_eq!(Duration::from_secs_f32(-0.0), Duration::ZERO); assert_eq!(Duration::from_secs_f64(-0.0), Duration::ZERO); } + +#[test] +#[should_panic(expected = "value is either too big or NaN")] +fn duration_fp_mul_nan() { + let _ = Duration::NANOSECOND.mul_f64(f64::NAN); +} + +#[test] +#[should_panic(expected = "value is either too big or NaN")] +fn duration_fp_mul_posinfinity() { + let _ = Duration::NANOSECOND.mul_f64(f64::INFINITY); +} + +#[test] +#[should_panic(expected = "value is negative")] +fn duration_fp_mul_neginfinity() { + let _ = Duration::NANOSECOND.mul_f64(f64::NEG_INFINITY); +} + +#[test] +#[should_panic(expected = "value is either too big or NaN")] +fn duration_fp_div_nan() { + let _ = Duration::NANOSECOND.div_f64(f64::NAN); +} + +#[test] +#[should_panic(expected = "value is either too big or NaN")] +fn duration_fp_div_poszero() { + let _ = Duration::NANOSECOND.div_f64(0.0); +} + +#[test] +#[should_panic(expected = "value is negative")] +fn duration_fp_div_negzero() { + let _ = Duration::NANOSECOND.div_f64(-0.0); +} + +#[test] +#[should_panic(expected = "value is negative")] +fn duration_fp_div_negative() { + let _ = Duration::NANOSECOND.div_f64(f64::MIN); +} + +const TOO_LARGE_FACTOR: f64 = Duration::MAX.as_nanos() as f64; +const TOO_LARGE_DIVISOR: f64 = (Duration::MAX.as_secs_f64() * 2e9).next_up(); +const SMALLEST_DIVISOR: f64 = (TOO_LARGE_DIVISOR.recip() * 2.0).next_up().next_up(); +const SMALLEST_FACTOR: f64 = TOO_LARGE_FACTOR.recip() / 2.0; +const SMALLEST_NEGFACTOR: f64 = (0.0f64.next_down() * 0.5e9).next_up(); + +#[test] +fn duration_fp_boundaries() { + const DURATION_BITS: u32 = Duration::MAX.as_nanos().ilog2() + 1; + const PRECISION: u32 = DURATION_BITS - f64::MANTISSA_DIGITS + 1; + + assert_eq!(Duration::MAX.mul_f64(0.0), Duration::ZERO); + assert_eq!(Duration::MAX.mul_f64(-0.0), Duration::ZERO); + + assert_eq!(Duration::MAX.mul_f64(SMALLEST_FACTOR), Duration::NANOSECOND); + assert_eq!(Duration::MAX.mul_f64(SMALLEST_FACTOR.next_down()), Duration::ZERO); + + assert_eq!(Duration::MAX.div_f64(TOO_LARGE_DIVISOR), Duration::ZERO); + assert_eq!(Duration::MAX.div_f64(TOO_LARGE_DIVISOR.next_down()), Duration::NANOSECOND); + + assert_eq!(Duration::MAX.div_f64(f64::INFINITY), Duration::ZERO); + assert_eq!(Duration::MAX.div_f64(f64::NEG_INFINITY), Duration::ZERO); + + // the following assertions pair with the subsequent (`should_panic`) tests + + assert_eq!(Duration::NANOSECOND.mul_f64(SMALLEST_NEGFACTOR), Duration::ZERO); + + assert!( + Duration::MAX - Duration::NANOSECOND.mul_f64(TOO_LARGE_FACTOR.next_down()) + < Duration::from_nanos(1 << PRECISION) + ); + assert!( + Duration::MAX - Duration::NANOSECOND.div_f64(SMALLEST_DIVISOR) + < Duration::from_nanos(1 << PRECISION) + ); +} + +#[test] +#[should_panic(expected = "value is negative")] +fn duration_fp_mul_negative() { + let _ = Duration::NANOSECOND.mul_f64(SMALLEST_NEGFACTOR.next_down()); +} + +#[test] +#[should_panic(expected = "value is either too big or NaN")] +fn duration_fp_mul_overflow() { + let _ = Duration::NANOSECOND.mul_f64(TOO_LARGE_FACTOR); +} + +#[test] +#[should_panic(expected = "value is either too big or NaN")] +fn duration_fp_div_overflow() { + let _ = Duration::NANOSECOND.div_f64(SMALLEST_DIVISOR.next_down()); +} diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 83f2a3b2c53f..fc0a627d293f 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -14,8 +14,8 @@ #![no_std] #![unstable(feature = "panic_unwind", issue = "32837")] #![doc(issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] +#![cfg_attr(all(target_os = "emscripten", not(emscripten_wasm_eh)), feature(lang_items))] #![feature(cfg_emscripten_wasm_eh)] -#![feature(cfg_select)] #![feature(core_intrinsics)] #![feature(panic_unwind)] #![feature(staged_api)] diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 603adf720789..72f4e2dcdd7c 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -37,14 +37,14 @@ macro_rules! with_api { fn injected_env_var(var: &str) -> Option; fn track_env_var(var: &str, value: Option<&str>); fn track_path(path: &str); - fn literal_from_str(s: &str) -> Result, ()>; + fn literal_from_str(s: &str) -> Result, String>; fn emit_diagnostic(diagnostic: Diagnostic<$Span>); fn ts_drop(stream: $TokenStream); fn ts_clone(stream: &$TokenStream) -> $TokenStream; fn ts_is_empty(stream: &$TokenStream) -> bool; fn ts_expand_expr(stream: &$TokenStream) -> Result<$TokenStream, ()>; - fn ts_from_str(src: &str) -> $TokenStream; + fn ts_from_str(src: &str) -> Result<$TokenStream, String>; fn ts_to_string(stream: &$TokenStream) -> String; fn ts_from_token_tree( tree: TokenTree<$TokenStream, $Span, $Symbol>, diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index e2f39c015bdd..a01bf38a62db 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -110,15 +110,18 @@ impl !Send for TokenStream {} impl !Sync for TokenStream {} /// Error returned from `TokenStream::from_str`. +/// +/// The contained error message is explicitly not guaranteed to be stable in any way, +/// and may change between Rust versions or across compilations. #[stable(feature = "proc_macro_lib", since = "1.15.0")] #[non_exhaustive] #[derive(Debug)] -pub struct LexError; +pub struct LexError(String); #[stable(feature = "proc_macro_lexerror_impls", since = "1.44.0")] impl fmt::Display for LexError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("cannot parse string into token stream") + f.write_str(&self.0) } } @@ -197,7 +200,7 @@ impl FromStr for TokenStream { type Err = LexError; fn from_str(src: &str) -> Result { - Ok(TokenStream(Some(BridgeMethods::ts_from_str(src)))) + Ok(TokenStream(Some(BridgeMethods::ts_from_str(src).map_err(LexError)?))) } } @@ -1594,7 +1597,7 @@ impl FromStr for Literal { fn from_str(src: &str) -> Result { match BridgeMethods::literal_from_str(src) { Ok(literal) => Ok(Literal(literal)), - Err(()) => Err(LexError), + Err(msg) => Err(LexError(msg)), } } } diff --git a/library/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs index 62d247fafb7a..6ee06cce5c63 100644 --- a/library/rtstartup/rsbegin.rs +++ b/library/rtstartup/rsbegin.rs @@ -90,12 +90,12 @@ impl ::Copy for $t {} unsafe extern "C" fn init() { // register unwind info on module startup - __register_frame_info(&__EH_FRAME_BEGIN__ as *const u8, &mut OBJ as *mut _ as *mut u8); + __register_frame_info(&__EH_FRAME_BEGIN__ as *const u8, &raw mut OBJ as *mut u8); } unsafe extern "C" fn uninit() { // unregister on shutdown - __deregister_frame_info(&__EH_FRAME_BEGIN__ as *const u8, &mut OBJ as *mut _ as *mut u8); + __deregister_frame_info(&__EH_FRAME_BEGIN__ as *const u8, &raw mut OBJ as *mut u8); } // MinGW-specific init/uninit routine registration diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index f4cc8edc0c1d..1b7a41d69736 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -146,10 +146,6 @@ harness = false name = "sync" path = "tests/sync/lib.rs" -[[test]] -name = "floats" -path = "tests/floats/lib.rs" - [[test]] name = "thread_local" path = "tests/thread_local/lib.rs" diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index b82beb3b8b2e..fe49660325e6 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -357,6 +357,7 @@ impl HashMap { /// map.insert(1, 2); /// ``` #[inline] + #[must_use] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] pub const fn with_hasher(hash_builder: S) -> HashMap { @@ -389,6 +390,7 @@ pub const fn with_hasher(hash_builder: S) -> HashMap { /// map.insert(1, 2); /// ``` #[inline] + #[must_use] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashMap { HashMap { base: base::HashMap::with_capacity_and_hasher(capacity, hasher) } @@ -409,6 +411,7 @@ impl HashMap { /// The `hash_builder` passed should implement the [`BuildHasher`] trait for /// the `HashMap` to be useful, see its documentation for details. #[inline] + #[must_use] #[unstable(feature = "allocator_api", issue = "32838")] pub fn with_hasher_in(hash_builder: S, alloc: A) -> Self { HashMap { base: base::HashMap::with_hasher_in(hash_builder, alloc) } @@ -430,6 +433,7 @@ pub fn with_hasher_in(hash_builder: S, alloc: A) -> Self { /// the `HashMap` to be useful, see its documentation for details. /// #[inline] + #[must_use] #[unstable(feature = "allocator_api", issue = "32838")] 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) } diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 3f3d601e4d30..0fe26848fe7b 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -229,6 +229,7 @@ impl HashSet { /// set.insert(2); /// ``` #[inline] + #[must_use] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] pub const fn with_hasher(hasher: S) -> HashSet { @@ -261,6 +262,7 @@ pub const fn with_hasher(hasher: S) -> HashSet { /// set.insert(1); /// ``` #[inline] + #[must_use] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet { HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, hasher) } @@ -281,6 +283,7 @@ impl HashSet { /// The `hash_builder` passed should implement the [`BuildHasher`] trait for /// the `HashSet` to be useful, see its documentation for details. #[inline] + #[must_use] #[unstable(feature = "allocator_api", issue = "32838")] pub fn with_hasher_in(hasher: S, alloc: A) -> HashSet { HashSet { base: base::HashSet::with_hasher_in(hasher, alloc) } @@ -301,6 +304,7 @@ pub fn with_hasher_in(hasher: S, alloc: A) -> HashSet { /// The `hash_builder` passed should implement the [`BuildHasher`] trait for /// the `HashSet` to be useful, see its documentation for details. #[inline] + #[must_use] #[unstable(feature = "allocator_api", issue = "32838")] 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) } diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 1f0ced5d0fd0..edf0127a665e 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -670,6 +670,17 @@ pub fn home_dir() -> Option { /// /// On Windows, the behavior is equivalent to that of [`GetTempPath2`][GetTempPath2] / /// [`GetTempPath`][GetTempPath], which this function uses internally. +/// Specifically, for non-SYSTEM processes, the function checks for the +/// following environment variables in order and returns the first path found: +/// +/// 1. The path specified by the `TMP` environment variable. +/// 2. The path specified by the `TEMP` environment variable. +/// 3. The path specified by the `USERPROFILE` environment variable. +/// 4. The Windows directory. +/// +/// When called from a process running as SYSTEM, +/// [`GetTempPath2`][GetTempPath2] returns `C:\Windows\SystemTemp` +/// regardless of environment variables. /// /// Note that, this [may change in the future][changes]. /// @@ -712,28 +723,21 @@ pub fn temp_dir() -> PathBuf { /// /// # Security /// -/// The output of this function should not be trusted for anything -/// that might have security implications. Basically, if users can run -/// the executable, they can change the output arbitrarily. +/// The output of this function must be treated with care to avoid security +/// vulnerabilities, particularly in processes that run with privileges higher +/// than the user, such as setuid or setgid programs. /// -/// As an example, you can easily introduce a race condition. It goes -/// like this: +/// For example, on some Unix platforms, the result is calculated by +/// searching `$PATH` for an executable matching `argv[0]`, but both the +/// environment and arguments can be be set arbitrarily by the user who +/// invokes the program. /// -/// 1. You get the path to the current executable using `current_exe()`, and -/// store it in a variable. -/// 2. Time passes. A malicious actor removes the current executable, and -/// replaces it with a malicious one. -/// 3. You then use the stored path to re-execute the current -/// executable. +/// On Linux, if `fs.secure_hardlinks` is not set, an attacker who can +/// create hardlinks to the executable may be able to cause this function +/// to return an attacker-controlled path, which they later replace with +/// a different program. /// -/// You expected to safely execute the current executable, but you're -/// instead executing something completely different. The code you -/// just executed runs with your privileges. -/// -/// This sort of behavior has been known to [lead to privilege escalation] when -/// used incorrectly. -/// -/// [lead to privilege escalation]: https://securityvulns.com/Wdocument183.html +/// This list of illustrative example attacks is not exhaustive. /// /// # Examples /// diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index cf6f9594c002..885bf376b98a 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2680,7 +2680,7 @@ fn as_inner(&self) -> &fs_imp::DirEntry { /// /// This function will only ever return an error of kind `NotFound` if the given /// path does not exist. Note that the inverse is not true, -/// ie. if a path does not exist, its removal may fail for a number of reasons, +/// i.e. if a path does not exist, its removal may fail for a number of reasons, /// such as insufficient permissions. /// /// # Examples @@ -3150,7 +3150,7 @@ pub fn create_dir_all>(path: P) -> io::Result<()> { /// /// This function will only ever return an error of kind `NotFound` if the given /// path does not exist. Note that the inverse is not true, -/// ie. if a path does not exist, its removal may fail for a number of reasons, +/// i.e. if a path does not exist, its removal may fail for a number of reasons, /// such as insufficient permissions. /// /// # Examples diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 03e3da013cd2..6fcb28edc7d8 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -289,7 +289,6 @@ #![feature(ffi_const)] #![feature(formatting_options)] #![feature(funnel_shifts)] -#![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] @@ -323,7 +322,6 @@ #![feature(bstr)] #![feature(bstr_internals)] #![feature(cast_maybe_uninit)] -#![feature(cfg_select)] #![feature(char_internals)] #![feature(clone_to_uninit)] #![feature(const_convert)] @@ -497,6 +495,8 @@ pub use core::convert; #[stable(feature = "rust1", since = "1.0.0")] pub use core::default; +#[unstable(feature = "field_projections", issue = "145383")] +pub use core::field; #[stable(feature = "futures_api", since = "1.36.0")] pub use core::future; #[stable(feature = "core_hint", since = "1.27.0")] @@ -696,7 +696,7 @@ pub mod arch { #[allow(dead_code, unused_attributes, fuzzy_provenance_casts, unsafe_op_in_unsafe_fn)] mod backtrace_rs; -#[unstable(feature = "cfg_select", issue = "115585")] +#[stable(feature = "cfg_select", since = "1.95.0")] pub use core::cfg_select; #[unstable( feature = "concat_bytes", @@ -726,7 +726,7 @@ pub mod arch { assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, r#try, unimplemented, unreachable, write, writeln, }; -#[stable(feature = "assert_matches", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "assert_matches", since = "1.95.0")] pub use core::{assert_matches, debug_assert_matches}; // Re-export unstable derive macro defined through core. diff --git a/library/std/src/num/f16.rs b/library/std/src/num/f16.rs index 318a0b3af86a..7ca266c8a5f6 100644 --- a/library/std/src/num/f16.rs +++ b/library/std/src/num/f16.rs @@ -16,6 +16,7 @@ use crate::sys::cmath; #[cfg(not(test))] +#[doc(test(attr(allow(unused_features))))] impl f16 { /// Raises a number to a floating point power. /// diff --git a/library/std/src/os/hermit/ffi.rs b/library/std/src/os/hermit/ffi.rs index 19761fd99b40..01a54e1ac8df 100644 --- a/library/std/src/os/hermit/ffi.rs +++ b/library/std/src/os/hermit/ffi.rs @@ -1,4 +1,4 @@ -//! HermitCore-specific extension to the primitives in the `std::ffi` module +//! Hermit-specific extension to the primitives in the `std::ffi` module //! //! # Examples //! diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index fab1b20b8c0e..a739d6ad2a90 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -593,5 +593,5 @@ fn from(fd: OwnedFd) -> process::ChildStderr { #[must_use] #[stable(feature = "unix_ppid", since = "1.27.0")] pub fn parent_id() -> u32 { - crate::sys::os::getppid() + crate::sys::process::getppid() } diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index b32c6cd442ff..ff3ae8145e0f 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -573,7 +573,8 @@ pub fn attribute(self, attribute: usize, value: &'a T) -> Self { /// /// # Example /// - /// ``` + #[cfg_attr(target_vendor = "win7", doc = "```no_run")] + #[cfg_attr(not(target_vendor = "win7"), doc = "```")] /// #![feature(windows_process_extensions_raw_attribute)] /// use std::ffi::c_void; /// use std::os::windows::process::{CommandExt, ProcThreadAttributeList}; diff --git a/library/std/src/path.rs b/library/std/src/path.rs index bf27df7b0428..0b2b5f7e58f4 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -93,7 +93,7 @@ use crate::rc::Rc; use crate::str::FromStr; use crate::sync::Arc; -use crate::sys::path::{HAS_PREFIXES, MAIN_SEP_STR, is_sep_byte, is_verbatim_sep, parse_prefix}; +use crate::sys::path::{HAS_PREFIXES, is_sep_byte, is_verbatim_sep, parse_prefix}; use crate::{cmp, fmt, fs, io, sys}; //////////////////////////////////////////////////////////////////////////////// @@ -266,22 +266,33 @@ fn has_implicit_root(&self) -> bool { /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -pub fn is_separator(c: char) -> bool { +#[rustc_const_unstable(feature = "const_path_separators", issue = "153106")] +pub const fn is_separator(c: char) -> bool { c.is_ascii() && is_sep_byte(c as u8) } -/// The primary separator of path components for the current platform. -/// -/// For example, `/` on Unix and `\` on Windows. +/// All path separators recognized on the current platform, represented as [`char`]s; for example, +/// this is `&['/'][..]` on Unix and `&['\\', '/'][..]` on Windows. The [primary +/// separator](MAIN_SEPARATOR) is always element 0 of the slice. +#[unstable(feature = "const_path_separators", issue = "153106")] +pub const SEPARATORS: &[char] = crate::sys::path::SEPARATORS; + +/// All path separators recognized on the current platform, represented as [`&str`]s; for example, +/// this is `&["/"][..]` on Unix and `&["\\", "/"][..]` on Windows. The [primary +/// separator](MAIN_SEPARATOR_STR) is always element 0 of the slice. +#[unstable(feature = "const_path_separators", issue = "153106")] +pub const SEPARATORS_STR: &[&str] = crate::sys::path::SEPARATORS_STR; + +/// The primary separator of path components for the current platform, represented as a [`char`]; +/// for example, this is `'/'` on Unix and `'\\'` on Windows. #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "path_main_separator")] -pub const MAIN_SEPARATOR: char = crate::sys::path::MAIN_SEP; +pub const MAIN_SEPARATOR: char = SEPARATORS[0]; -/// The primary separator of path components for the current platform. -/// -/// For example, `/` on Unix and `\` on Windows. +/// The primary separator of path components for the current platform, represented as a [`&str`]; +/// for example, this is `"/"` on Unix and `"\\"` on Windows. #[stable(feature = "main_separator_str", since = "1.68.0")] -pub const MAIN_SEPARATOR_STR: &str = crate::sys::path::MAIN_SEP_STR; +pub const MAIN_SEPARATOR_STR: &str = SEPARATORS_STR[0]; //////////////////////////////////////////////////////////////////////////////// // Misc helpers @@ -562,7 +573,7 @@ impl<'a> Component<'a> { pub fn as_os_str(self) -> &'a OsStr { match self { Component::Prefix(p) => p.as_os_str(), - Component::RootDir => OsStr::new(MAIN_SEP_STR), + Component::RootDir => OsStr::new(MAIN_SEPARATOR_STR), Component::CurDir => OsStr::new("."), Component::ParentDir => OsStr::new(".."), Component::Normal(path) => path, @@ -1379,7 +1390,7 @@ fn _push(&mut self, path: &Path) { for c in buf { if need_sep && c != Component::RootDir { - res.push(MAIN_SEP_STR); + res.push(MAIN_SEPARATOR_STR); } res.push(c.as_os_str()); @@ -1402,7 +1413,7 @@ fn _push(&mut self, path: &Path) { // `path` is a pure relative path } else if need_sep { - self.inner.push(MAIN_SEP_STR); + self.inner.push(MAIN_SEPARATOR_STR); } self.inner.push(path); @@ -3308,6 +3319,34 @@ pub fn canonicalize(&self) -> io::Result { fs::canonicalize(self) } + /// Makes the path absolute without accessing the filesystem. + /// + /// This is an alias to [`path::absolute`](absolute). + /// + /// # Errors + /// + /// This function may return an error in the following situations: + /// + /// * If the path is syntactically invalid; in particular, if it is empty. + /// * If getting the [current directory][crate::env::current_dir] fails. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(path_absolute_method)] + /// use std::path::Path; + /// + /// let path = Path::new("foo/./bar"); + /// let absolute = path.absolute()?; + /// assert!(absolute.is_absolute()); + /// # Ok::<(), std::io::Error>(()) + /// ``` + #[unstable(feature = "path_absolute_method", issue = "153328")] + #[inline] + pub fn absolute(&self) -> io::Result { + absolute(self) + } + /// Normalize a path, including `..` without traversing the filesystem. /// /// Returns an error if normalization would leave leading `..` components. diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index af9d28ebad35..ee57e031c959 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -79,7 +79,7 @@ mod panic {} #[doc(no_inline)] pub use self::ambiguous_macros_only::{vec, panic}; -#[unstable(feature = "cfg_select", issue = "115585")] +#[stable(feature = "cfg_select", since = "1.95.0")] #[doc(no_inline)] pub use core::prelude::v1::cfg_select; diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 6838bb422b0e..321b68b3225a 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -156,6 +156,7 @@ target_env = "sgx", target_os = "xous", target_os = "trusty", + target_os = "hermit", )) ))] mod tests; @@ -1380,6 +1381,7 @@ impl Output { /// # Examples /// /// ``` + /// # #![allow(unused_features)] /// #![feature(exit_status_error)] /// # #[cfg(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos")))))] { /// use std::process::Command; @@ -1959,6 +1961,7 @@ impl crate::sealed::Sealed for ExitStatusError {} pub struct ExitStatusError(imp::ExitStatusError); #[unstable(feature = "exit_status_error", issue = "84908")] +#[doc(test(attr(allow(unused_features))))] impl ExitStatusError { /// Reports the exit code, if applicable, from an `ExitStatusError`. /// @@ -2464,7 +2467,7 @@ pub fn wait_with_output(mut self) -> io::Result { #[cfg_attr(not(test), rustc_diagnostic_item = "process_exit")] pub fn exit(code: i32) -> ! { crate::rt::cleanup(); - crate::sys::os::exit(code) + crate::sys::exit::exit(code) } /// Terminates the process in an abnormal fashion. @@ -2545,7 +2548,7 @@ pub fn abort() -> ! { #[must_use] #[stable(feature = "getpid", since = "1.26.0")] pub fn id() -> u32 { - crate::sys::os::getpid() + imp::getpid() } /// A trait for implementing arbitrary return types in the `main` function. diff --git a/library/std/src/random.rs b/library/std/src/random.rs index 3994c5cfaf6f..a18dcf98ec7f 100644 --- a/library/std/src/random.rs +++ b/library/std/src/random.rs @@ -37,7 +37,7 @@ /// Horizon, Cygwin | `getrandom` /// AIX, Hurd, L4Re, QNX | `/dev/urandom` /// Redox | `/scheme/rand` -/// RTEMS | [`arc4random_buf`](https://docs.rtems.org/branches/master/bsp-howto/getentropy.html) +/// RTEMS | [`arc4random_buf`](https://docs.rtems.org/branches/main/bsp-howto/getentropy.html) /// SGX | [`rdrand`](https://en.wikipedia.org/wiki/RDRAND) /// SOLID | `SOLID_RNG_SampleRandomBytes` /// TEEOS | `TEE_GenerateRandom` diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 11c0a0b9daf7..1e7de695ddae 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -187,7 +187,7 @@ fn lang_start_internal( cleanup(); // Guard against multiple threads calling `libc::exit` concurrently. // See the documentation for `unique_thread_exit` for more information. - crate::sys::exit_guard::unique_thread_exit(); + crate::sys::exit::unique_thread_exit(); ret_code }) diff --git a/library/std/src/sys/args/unix.rs b/library/std/src/sys/args/unix.rs index 0dfbd5f03eba..7a592c2b079d 100644 --- a/library/std/src/sys/args/unix.rs +++ b/library/std/src/sys/args/unix.rs @@ -164,7 +164,7 @@ pub fn argc_argv() -> (isize, *const *const c_char) { // of this used `[[NSProcessInfo processInfo] arguments]`. #[cfg(target_vendor = "apple")] mod imp { - use crate::ffi::{c_char, c_int}; + use crate::ffi::c_char; pub unsafe fn init(_argc: isize, _argv: *const *const u8) { // No need to initialize anything in here, `libdyld.dylib` has already @@ -172,12 +172,6 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8) { } pub fn argc_argv() -> (isize, *const *const c_char) { - unsafe extern "C" { - // These functions are in crt_externs.h. - fn _NSGetArgc() -> *mut c_int; - fn _NSGetArgv() -> *mut *mut *mut c_char; - } - // SAFETY: The returned pointer points to a static initialized early // in the program lifetime by `libdyld.dylib`, and as such is always // valid. @@ -187,9 +181,9 @@ pub fn argc_argv() -> (isize, *const *const c_char) { // doesn't exist a lock that we can take. Instead, it is generally // expected that it's only modified in `main` / before other code // runs, so reading this here should be fine. - let argc = unsafe { _NSGetArgc().read() }; + let argc = unsafe { libc::_NSGetArgc().read() }; // SAFETY: Same as above. - let argv = unsafe { _NSGetArgv().read() }; + let argv = unsafe { libc::_NSGetArgv().read() }; // Cast from `*mut *mut c_char` to `*const *const c_char` (argc as isize, argv.cast()) diff --git a/library/std/src/sys/args/xous.rs b/library/std/src/sys/args/xous.rs index 2010bad14d1f..9025ed4f1354 100644 --- a/library/std/src/sys/args/xous.rs +++ b/library/std/src/sys/args/xous.rs @@ -1,9 +1,8 @@ pub use super::common::Args; -use crate::sys::pal::os::get_application_parameters; -use crate::sys::pal::os::params::ArgumentList; +use crate::sys::pal::params::{self, ArgumentList}; pub fn args() -> Args { - let Some(params) = get_application_parameters() else { + let Some(params) = params::get() else { return Args::new(vec![]); }; diff --git a/library/std/src/sys/env/xous.rs b/library/std/src/sys/env/xous.rs index 8f65f30d35fc..f576cfa0025a 100644 --- a/library/std/src/sys/env/xous.rs +++ b/library/std/src/sys/env/xous.rs @@ -2,21 +2,19 @@ use crate::collections::HashMap; use crate::ffi::{OsStr, OsString}; use crate::io; -use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; -use crate::sync::{Mutex, Once}; -use crate::sys::pal::os::{get_application_parameters, params}; +use crate::sync::{Mutex, OnceLock}; +use crate::sys::pal::params; -static ENV: Atomic = AtomicUsize::new(0); -static ENV_INIT: Once = Once::new(); type EnvStore = Mutex>; +static ENV: OnceLock = OnceLock::new(); + fn get_env_store() -> &'static EnvStore { - ENV_INIT.call_once(|| { - let env_store = EnvStore::default(); - if let Some(params) = get_application_parameters() { + ENV.get_or_init(|| { + let mut env_store = HashMap::new(); + if let Some(params) = params::get() { for param in params { if let Ok(envs) = params::EnvironmentBlock::try_from(¶m) { - let mut env_store = env_store.lock().unwrap(); for env in envs { env_store.insert(env.key.into(), env.value.into()); } @@ -24,17 +22,12 @@ fn get_env_store() -> &'static EnvStore { } } } - ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed) - }); - unsafe { &*core::ptr::with_exposed_provenance::(ENV.load(Ordering::Relaxed)) } + Mutex::new(env_store) + }) } pub fn env() -> Env { - let clone_to_vec = |map: &HashMap| -> Vec<_> { - map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() - }; - - let env = clone_to_vec(&*get_env_store().lock().unwrap()); + let env = get_env_store().lock().unwrap().iter().map(|(k, v)| (k.clone(), v.clone())).collect(); Env::new(env) } diff --git a/library/std/src/sys/exit_guard.rs b/library/std/src/sys/exit.rs similarity index 60% rename from library/std/src/sys/exit_guard.rs rename to library/std/src/sys/exit.rs index e7d7a478a5ba..53fb92ba077e 100644 --- a/library/std/src/sys/exit_guard.rs +++ b/library/std/src/sys/exit.rs @@ -19,8 +19,7 @@ /// * If it is called again on the same thread as the first call, it will abort. /// * If it is called again on a different thread, it will wait in a loop /// (waiting for the process to exit). - #[cfg_attr(any(test, doctest), allow(dead_code))] - pub(crate) fn unique_thread_exit() { + pub fn unique_thread_exit() { use crate::ffi::c_int; use crate::ptr; use crate::sync::atomic::AtomicPtr; @@ -62,9 +61,83 @@ pub(crate) fn unique_thread_exit() { /// /// Mitigation is ***NOT*** implemented on this platform, either because this platform /// is not affected, or because mitigation is not yet implemented for this platform. - #[cfg_attr(any(test, doctest), allow(dead_code))] - pub(crate) fn unique_thread_exit() { + #[cfg_attr(any(test, doctest), expect(dead_code))] + pub fn unique_thread_exit() { // Mitigation not required on platforms where `exit` is thread-safe. } } } + +pub fn exit(code: i32) -> ! { + cfg_select! { + target_os = "hermit" => { + unsafe { hermit_abi::exit(code) } + } + target_os = "linux" => { + unsafe { + unique_thread_exit(); + libc::exit(code) + } + } + target_os = "motor" => { + moto_rt::process::exit(code) + } + all(target_vendor = "fortanix", target_env = "sgx") => { + crate::sys::pal::abi::exit_with_code(code as _) + } + target_os = "solid_asp3" => { + rtabort!("exit({}) called", code) + } + target_os = "teeos" => { + let _ = code; + panic!("TA should not call `exit`") + } + target_os = "uefi" => { + use r_efi::base::Status; + + use crate::os::uefi::env; + + if let (Some(boot_services), Some(handle)) = + (env::boot_services(), env::try_image_handle()) + { + let boot_services = boot_services.cast::(); + let _ = unsafe { + ((*boot_services.as_ptr()).exit)( + handle.as_ptr(), + Status::from_usize(code as usize), + 0, + crate::ptr::null_mut(), + ) + }; + } + crate::intrinsics::abort() + } + any( + target_family = "unix", + target_os = "wasi", + ) => { + unsafe { libc::exit(code as crate::ffi::c_int) } + } + target_os = "vexos" => { + let _ = code; + + unsafe { + vex_sdk::vexSystemExitRequest(); + + loop { + vex_sdk::vexTasksRun(); + } + } + } + target_os = "windows" => { + unsafe { crate::sys::pal::c::ExitProcess(code as u32) } + } + target_os = "xous" => { + crate::os::xous::ffi::exit(code as u32) + } + _ => { + let _ = code; + crate::intrinsics::abort() + } + } +} diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index 81083a4fa81d..5f75bcd92421 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -12,8 +12,8 @@ #[path = "unsupported.rs"] mod unsupported_fs; pub use unsupported_fs::{ - DirBuilder, FileTimes, canonicalize, link, readlink, remove_dir_all, rename, rmdir, symlink, - unlink, + Dir, DirBuilder, FileTimes, canonicalize, link, readlink, remove_dir_all, rename, rmdir, + symlink, unlink, }; /// VEXos file descriptor. diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 5436c144d333..5ad23972860b 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -11,7 +11,7 @@ pub mod cmath; pub mod env; pub mod env_consts; -pub mod exit_guard; +pub mod exit; pub mod fd; pub mod fs; pub mod io; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index db64f8d882e2..f10a090a6e91 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -1,7 +1,7 @@ -//! System bindings for HermitCore +//! System bindings for Hermit //! //! This module contains the facade (aka platform-specific) implementations of -//! OS level functionality for HermitCore. +//! OS level functionality for Hermit. //! //! This is all super highly experimental and not actually intended for //! wide/production use yet, it's still all in the experimental category. This @@ -30,7 +30,7 @@ pub fn unsupported() -> io::Result { } pub fn unsupported_err() -> io::Error { - io::const_error!(io::ErrorKind::Unsupported, "operation not supported on HermitCore yet") + io::const_error!(io::ErrorKind::Unsupported, "operation not supported on Hermit yet") } pub fn abort_internal() -> ! { diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 48a7cdcd2f76..188caded55d4 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -1,4 +1,3 @@ -use super::hermit_abi; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; @@ -56,11 +55,3 @@ pub fn temp_dir() -> PathBuf { pub fn home_dir() -> Option { None } - -pub fn exit(code: i32) -> ! { - unsafe { hermit_abi::exit(code) } -} - -pub fn getpid() -> u32 { - unsafe { hermit_abi::getpid() as u32 } -} diff --git a/library/std/src/sys/pal/motor/os.rs b/library/std/src/sys/pal/motor/os.rs index cdf66e3958db..0af579303306 100644 --- a/library/std/src/sys/pal/motor/os.rs +++ b/library/std/src/sys/pal/motor/os.rs @@ -62,11 +62,3 @@ pub fn temp_dir() -> PathBuf { pub fn home_dir() -> Option { None } - -pub fn exit(code: i32) -> ! { - moto_rt::process::exit(code) -} - -pub fn getpid() -> u32 { - panic!("Pids on Motor OS are u64.") -} diff --git a/library/std/src/sys/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs index 1c6c681d4c17..3314f4f3b622 100644 --- a/library/std/src/sys/pal/sgx/abi/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/mod.rs @@ -96,7 +96,7 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64 } } -pub(super) fn exit_with_code(code: isize) -> ! { +pub fn exit_with_code(code: isize) -> ! { if code != 0 { if let Some(mut out) = panic::SgxPanicOutput::new() { let _ = write!(out, "Exited with status code {code}"); diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs index ba47af7ff88d..5b0af37a3d37 100644 --- a/library/std/src/sys/pal/sgx/os.rs +++ b/library/std/src/sys/pal/sgx/os.rs @@ -55,11 +55,3 @@ pub fn temp_dir() -> PathBuf { pub fn home_dir() -> Option { None } - -pub fn exit(code: i32) -> ! { - super::abi::exit_with_code(code as _) -} - -pub fn getpid() -> u32 { - panic!("no pids in SGX") -} diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index c336a1042da4..4a07d240d2e6 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -62,11 +62,3 @@ pub fn temp_dir() -> PathBuf { pub fn home_dir() -> Option { None } - -pub fn exit(code: i32) -> ! { - rtabort!("exit({}) called", code); -} - -pub fn getpid() -> u32 { - panic!("no pids on this platform") -} diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index a4b1d3c6ae67..c09b84f42bab 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -66,11 +66,3 @@ pub fn temp_dir() -> PathBuf { pub fn home_dir() -> Option { None } - -pub fn exit(_code: i32) -> ! { - panic!("TA should not call `exit`") -} - -pub fn getpid() -> u32 { - panic!("no pids on this platform") -} diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 5b9785c8371e..b25be430a3f8 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -1,13 +1,9 @@ -use r_efi::efi::Status; use r_efi::efi::protocols::{device_path, loaded_image_device_path}; use super::{helpers, unsupported_err}; use crate::ffi::{OsStr, OsString}; -use crate::marker::PhantomData; -use crate::os::uefi; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::path::{self, PathBuf}; -use crate::ptr::NonNull; use crate::{fmt, io}; const PATHS_SEP: u16 = b';' as u16; @@ -41,16 +37,37 @@ pub fn chdir(p: &path::Path) -> io::Result<()> { if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } -pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); +pub struct SplitPaths<'a> { + data: crate::os::uefi::ffi::EncodeWide<'a>, + must_yield: bool, +} -pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { - panic!("unsupported") +pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { + SplitPaths { data: unparsed.encode_wide(), must_yield: true } } impl<'a> Iterator for SplitPaths<'a> { type Item = PathBuf; + fn next(&mut self) -> Option { - self.0 + let must_yield = self.must_yield; + self.must_yield = false; + + let mut in_progress = Vec::new(); + for b in self.data.by_ref() { + if b == PATHS_SEP { + self.must_yield = true; + break; + } else { + in_progress.push(b) + } + } + + if !must_yield && in_progress.is_empty() { + None + } else { + Some(PathBuf::from(OsString::from_wide(&in_progress))) + } } } @@ -104,24 +121,3 @@ pub fn temp_dir() -> PathBuf { pub fn home_dir() -> Option { None } - -pub fn exit(code: i32) -> ! { - if let (Some(boot_services), Some(handle)) = - (uefi::env::boot_services(), uefi::env::try_image_handle()) - { - let boot_services: NonNull = boot_services.cast(); - let _ = unsafe { - ((*boot_services.as_ptr()).exit)( - handle.as_ptr(), - Status::from_usize(code as usize), - 0, - crate::ptr::null_mut(), - ) - }; - } - crate::intrinsics::abort() -} - -pub fn getpid() -> u32 { - panic!("no pids on this platform") -} diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index b8280a8f29a0..d11282682d08 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -533,19 +533,6 @@ unsafe fn fallback() -> Option { } } -pub fn exit(code: i32) -> ! { - crate::sys::exit_guard::unique_thread_exit(); - unsafe { libc::exit(code as c_int) } -} - -pub fn getpid() -> u32 { - unsafe { libc::getpid() as u32 } -} - -pub fn getppid() -> u32 { - unsafe { libc::getppid() as u32 } -} - #[cfg(all(target_os = "linux", target_env = "gnu"))] pub fn glibc_version() -> Option<(usize, usize)> { unsafe extern "C" { diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs index cb925ef4348d..fe8addeafd2b 100644 --- a/library/std/src/sys/pal/unsupported/os.rs +++ b/library/std/src/sys/pal/unsupported/os.rs @@ -55,11 +55,3 @@ pub fn temp_dir() -> PathBuf { pub fn home_dir() -> Option { None } - -pub fn exit(_code: i32) -> ! { - crate::intrinsics::abort() -} - -pub fn getpid() -> u32 { - panic!("no pids on this platform") -} diff --git a/library/std/src/sys/pal/vexos/mod.rs b/library/std/src/sys/pal/vexos/mod.rs index 16aa3f088f04..d1380ab8dff1 100644 --- a/library/std/src/sys/pal/vexos/mod.rs +++ b/library/std/src/sys/pal/vexos/mod.rs @@ -1,3 +1,4 @@ +#[path = "../unsupported/os.rs"] pub mod os; #[expect(dead_code)] diff --git a/library/std/src/sys/pal/vexos/os.rs b/library/std/src/sys/pal/vexos/os.rs deleted file mode 100644 index 303b452a078f..000000000000 --- a/library/std/src/sys/pal/vexos/os.rs +++ /dev/null @@ -1,19 +0,0 @@ -#[expect(dead_code)] -#[path = "../unsupported/os.rs"] -mod unsupported_os; -pub use unsupported_os::{ - JoinPathsError, SplitPaths, chdir, current_exe, getcwd, getpid, home_dir, join_paths, - split_paths, temp_dir, -}; - -pub use super::unsupported; - -pub fn exit(_code: i32) -> ! { - unsafe { - vex_sdk::vexSystemExitRequest(); - - loop { - vex_sdk::vexTasksRun(); - } - } -} diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index 285be3ca9fda..c8f3ddf692bc 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -102,14 +102,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - unsafe { libc::exit(code) } -} - -pub fn getpid() -> u32 { - panic!("unsupported"); -} - #[doc(hidden)] pub trait IsMinusOne { fn is_minus_one(&self) -> bool; diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index 3eb6ec827840..30cad2a05683 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -188,11 +188,3 @@ pub fn home_dir() -> Option { .map(PathBuf::from) .or_else(home_dir_crt) } - -pub fn exit(code: i32) -> ! { - unsafe { c::ExitProcess(code as u32) } -} - -pub fn getpid() -> u32 { - unsafe { c::GetCurrentProcessId() } -} diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index 87c99068929c..9b22fb88c998 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -1,7 +1,55 @@ #![forbid(unsafe_op_in_unsafe_fn)] pub mod os; +pub mod params; #[path = "../unsupported/common.rs"] mod common; pub use common::*; + +#[cfg(not(test))] +#[cfg(feature = "panic-unwind")] +mod eh_unwinding { + pub(crate) struct EhFrameFinder; + pub(crate) static mut EH_FRAME_ADDRESS: usize = 0; + pub(crate) static EH_FRAME_SETTINGS: EhFrameFinder = EhFrameFinder; + + unsafe impl unwind::EhFrameFinder for EhFrameFinder { + fn find(&self, _pc: usize) -> Option { + if unsafe { EH_FRAME_ADDRESS == 0 } { + None + } else { + Some(unwind::FrameInfo { + text_base: None, + kind: unwind::FrameInfoKind::EhFrame(unsafe { EH_FRAME_ADDRESS }), + }) + } + } + } +} + +#[cfg(not(test))] +mod c_compat { + use crate::os::xous::ffi::exit; + + unsafe extern "C" { + fn main() -> u32; + } + + #[unsafe(no_mangle)] + pub extern "C" fn abort() { + exit(1); + } + + #[unsafe(no_mangle)] + pub extern "C" fn _start(eh_frame: usize, params: *mut u8) { + #[cfg(feature = "panic-unwind")] + { + unsafe { super::eh_unwinding::EH_FRAME_ADDRESS = eh_frame }; + unwind::set_custom_eh_frame_finder(&super::eh_unwinding::EH_FRAME_SETTINGS).ok(); + } + + unsafe { super::params::set(params) }; + exit(unsafe { main() }); + } +} diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index cd7b7b59d112..fe8addeafd2b 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -2,66 +2,8 @@ use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; -use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::{fmt, io}; -pub(crate) mod params; - -static PARAMS_ADDRESS: Atomic<*mut u8> = AtomicPtr::new(core::ptr::null_mut()); - -#[cfg(not(test))] -#[cfg(feature = "panic-unwind")] -mod eh_unwinding { - pub(crate) struct EhFrameFinder; - pub(crate) static mut EH_FRAME_ADDRESS: usize = 0; - pub(crate) static EH_FRAME_SETTINGS: EhFrameFinder = EhFrameFinder; - - unsafe impl unwind::EhFrameFinder for EhFrameFinder { - fn find(&self, _pc: usize) -> Option { - if unsafe { EH_FRAME_ADDRESS == 0 } { - None - } else { - Some(unwind::FrameInfo { - text_base: None, - kind: unwind::FrameInfoKind::EhFrame(unsafe { EH_FRAME_ADDRESS }), - }) - } - } - } -} - -#[cfg(not(test))] -mod c_compat { - use crate::os::xous::ffi::exit; - unsafe extern "C" { - fn main() -> u32; - } - - #[unsafe(no_mangle)] - pub extern "C" fn abort() { - exit(1); - } - - #[unsafe(no_mangle)] - pub extern "C" fn _start(eh_frame: usize, params_address: usize) { - #[cfg(feature = "panic-unwind")] - { - unsafe { super::eh_unwinding::EH_FRAME_ADDRESS = eh_frame }; - unwind::set_custom_eh_frame_finder(&super::eh_unwinding::EH_FRAME_SETTINGS).ok(); - } - - if params_address != 0 { - let params_address = crate::ptr::with_exposed_provenance_mut::(params_address); - if unsafe { - super::params::ApplicationParameters::new_from_ptr(params_address).is_some() - } { - super::PARAMS_ADDRESS.store(params_address, core::sync::atomic::Ordering::Relaxed); - } - } - exit(unsafe { main() }); - } -} - pub fn getcwd() -> io::Result { unsupported() } @@ -106,11 +48,6 @@ pub fn current_exe() -> io::Result { unsupported() } -pub(crate) fn get_application_parameters() -> Option { - let params_address = PARAMS_ADDRESS.load(Ordering::Relaxed); - unsafe { params::ApplicationParameters::new_from_ptr(params_address) } -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } @@ -118,11 +55,3 @@ pub fn temp_dir() -> PathBuf { pub fn home_dir() -> Option { None } - -pub fn exit(code: i32) -> ! { - crate::os::xous::ffi::exit(code as u32); -} - -pub fn getpid() -> u32 { - panic!("no pids on this platform") -} diff --git a/library/std/src/sys/pal/xous/os/params.rs b/library/std/src/sys/pal/xous/params.rs similarity index 67% rename from library/std/src/sys/pal/xous/os/params.rs rename to library/std/src/sys/pal/xous/params.rs index 0d02cf35477f..d6f671e9a6b6 100644 --- a/library/std/src/sys/pal/xous/os/params.rs +++ b/library/std/src/sys/pal/xous/params.rs @@ -1,75 +1,100 @@ -/// Xous passes a pointer to the parameter block as the second argument. -/// This is used for passing flags such as environment variables. The -/// format of the argument block is: -/// -/// #[repr(C)] -/// struct BlockHeader { -/// /// Magic number that identifies this block. Must be printable ASCII. -/// magic: [u8; 4], -/// -/// /// The size of the data block. Does not include this header. May be 0. -/// size: u32, -/// -/// /// The contents of this block. Varies depending on the block type. -/// data: [u8; 0], -/// } -/// -/// There is a BlockHeader at the start that has magic `AppP`, and the data -/// that follows is the number of blocks present: -/// -/// #[repr(C)] -/// struct ApplicationParameters { -/// magic: b"AppP", -/// size: 4u32, -/// -/// /// The size of the entire application slice, in bytes, including all headers -/// length: u32, -/// -/// /// Number of application parameters present. Must be at least 1 (this block) -/// entries: (parameter_count as u32).to_bytes_le(), -/// } -/// -/// #[repr(C)] -/// struct EnvironmentBlock { -/// magic: b"EnvB", -/// -/// /// Total number of bytes, excluding this header -/// size: 2+data.len(), -/// -/// /// The number of environment variables -/// count: u16, -/// -/// /// Environment variable iteration -/// data: [u8; 0], -/// } -/// -/// Environment variables are present in an `EnvB` block. The `data` section is -/// a sequence of bytes of the form: -/// -/// (u16 /* key_len */; [0u8; key_len as usize] /* key */, -/// u16 /* val_len */ [0u8; val_len as usize]) -/// -/// #[repr(C)] -/// struct ArgumentList { -/// magic: b"ArgL", -/// -/// /// Total number of bytes, excluding this header -/// size: 2+data.len(), -/// -/// /// The number of arguments variables -/// count: u16, -/// -/// /// Argument variable iteration -/// data: [u8; 0], -/// } -/// -/// Args are just an array of strings that represent command line arguments. -/// They are a sequence of the form: -/// -/// (u16 /* val_len */ [0u8; val_len as usize]) -use core::slice; +//! Xous passes a pointer to the parameter block as the second argument. +//! This is used for passing flags such as environment variables. The +//! format of the argument block is: +//! +//! #[repr(C)] +//! struct BlockHeader { +//! /// Magic number that identifies this block. Must be printable ASCII. +//! magic: [u8; 4], +//! +//! /// The size of the data block. Does not include this header. May be 0. +//! size: u32, +//! +//! /// The contents of this block. Varies depending on the block type. +//! data: [u8; 0], +//! } +//! +//! There is a BlockHeader at the start that has magic `AppP`, and the data +//! that follows is the number of blocks present: +//! +//! #[repr(C)] +//! struct ApplicationParameters { +//! magic: b"AppP", +//! size: 4u32, +//! +//! /// The size of the entire application slice, in bytes, including all headers +//! length: u32, +//! +//! /// Number of application parameters present. Must be at least 1 (this block) +//! entries: (parameter_count as u32).to_bytes_le(), +//! } +//! +//! #[repr(C)] +//! struct EnvironmentBlock { +//! magic: b"EnvB", +//! +//! /// Total number of bytes, excluding this header +//! size: 2+data.len(), +//! +//! /// The number of environment variables +//! count: u16, +//! +//! /// Environment variable iteration +//! data: [u8; 0], +//! } +//! +//! Environment variables are present in an `EnvB` block. The `data` section is +//! a sequence of bytes of the form: +//! +//! (u16 /* key_len */; [0u8; key_len as usize] /* key */, +//! u16 /* val_len */ [0u8; val_len as usize]) +//! +//! #[repr(C)] +//! struct ArgumentList { +//! magic: b"ArgL", +//! +//! /// Total number of bytes, excluding this header +//! size: 2+data.len(), +//! +//! /// The number of arguments variables +//! count: u16, +//! +//! /// Argument variable iteration +//! data: [u8; 0], +//! } +//! +//! Args are just an array of strings that represent command line arguments. +//! They are a sequence of the form: +//! +//! (u16 /* val_len */ [0u8; val_len as usize]) use crate::ffi::OsString; +use crate::slice; + +#[cfg(test)] +mod tests; + +static mut PARAMS: *mut u8 = crate::ptr::null_mut(); + +/// Remember the location of the parameter block. +/// +/// # Safety +/// * This function may only be called before `main`. +/// * `data` must either be `null` or point to a valid parameter block. +pub(super) unsafe fn set(data: *mut u8) { + // SAFETY: + // Since this function is called before `main`, there cannot be any threads + // already running. Thus this write cannot race, and all threads will observe + // this write and the parameter block initialization since it happens-before + // their creation. + unsafe { PARAMS = data }; +} + +pub(crate) fn get() -> Option { + // SAFETY: See above. + let data = unsafe { PARAMS }; + unsafe { ApplicationParameters::new_from_ptr(data) } +} /// Magic number indicating we have an environment block const ENV_MAGIC: [u8; 4] = *b"EnvB"; @@ -80,9 +105,6 @@ /// Magic number indicating the loader has passed application parameters const PARAMS_MAGIC: [u8; 4] = *b"AppP"; -#[cfg(test)] -mod tests; - pub(crate) struct ApplicationParameters { data: &'static [u8], offset: usize, @@ -90,7 +112,7 @@ pub(crate) struct ApplicationParameters { } impl ApplicationParameters { - pub(crate) unsafe fn new_from_ptr(data: *const u8) -> Option { + unsafe fn new_from_ptr(data: *const u8) -> Option { if data.is_null() { return None; } diff --git a/library/std/src/sys/pal/xous/os/params/tests.rs b/library/std/src/sys/pal/xous/params/tests.rs similarity index 100% rename from library/std/src/sys/pal/xous/os/params/tests.rs rename to library/std/src/sys/pal/xous/params/tests.rs diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index cb925ef4348d..fe8addeafd2b 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -55,11 +55,3 @@ pub fn temp_dir() -> PathBuf { pub fn home_dir() -> Option { None } - -pub fn exit(_code: i32) -> ! { - crate::intrinsics::abort() -} - -pub fn getpid() -> u32 { - panic!("no pids on this platform") -} diff --git a/library/std/src/sys/path/cygwin.rs b/library/std/src/sys/path/cygwin.rs index 75f0de6beaeb..494dd91f2314 100644 --- a/library/std/src/sys/path/cygwin.rs +++ b/library/std/src/sys/path/cygwin.rs @@ -5,25 +5,20 @@ use crate::sys::helpers::run_path_with_cstr; use crate::{io, ptr}; -#[inline] -pub fn is_sep_byte(b: u8) -> bool { - b == b'/' || b == b'\\' -} +path_separator_bytes!(b'/', b'\\'); /// Cygwin always prefers `/` over `\`, and it always converts all `/` to `\` /// internally when calling Win32 APIs. Therefore, the server component of path /// `\\?\UNC\localhost/share` is `localhost/share` on Win32, but `localhost` /// on Cygwin. #[inline] -pub fn is_verbatim_sep(b: u8) -> bool { - b == b'/' || b == b'\\' +pub const fn is_verbatim_sep(b: u8) -> bool { + is_sep_byte(b) } pub use super::windows_prefix::parse_prefix; pub const HAS_PREFIXES: bool = true; -pub const MAIN_SEP_STR: &str = "/"; -pub const MAIN_SEP: char = '/'; unsafe extern "C" { // Doc: https://cygwin.com/cygwin-api/func-cygwin-conv-path.html diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs index 254683bc83f9..bbb7577e186a 100644 --- a/library/std/src/sys/path/mod.rs +++ b/library/std/src/sys/path/mod.rs @@ -1,3 +1,22 @@ +// There's a lot of necessary redundancy in separator definition. Consolidated into a macro to +// prevent transcription errors. +macro_rules! path_separator_bytes { + ($($sep:literal),+) => ( + pub const SEPARATORS: &[char] = &[$($sep as char,)+]; + pub const SEPARATORS_STR: &[&str] = &[$( + match str::from_utf8(&[$sep]) { + Ok(s) => s, + Err(_) => panic!("path_separator_bytes must be ASCII bytes"), + } + ),+]; + + #[inline] + pub const fn is_sep_byte(b: u8) -> bool { + $(b == $sep) ||+ + } + ) +} + cfg_select! { target_os = "windows" => { mod windows; diff --git a/library/std/src/sys/path/sgx.rs b/library/std/src/sys/path/sgx.rs index dca61f3ea145..9b7ab30a9c65 100644 --- a/library/std/src/sys/path/sgx.rs +++ b/library/std/src/sys/path/sgx.rs @@ -3,14 +3,11 @@ use crate::path::{Path, PathBuf, Prefix}; use crate::sys::unsupported; -#[inline] -pub fn is_sep_byte(b: u8) -> bool { - b == b'/' -} +path_separator_bytes!(b'/'); #[inline] -pub fn is_verbatim_sep(b: u8) -> bool { - b == b'/' +pub const fn is_verbatim_sep(b: u8) -> bool { + is_sep_byte(b) } pub fn parse_prefix(_: &OsStr) -> Option> { @@ -18,8 +15,6 @@ pub fn parse_prefix(_: &OsStr) -> Option> { } pub const HAS_PREFIXES: bool = false; -pub const MAIN_SEP_STR: &str = "/"; -pub const MAIN_SEP: char = '/'; pub(crate) fn absolute(_path: &Path) -> io::Result { unsupported() diff --git a/library/std/src/sys/path/uefi.rs b/library/std/src/sys/path/uefi.rs index 84d634af93cf..8c911ee5f9c5 100644 --- a/library/std/src/sys/path/uefi.rs +++ b/library/std/src/sys/path/uefi.rs @@ -5,17 +5,14 @@ use crate::sys::pal::helpers; use crate::sys::unsupported_err; +path_separator_bytes!(b'\\'); + const FORWARD_SLASH: u8 = b'/'; const COLON: u8 = b':'; #[inline] -pub fn is_sep_byte(b: u8) -> bool { - b == b'\\' -} - -#[inline] -pub fn is_verbatim_sep(b: u8) -> bool { - b == b'\\' +pub const fn is_verbatim_sep(b: u8) -> bool { + is_sep_byte(b) } pub fn parse_prefix(_: &OsStr) -> Option> { @@ -23,8 +20,6 @@ pub fn parse_prefix(_: &OsStr) -> Option> { } pub const HAS_PREFIXES: bool = true; -pub const MAIN_SEP_STR: &str = "\\"; -pub const MAIN_SEP: char = '\\'; /// UEFI paths can be of 4 types: /// diff --git a/library/std/src/sys/path/unix.rs b/library/std/src/sys/path/unix.rs index 611d250db405..b49c39a9253f 100644 --- a/library/std/src/sys/path/unix.rs +++ b/library/std/src/sys/path/unix.rs @@ -2,14 +2,11 @@ use crate::path::{Path, PathBuf, Prefix}; use crate::{env, io}; -#[inline] -pub fn is_sep_byte(b: u8) -> bool { - b == b'/' -} +path_separator_bytes!(b'/'); #[inline] -pub fn is_verbatim_sep(b: u8) -> bool { - b == b'/' +pub const fn is_verbatim_sep(b: u8) -> bool { + is_sep_byte(b) } #[inline] @@ -18,8 +15,6 @@ pub fn parse_prefix(_: &OsStr) -> Option> { } pub const HAS_PREFIXES: bool = false; -pub const MAIN_SEP_STR: &str = "/"; -pub const MAIN_SEP: char = '/'; /// Make a POSIX path absolute without changing its semantics. pub(crate) fn absolute(path: &Path) -> io::Result { diff --git a/library/std/src/sys/path/unsupported_backslash.rs b/library/std/src/sys/path/unsupported_backslash.rs index 8ed1fdc36e23..ce1851e93839 100644 --- a/library/std/src/sys/path/unsupported_backslash.rs +++ b/library/std/src/sys/path/unsupported_backslash.rs @@ -4,14 +4,11 @@ use crate::path::{Path, PathBuf, Prefix}; use crate::sys::unsupported; -#[inline] -pub fn is_sep_byte(b: u8) -> bool { - b == b'\\' -} +path_separator_bytes!(b'\\'); #[inline] -pub fn is_verbatim_sep(b: u8) -> bool { - b == b'\\' +pub const fn is_verbatim_sep(b: u8) -> bool { + is_sep_byte(b) } pub fn parse_prefix(_: &OsStr) -> Option> { @@ -19,8 +16,6 @@ pub fn parse_prefix(_: &OsStr) -> Option> { } pub const HAS_PREFIXES: bool = true; -pub const MAIN_SEP_STR: &str = "\\"; -pub const MAIN_SEP: char = '\\'; pub(crate) fn absolute(_path: &Path) -> io::Result { unsupported() diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs index 509b13b6ae81..1c7bf50d1907 100644 --- a/library/std/src/sys/path/windows.rs +++ b/library/std/src/sys/path/windows.rs @@ -9,9 +9,9 @@ pub use super::windows_prefix::parse_prefix; +path_separator_bytes!(b'\\', b'/'); + pub const HAS_PREFIXES: bool = true; -pub const MAIN_SEP_STR: &str = "\\"; -pub const MAIN_SEP: char = '\\'; /// A null terminated wide string. #[repr(transparent)] @@ -48,12 +48,7 @@ pub fn with_native_path(path: &Path, f: &dyn Fn(&WCStr) -> io::Result) -> } #[inline] -pub fn is_sep_byte(b: u8) -> bool { - b == b'/' || b == b'\\' -} - -#[inline] -pub fn is_verbatim_sep(b: u8) -> bool { +pub const fn is_verbatim_sep(b: u8) -> bool { b == b'\\' } diff --git a/library/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs index 121d3bc9d5c3..46f4ebf6db42 100644 --- a/library/std/src/sys/process/mod.rs +++ b/library/std/src/sys/process/mod.rs @@ -27,9 +27,11 @@ mod env; pub use env::CommandEnvs; +#[cfg(target_family = "unix")] +pub use imp::getppid; pub use imp::{ ChildPipe, Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio, - read_output, + getpid, read_output, }; #[cfg(any( diff --git a/library/std/src/sys/process/motor.rs b/library/std/src/sys/process/motor.rs index a5d018447890..133633f7bc67 100644 --- a/library/std/src/sys/process/motor.rs +++ b/library/std/src/sys/process/motor.rs @@ -327,3 +327,7 @@ pub fn read_output( ) -> io::Result<()> { Err(io::Error::from_raw_os_error(moto_rt::E_NOT_IMPLEMENTED.into())) } + +pub fn getpid() -> u32 { + panic!("Pids on Motor OS are u64.") +} diff --git a/library/std/src/sys/process/uefi.rs b/library/std/src/sys/process/uefi.rs index 31914aeb67c5..88dd4c899b37 100644 --- a/library/std/src/sys/process/uefi.rs +++ b/library/std/src/sys/process/uefi.rs @@ -900,3 +900,7 @@ fn env_changes(env: &CommandEnv) -> Option, O Some(result) } + +pub fn getpid() -> u32 { + panic!("no pids on this platform") +} diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index f6bbfed61ef3..2d83782b7d0b 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -679,3 +679,11 @@ fn read(fd: &FileDesc, dst: &mut Vec) -> Result { } } } + +pub fn getpid() -> u32 { + unsafe { libc::getpid() as u32 } +} + +pub fn getppid() -> u32 { + unsafe { libc::getppid() as u32 } +} diff --git a/library/std/src/sys/process/unix/mod.rs b/library/std/src/sys/process/unix/mod.rs index 1938e8f4b737..dafe7c8c76da 100644 --- a/library/std/src/sys/process/unix/mod.rs +++ b/library/std/src/sys/process/unix/mod.rs @@ -23,5 +23,7 @@ pub use imp::{ExitStatus, ExitStatusError, Process}; -pub use self::common::{ChildPipe, Command, CommandArgs, ExitCode, Stdio, read_output}; +pub use self::common::{ + ChildPipe, Command, CommandArgs, ExitCode, Stdio, getpid, getppid, read_output, +}; pub use crate::ffi::OsString as EnvKey; diff --git a/library/std/src/sys/process/unsupported.rs b/library/std/src/sys/process/unsupported.rs index 455c38e55b7b..9ed66a559117 100644 --- a/library/std/src/sys/process/unsupported.rs +++ b/library/std/src/sys/process/unsupported.rs @@ -327,3 +327,7 @@ pub fn read_output( ) -> io::Result<()> { match out.diverge() {} } + +pub fn getpid() -> u32 { + panic!("no pids on this platform") +} diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index b40833ad212c..deb4243d314e 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -976,3 +976,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.iter.clone()).finish() } } + +pub fn getpid() -> u32 { + unsafe { c::GetCurrentProcessId() } +} diff --git a/library/std/src/sys/sync/once/mod.rs b/library/std/src/sys/sync/once/mod.rs index aeea884b9f61..5796c6d207db 100644 --- a/library/std/src/sys/sync/once/mod.rs +++ b/library/std/src/sys/sync/once/mod.rs @@ -12,7 +12,7 @@ all(target_os = "windows", not(target_vendor="win7")), target_os = "linux", target_os = "android", - all(target_arch = "wasm32", target_feature = "atomics"), + all(target_family = "wasm", target_feature = "atomics"), target_os = "freebsd", target_os = "motor", target_os = "openbsd", diff --git a/library/std/src/sys/sync/thread_parking/mod.rs b/library/std/src/sys/sync/thread_parking/mod.rs index 74b5b72b19a7..9d5a0a996b11 100644 --- a/library/std/src/sys/sync/thread_parking/mod.rs +++ b/library/std/src/sys/sync/thread_parking/mod.rs @@ -3,7 +3,7 @@ all(target_os = "windows", not(target_vendor = "win7")), target_os = "linux", target_os = "android", - all(target_arch = "wasm32", target_feature = "atomics"), + all(target_family = "wasm", target_feature = "atomics"), target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly", diff --git a/library/std/src/thread/functions.rs b/library/std/src/thread/functions.rs index 95d7aaf51840..70642108e1e0 100644 --- a/library/std/src/thread/functions.rs +++ b/library/std/src/thread/functions.rs @@ -168,7 +168,11 @@ pub fn yield_now() { imp::yield_now() } -/// Determines whether the current thread is unwinding because of panic. +/// Determines whether the current thread is panicking. +/// +/// This returns `true` both when the thread is unwinding due to a panic, +/// or executing a panic hook. Note that the latter case will still happen +/// when `panic=abort` is set. /// /// A common use of this feature is to poison shared resources when writing /// unsafe code, by checking `panicking` when the `drop` is called. @@ -309,14 +313,14 @@ pub fn sleep(dur: Duration) { /// /// | Platform | System call | /// |-----------|----------------------------------------------------------------------| -/// | Linux | [clock_nanosleep] (Monotonic clock) | -/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock)] | -/// | Android | [clock_nanosleep] (Monotonic Clock)] | -/// | Solaris | [clock_nanosleep] (Monotonic Clock)] | -/// | Illumos | [clock_nanosleep] (Monotonic Clock)] | -/// | Dragonfly | [clock_nanosleep] (Monotonic Clock)] | -/// | Hurd | [clock_nanosleep] (Monotonic Clock)] | -/// | Vxworks | [clock_nanosleep] (Monotonic Clock)] | +/// | Linux | [clock_nanosleep] (Monotonic Clock) | +/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock) | +/// | Android | [clock_nanosleep] (Monotonic Clock) | +/// | Solaris | [clock_nanosleep] (Monotonic Clock) | +/// | Illumos | [clock_nanosleep] (Monotonic Clock) | +/// | Dragonfly | [clock_nanosleep] (Monotonic Clock) | +/// | Hurd | [clock_nanosleep] (Monotonic Clock) | +/// | Vxworks | [clock_nanosleep] (Monotonic Clock) | /// | Apple | `mach_wait_until` | /// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself | /// diff --git a/library/std/tests/env.rs b/library/std/tests/env.rs index b53fd69b7070..9d624d5592ce 100644 --- a/library/std/tests/env.rs +++ b/library/std/tests/env.rs @@ -59,6 +59,23 @@ fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"])); } +#[test] +#[cfg(target_os = "uefi")] +fn split_paths_uefi() { + use std::path::PathBuf; + + fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { + split_paths(unparsed).collect::>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() + } + + assert!(check_parse("", &mut [""])); + assert!(check_parse(";;", &mut ["", "", ""])); + assert!(check_parse(r"fs0:\", &mut [r"fs0:\"])); + assert!(check_parse(r"fs0:\;", &mut [r"fs0:\", ""])); + assert!(check_parse(r"fs0:\;fs0:\boot\", &mut [r"fs0:\", r"fs0:\boot\"])); +} + #[test] #[cfg(unix)] fn join_paths_unix() { diff --git a/library/std/tests/env_modify.rs b/library/std/tests/env_modify.rs index 4cd87bf7a002..3404ca537acc 100644 --- a/library/std/tests/env_modify.rs +++ b/library/std/tests/env_modify.rs @@ -1,6 +1,5 @@ // These tests are in a separate integration test as they modify the environment, // and would otherwise cause some other tests to fail. -#![feature(cfg_select)] use std::env::*; use std::ffi::{OsStr, OsString}; diff --git a/library/std/tests/floats/f128.rs b/library/std/tests/floats/f128.rs deleted file mode 100644 index d20762023caf..000000000000 --- a/library/std/tests/floats/f128.rs +++ /dev/null @@ -1,320 +0,0 @@ -#![cfg(target_has_reliable_f128)] - -use std::f128::consts; -use std::ops::{Add, Div, Mul, Sub}; - -// Note these tolerances make sense around zero, but not for more extreme exponents. - -/// Default tolerances. Works for values that should be near precise but not exact. Roughly -/// the precision carried by `100 * 100`. -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -const TOL: f128 = 1e-12; - -/// For operations that are near exact, usually not involving math of different -/// signs. -const TOL_PRECISE: f128 = 1e-28; - -/// Tolerances for math that is allowed to be imprecise, usually due to multiple chained -/// operations. -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -const TOL_IMPR: f128 = 1e-10; - -/// Compare by representation -#[allow(unused_macros)] -macro_rules! assert_f128_biteq { - ($a:expr, $b:expr) => { - let (l, r): (&f128, &f128) = (&$a, &$b); - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}"); - }; -} - -#[test] -fn test_num_f128() { - // FIXME(f128): replace with a `test_num` call once the required `fmodl`/`fmodf128` - // function is available on all platforms. - let ten = 10f128; - let two = 2f128; - assert_eq!(ten.add(two), ten + two); - assert_eq!(ten.sub(two), ten - two); - assert_eq!(ten.mul(two), ten * two); - assert_eq!(ten.div(two), ten / two); -} - -// Many math functions allow for less accurate results, so the next tolerance up is used - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_powf() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.powf(1.0), 1.0); - assert_approx_eq!(3.4f128.powf(4.5), 246.40818323761892815995637964326426756, TOL_IMPR); - assert_approx_eq!(2.7f128.powf(-3.2), 0.041652009108526178281070304373500889273, TOL_IMPR); - assert_approx_eq!((-3.1f128).powf(2.0), 9.6100000000000005506706202140776519387, TOL_IMPR); - assert_approx_eq!(5.9f128.powf(-2.0), 0.028727377190462507313100483690639638451, TOL_IMPR); - assert_eq!(8.3f128.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_exp() { - assert_eq!(1.0, 0.0f128.exp()); - assert_approx_eq!(consts::E, 1.0f128.exp(), TOL); - assert_approx_eq!(148.41315910257660342111558004055227962348775, 5.0f128.exp(), TOL); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_exp2() { - assert_eq!(32.0, 5.0f128.exp2()); - assert_eq!(1.0, 0.0f128.exp2()); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_ln() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_approx_eq!(1.0f128.exp().ln(), 1.0, TOL); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f128).ln().is_nan()); - assert_eq!((-0.0f128).ln(), neg_inf); - assert_eq!(0.0f128.ln(), neg_inf); - assert_approx_eq!(4.0f128.ln(), 1.3862943611198906188344642429163531366, TOL); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_log() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(10.0f128.log(10.0), 1.0); - assert_approx_eq!(2.3f128.log(3.5), 0.66485771361478710036766645911922010272, TOL); - assert_eq!(1.0f128.exp().log(1.0f128.exp()), 1.0); - assert!(1.0f128.log(1.0).is_nan()); - assert!(1.0f128.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f128).log(0.1).is_nan()); - assert_eq!((-0.0f128).log(2.0), neg_inf); - assert_eq!(0.0f128.log(7.0), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_log2() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_approx_eq!(10.0f128.log2(), 3.32192809488736234787031942948939017, TOL); - assert_approx_eq!(2.3f128.log2(), 1.2016338611696504130002982471978765921, TOL); - assert_approx_eq!(1.0f128.exp().log2(), 1.4426950408889634073599246810018921381, TOL); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f128).log2().is_nan()); - assert_eq!((-0.0f128).log2(), neg_inf); - assert_eq!(0.0f128.log2(), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_log10() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(10.0f128.log10(), 1.0); - assert_approx_eq!(2.3f128.log10(), 0.36172783601759284532595218865859309898, TOL); - assert_approx_eq!(1.0f128.exp().log10(), 0.43429448190325182765112891891660508222, TOL); - assert_eq!(1.0f128.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f128).log10().is_nan()); - assert_eq!((-0.0f128).log10(), neg_inf); - assert_eq!(0.0f128.log10(), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_asinh() { - // Lower accuracy results are allowed, use increased tolerances - assert_eq!(0.0f128.asinh(), 0.0f128); - assert_eq!((-0.0f128).asinh(), -0.0f128); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f128).asinh().is_sign_negative()); - - // issue 63271 - assert_approx_eq!(2.0f128.asinh(), 1.443635475178810342493276740273105f128, TOL_IMPR); - assert_approx_eq!((-2.0f128).asinh(), -1.443635475178810342493276740273105f128, TOL_IMPR); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!( - (-67452098.07139316f128).asinh(), - -18.720075426274544393985484294000831757220, - TOL_IMPR - ); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f128, 60.0f128.sinh().asinh(), TOL_IMPR); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f128, 1e-15f128.sinh().asinh() * 1e15f128, TOL_IMPR); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_acosh() { - assert_eq!(1.0f128.acosh(), 0.0f128); - assert!(0.999f128.acosh().is_nan()); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f128.acosh(), 1.31695789692481670862504634730796844f128, TOL_IMPR); - assert_approx_eq!(3.0f128.acosh(), 1.76274717403908605046521864995958461f128, TOL_IMPR); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f128, 60.0f128.cosh().acosh(), TOL_IMPR); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_atanh() { - assert_eq!(0.0f128.atanh(), 0.0f128); - assert_eq!((-0.0f128).atanh(), -0.0f128); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(1.0f128.atanh(), inf); - assert_eq!((-1.0f128).atanh(), neg_inf); - assert!(2f128.atanh().atanh().is_nan()); - assert!((-2f128).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f128.atanh(), 0.54930614433405484569762261846126285f128, TOL_IMPR); - assert_approx_eq!((-0.5f128).atanh(), -0.54930614433405484569762261846126285f128, TOL_IMPR); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_gamma() { - // precision can differ among platforms - assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR); - assert_approx_eq!(2.0f128.gamma(), 1.0f128, TOL_IMPR); - assert_approx_eq!(3.0f128.gamma(), 2.0f128, TOL_IMPR); - assert_approx_eq!(4.0f128.gamma(), 6.0f128, TOL_IMPR); - assert_approx_eq!(5.0f128.gamma(), 24.0f128, TOL_IMPR); - assert_approx_eq!(0.5f128.gamma(), consts::PI.sqrt(), TOL_IMPR); - assert_approx_eq!((-0.5f128).gamma(), -2.0 * consts::PI.sqrt(), TOL_IMPR); - assert_eq!(0.0f128.gamma(), f128::INFINITY); - assert_eq!((-0.0f128).gamma(), f128::NEG_INFINITY); - assert!((-1.0f128).gamma().is_nan()); - assert!((-2.0f128).gamma().is_nan()); - assert!(f128::NAN.gamma().is_nan()); - assert!(f128::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f128::INFINITY.gamma(), f128::INFINITY); - assert_eq!(1760.9f128.gamma(), f128::INFINITY); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_ln_gamma() { - assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); - assert_eq!(1.0f128.ln_gamma().1, 1); - assert_approx_eq!(2.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); - assert_eq!(2.0f128.ln_gamma().1, 1); - assert_approx_eq!(3.0f128.ln_gamma().0, 2.0f128.ln(), TOL_IMPR); - assert_eq!(3.0f128.ln_gamma().1, 1); - assert_approx_eq!((-0.5f128).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_IMPR); - assert_eq!((-0.5f128).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - let pi: f128 = consts::PI; - let frac_pi_2: f128 = consts::FRAC_PI_2; - let frac_pi_3: f128 = consts::FRAC_PI_3; - let frac_pi_4: f128 = consts::FRAC_PI_4; - let frac_pi_6: f128 = consts::FRAC_PI_6; - let frac_pi_8: f128 = consts::FRAC_PI_8; - let frac_1_pi: f128 = consts::FRAC_1_PI; - let frac_2_pi: f128 = consts::FRAC_2_PI; - - assert_approx_eq!(frac_pi_2, pi / 2f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_3, pi / 3f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_4, pi / 4f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_6, pi / 6f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_8, pi / 8f128, TOL_PRECISE); - assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE); - assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE); - - #[cfg(not(miri))] - #[cfg(target_has_reliable_f128_math)] - { - let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; - let sqrt2: f128 = consts::SQRT_2; - let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; - let e: f128 = consts::E; - let log2_e: f128 = consts::LOG2_E; - let log10_e: f128 = consts::LOG10_E; - let ln_2: f128 = consts::LN_2; - let ln_10: f128 = consts::LN_10; - - assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt(), TOL_PRECISE); - assert_approx_eq!(sqrt2, 2f128.sqrt(), TOL_PRECISE); - assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt(), TOL_PRECISE); - assert_approx_eq!(log2_e, e.log2(), TOL_PRECISE); - assert_approx_eq!(log10_e, e.log10(), TOL_PRECISE); - assert_approx_eq!(ln_2, 2f128.ln(), TOL_PRECISE); - assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); - } -} diff --git a/library/std/tests/floats/f16.rs b/library/std/tests/floats/f16.rs deleted file mode 100644 index cc0960765f41..000000000000 --- a/library/std/tests/floats/f16.rs +++ /dev/null @@ -1,297 +0,0 @@ -#![cfg(target_has_reliable_f16)] - -use std::f16::consts; - -/// Tolerance for results on the order of 10.0e-2 -#[allow(unused)] -const TOL_N2: f16 = 0.0001; - -/// Tolerance for results on the order of 10.0e+0 -#[allow(unused)] -const TOL_0: f16 = 0.01; - -/// Tolerance for results on the order of 10.0e+2 -#[allow(unused)] -const TOL_P2: f16 = 0.5; - -/// Tolerance for results on the order of 10.0e+4 -#[allow(unused)] -const TOL_P4: f16 = 10.0; - -/// Compare by representation -#[allow(unused_macros)] -macro_rules! assert_f16_biteq { - ($a:expr, $b:expr) => { - let (l, r): (&f16, &f16) = (&$a, &$b); - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})"); - }; -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_powf() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(1.0f16.powf(1.0), 1.0); - assert_approx_eq!(3.4f16.powf(4.5), 246.408183, TOL_P2); - assert_approx_eq!(2.7f16.powf(-3.2), 0.041652, TOL_N2); - assert_approx_eq!((-3.1f16).powf(2.0), 9.61, TOL_P2); - assert_approx_eq!(5.9f16.powf(-2.0), 0.028727, TOL_N2); - assert_eq!(8.3f16.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_exp() { - assert_eq!(1.0, 0.0f16.exp()); - assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0); - assert_approx_eq!(148.413159, 5.0f16.exp(), TOL_0); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_exp2() { - assert_eq!(32.0, 5.0f16.exp2()); - assert_eq!(1.0, 0.0f16.exp2()); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_ln() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_approx_eq!(1.0f16.exp().ln(), 1.0, TOL_0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f16).ln().is_nan()); - assert_eq!((-0.0f16).ln(), neg_inf); - assert_eq!(0.0f16.ln(), neg_inf); - assert_approx_eq!(4.0f16.ln(), 1.386294, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_log() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(10.0f16.log(10.0), 1.0); - assert_approx_eq!(2.3f16.log(3.5), 0.664858, TOL_0); - assert_eq!(1.0f16.exp().log(1.0f16.exp()), 1.0); - assert!(1.0f16.log(1.0).is_nan()); - assert!(1.0f16.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f16).log(0.1).is_nan()); - assert_eq!((-0.0f16).log(2.0), neg_inf); - assert_eq!(0.0f16.log(7.0), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_log2() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_approx_eq!(10.0f16.log2(), 3.321928, TOL_0); - assert_approx_eq!(2.3f16.log2(), 1.201634, TOL_0); - assert_approx_eq!(1.0f16.exp().log2(), 1.442695, TOL_0); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f16).log2().is_nan()); - assert_eq!((-0.0f16).log2(), neg_inf); - assert_eq!(0.0f16.log2(), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_log10() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(10.0f16.log10(), 1.0); - assert_approx_eq!(2.3f16.log10(), 0.361728, TOL_0); - assert_approx_eq!(1.0f16.exp().log10(), 0.434294, TOL_0); - assert_eq!(1.0f16.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f16).log10().is_nan()); - assert_eq!((-0.0f16).log10(), neg_inf); - assert_eq!(0.0f16.log10(), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_asinh() { - assert_eq!(0.0f16.asinh(), 0.0f16); - assert_eq!((-0.0f16).asinh(), -0.0f16); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f16).asinh().is_sign_negative()); - // issue 63271 - assert_approx_eq!(2.0f16.asinh(), 1.443635475178810342493276740273105f16, TOL_0); - assert_approx_eq!((-2.0f16).asinh(), -1.443635475178810342493276740273105f16, TOL_0); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-200.0f16).asinh(), -5.991470797049389, TOL_0); - - // test for low accuracy from issue 104548 - assert_approx_eq!(10.0f16, 10.0f16.sinh().asinh(), TOL_0); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f16, 1e-3f16.sinh().asinh() * 1e3f16, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_acosh() { - assert_eq!(1.0f16.acosh(), 0.0f16); - assert!(0.999f16.acosh().is_nan()); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f16.acosh(), 1.31695789692481670862504634730796844f16, TOL_0); - assert_approx_eq!(3.0f16.acosh(), 1.76274717403908605046521864995958461f16, TOL_0); - - // test for low accuracy from issue 104548 - assert_approx_eq!(10.0f16, 10.0f16.cosh().acosh(), TOL_P2); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_atanh() { - assert_eq!(0.0f16.atanh(), 0.0f16); - assert_eq!((-0.0f16).atanh(), -0.0f16); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(1.0f16.atanh(), inf); - assert_eq!((-1.0f16).atanh(), neg_inf); - assert!(2f16.atanh().atanh().is_nan()); - assert!((-2f16).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f16.atanh(), 0.54930614433405484569762261846126285f16, TOL_0); - assert_approx_eq!((-0.5f16).atanh(), -0.54930614433405484569762261846126285f16, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_gamma() { - // precision can differ among platforms - assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0); - assert_approx_eq!(2.0f16.gamma(), 1.0f16, TOL_0); - assert_approx_eq!(3.0f16.gamma(), 2.0f16, TOL_0); - assert_approx_eq!(4.0f16.gamma(), 6.0f16, TOL_0); - assert_approx_eq!(5.0f16.gamma(), 24.0f16, TOL_0); - assert_approx_eq!(0.5f16.gamma(), consts::PI.sqrt(), TOL_0); - assert_approx_eq!((-0.5f16).gamma(), -2.0 * consts::PI.sqrt(), TOL_0); - assert_eq!(0.0f16.gamma(), f16::INFINITY); - assert_eq!((-0.0f16).gamma(), f16::NEG_INFINITY); - assert!((-1.0f16).gamma().is_nan()); - assert!((-2.0f16).gamma().is_nan()); - assert!(f16::NAN.gamma().is_nan()); - assert!(f16::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f16::INFINITY.gamma(), f16::INFINITY); - assert_eq!(171.71f16.gamma(), f16::INFINITY); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_ln_gamma() { - assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0); - assert_eq!(1.0f16.ln_gamma().1, 1); - assert_approx_eq!(2.0f16.ln_gamma().0, 0.0f16, TOL_0); - assert_eq!(2.0f16.ln_gamma().1, 1); - assert_approx_eq!(3.0f16.ln_gamma().0, 2.0f16.ln(), TOL_0); - assert_eq!(3.0f16.ln_gamma().1, 1); - assert_approx_eq!((-0.5f16).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_0); - assert_eq!((-0.5f16).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - let pi: f16 = consts::PI; - let frac_pi_2: f16 = consts::FRAC_PI_2; - let frac_pi_3: f16 = consts::FRAC_PI_3; - let frac_pi_4: f16 = consts::FRAC_PI_4; - let frac_pi_6: f16 = consts::FRAC_PI_6; - let frac_pi_8: f16 = consts::FRAC_PI_8; - let frac_1_pi: f16 = consts::FRAC_1_PI; - let frac_2_pi: f16 = consts::FRAC_2_PI; - - assert_approx_eq!(frac_pi_2, pi / 2f16, TOL_0); - assert_approx_eq!(frac_pi_3, pi / 3f16, TOL_0); - assert_approx_eq!(frac_pi_4, pi / 4f16, TOL_0); - assert_approx_eq!(frac_pi_6, pi / 6f16, TOL_0); - assert_approx_eq!(frac_pi_8, pi / 8f16, TOL_0); - assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0); - assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0); - - #[cfg(not(miri))] - #[cfg(target_has_reliable_f16_math)] - { - let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; - let sqrt2: f16 = consts::SQRT_2; - let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; - let e: f16 = consts::E; - let log2_e: f16 = consts::LOG2_E; - let log10_e: f16 = consts::LOG10_E; - let ln_2: f16 = consts::LN_2; - let ln_10: f16 = consts::LN_10; - - assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt(), TOL_0); - assert_approx_eq!(sqrt2, 2f16.sqrt(), TOL_0); - assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt(), TOL_0); - assert_approx_eq!(log2_e, e.log2(), TOL_0); - assert_approx_eq!(log10_e, e.log10(), TOL_0); - assert_approx_eq!(ln_2, 2f16.ln(), TOL_0); - assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); - } -} diff --git a/library/std/tests/floats/f32.rs b/library/std/tests/floats/f32.rs deleted file mode 100644 index 3acd06709141..000000000000 --- a/library/std/tests/floats/f32.rs +++ /dev/null @@ -1,258 +0,0 @@ -use std::f32::consts; - -/// Miri adds some extra errors to float functions; make sure the tests still pass. -/// These values are purely used as a canary to test against and are thus not a stable guarantee Rust provides. -/// They serve as a way to get an idea of the real precision of floating point operations on different platforms. -const APPROX_DELTA: f32 = if cfg!(miri) { 1e-3 } else { 1e-6 }; - -#[allow(unused_macros)] -macro_rules! assert_f32_biteq { - ($left : expr, $right : expr) => { - let l: &f32 = &$left; - let r: &f32 = &$right; - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})"); - }; -} - -#[test] -fn test_powf() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.powf(1.0), 1.0); - assert_approx_eq!(3.4f32.powf(4.5), 246.408218, APPROX_DELTA); - assert_approx_eq!(2.7f32.powf(-3.2), 0.041652); - assert_approx_eq!((-3.1f32).powf(2.0), 9.61, APPROX_DELTA); - assert_approx_eq!(5.9f32.powf(-2.0), 0.028727); - assert_eq!(8.3f32.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - -#[test] -fn test_exp() { - assert_eq!(1.0, 0.0f32.exp()); - assert_approx_eq!(2.718282, 1.0f32.exp(), APPROX_DELTA); - assert_approx_eq!(148.413162, 5.0f32.exp(), APPROX_DELTA); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -fn test_exp2() { - assert_approx_eq!(32.0, 5.0f32.exp2(), APPROX_DELTA); - assert_eq!(1.0, 0.0f32.exp2()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -fn test_ln() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(1.0f32.exp().ln(), 1.0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f32).ln().is_nan()); - assert_eq!((-0.0f32).ln(), neg_inf); - assert_eq!(0.0f32.ln(), neg_inf); - assert_approx_eq!(4.0f32.ln(), 1.386294, APPROX_DELTA); -} - -#[test] -fn test_log() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(10.0f32.log(10.0), 1.0); - assert_approx_eq!(2.3f32.log(3.5), 0.664858); - assert_approx_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0, APPROX_DELTA); - assert!(1.0f32.log(1.0).is_nan()); - assert!(1.0f32.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f32).log(0.1).is_nan()); - assert_eq!((-0.0f32).log(2.0), neg_inf); - assert_eq!(0.0f32.log(7.0), neg_inf); -} - -#[test] -fn test_log2() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(10.0f32.log2(), 3.321928, APPROX_DELTA); - assert_approx_eq!(2.3f32.log2(), 1.201634); - assert_approx_eq!(1.0f32.exp().log2(), 1.442695, APPROX_DELTA); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f32).log2().is_nan()); - assert_eq!((-0.0f32).log2(), neg_inf); - assert_eq!(0.0f32.log2(), neg_inf); -} - -#[test] -fn test_log10() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(10.0f32.log10(), 1.0); - assert_approx_eq!(2.3f32.log10(), 0.361728); - assert_approx_eq!(1.0f32.exp().log10(), 0.434294); - assert_eq!(1.0f32.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f32).log10().is_nan()); - assert_eq!((-0.0f32).log10(), neg_inf); - assert_eq!(0.0f32.log10(), neg_inf); -} - -#[test] -fn test_asinh() { - assert_eq!(0.0f32.asinh(), 0.0f32); - assert_eq!((-0.0f32).asinh(), -0.0f32); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271 - assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); - assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32, APPROX_DELTA); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f32, 60.0f32.sinh().asinh(), APPROX_DELTA); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f32, 1e-15f32.sinh().asinh() * 1e15f32); -} - -#[test] -fn test_acosh() { - assert_eq!(1.0f32.acosh(), 0.0f32); - assert!(0.999f32.acosh().is_nan()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32); - assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f32, 60.0f32.cosh().acosh(), APPROX_DELTA); -} - -#[test] -fn test_atanh() { - assert_eq!(0.0f32.atanh(), 0.0f32); - assert_eq!((-0.0f32).atanh(), -0.0f32); - - let inf32: f32 = f32::INFINITY; - let neg_inf32: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.atanh(), inf32); - assert_eq!((-1.0f32).atanh(), neg_inf32); - - assert!(2f64.atanh().atanh().is_nan()); - assert!((-2f64).atanh().atanh().is_nan()); - - let inf64: f32 = f32::INFINITY; - let neg_inf64: f32 = f32::NEG_INFINITY; - let nan32: f32 = f32::NAN; - assert!(inf64.atanh().is_nan()); - assert!(neg_inf64.atanh().is_nan()); - assert!(nan32.atanh().is_nan()); - - assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32); - assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32); -} - -#[test] -fn test_gamma() { - // precision can differ between platforms - assert_approx_eq!(1.0f32.gamma(), 1.0f32, APPROX_DELTA); - assert_approx_eq!(2.0f32.gamma(), 1.0f32, APPROX_DELTA); - assert_approx_eq!(3.0f32.gamma(), 2.0f32, APPROX_DELTA); - assert_approx_eq!(4.0f32.gamma(), 6.0f32, APPROX_DELTA); - assert_approx_eq!(5.0f32.gamma(), 24.0f32, APPROX_DELTA); - assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt(), APPROX_DELTA); - assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt(), APPROX_DELTA); - assert_eq!(0.0f32.gamma(), f32::INFINITY); - assert_eq!((-0.0f32).gamma(), f32::NEG_INFINITY); - assert!((-1.0f32).gamma().is_nan()); - assert!((-2.0f32).gamma().is_nan()); - assert!(f32::NAN.gamma().is_nan()); - assert!(f32::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f32::INFINITY.gamma(), f32::INFINITY); - assert_eq!(171.71f32.gamma(), f32::INFINITY); -} - -#[test] -fn test_ln_gamma() { - assert_approx_eq!(1.0f32.ln_gamma().0, 0.0f32); - assert_eq!(1.0f32.ln_gamma().1, 1); - assert_approx_eq!(2.0f32.ln_gamma().0, 0.0f32); - assert_eq!(2.0f32.ln_gamma().1, 1); - assert_approx_eq!(3.0f32.ln_gamma().0, 2.0f32.ln()); - assert_eq!(3.0f32.ln_gamma().1, 1); - assert_approx_eq!((-0.5f32).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), APPROX_DELTA); - assert_eq!((-0.5f32).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - let pi: f32 = consts::PI; - let frac_pi_2: f32 = consts::FRAC_PI_2; - let frac_pi_3: f32 = consts::FRAC_PI_3; - let frac_pi_4: f32 = consts::FRAC_PI_4; - let frac_pi_6: f32 = consts::FRAC_PI_6; - let frac_pi_8: f32 = consts::FRAC_PI_8; - let frac_1_pi: f32 = consts::FRAC_1_PI; - let frac_2_pi: f32 = consts::FRAC_2_PI; - let frac_2_sqrtpi: f32 = consts::FRAC_2_SQRT_PI; - let sqrt2: f32 = consts::SQRT_2; - let frac_1_sqrt2: f32 = consts::FRAC_1_SQRT_2; - let e: f32 = consts::E; - let log2_e: f32 = consts::LOG2_E; - let log10_e: f32 = consts::LOG10_E; - let ln_2: f32 = consts::LN_2; - let ln_10: f32 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f32); - assert_approx_eq!(frac_pi_3, pi / 3f32, APPROX_DELTA); - assert_approx_eq!(frac_pi_4, pi / 4f32); - assert_approx_eq!(frac_pi_6, pi / 6f32); - assert_approx_eq!(frac_pi_8, pi / 8f32); - assert_approx_eq!(frac_1_pi, 1f32 / pi); - assert_approx_eq!(frac_2_pi, 2f32 / pi); - assert_approx_eq!(frac_2_sqrtpi, 2f32 / pi.sqrt()); - assert_approx_eq!(sqrt2, 2f32.sqrt()); - assert_approx_eq!(frac_1_sqrt2, 1f32 / 2f32.sqrt()); - assert_approx_eq!(log2_e, e.log2()); - assert_approx_eq!(log10_e, e.log10()); - assert_approx_eq!(ln_2, 2f32.ln()); - assert_approx_eq!(ln_10, 10f32.ln(), APPROX_DELTA); -} diff --git a/library/std/tests/floats/f64.rs b/library/std/tests/floats/f64.rs deleted file mode 100644 index fccf20097278..000000000000 --- a/library/std/tests/floats/f64.rs +++ /dev/null @@ -1,249 +0,0 @@ -use std::f64::consts; - -#[allow(unused_macros)] -macro_rules! assert_f64_biteq { - ($left : expr, $right : expr) => { - let l: &f64 = &$left; - let r: &f64 = &$right; - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l} ({lb:#018x}) is not bitequal to {r} ({rb:#018x})"); - }; -} - -#[test] -fn test_powf() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.powf(1.0), 1.0); - assert_approx_eq!(3.4f64.powf(4.5), 246.408183); - assert_approx_eq!(2.7f64.powf(-3.2), 0.041652); - assert_approx_eq!((-3.1f64).powf(2.0), 9.61); - assert_approx_eq!(5.9f64.powf(-2.0), 0.028727); - assert_eq!(8.3f64.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - -#[test] -fn test_exp() { - assert_eq!(1.0, 0.0f64.exp()); - assert_approx_eq!(2.718282, 1.0f64.exp()); - assert_approx_eq!(148.413159, 5.0f64.exp()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -fn test_exp2() { - assert_approx_eq!(32.0, 5.0f64.exp2()); - assert_eq!(1.0, 0.0f64.exp2()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -fn test_ln() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(1.0f64.exp().ln(), 1.0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f64).ln().is_nan()); - assert_eq!((-0.0f64).ln(), neg_inf); - assert_eq!(0.0f64.ln(), neg_inf); - assert_approx_eq!(4.0f64.ln(), 1.386294); -} - -#[test] -fn test_log() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(10.0f64.log(10.0), 1.0); - assert_approx_eq!(2.3f64.log(3.5), 0.664858); - assert_approx_eq!(1.0f64.exp().log(1.0f64.exp()), 1.0); - assert!(1.0f64.log(1.0).is_nan()); - assert!(1.0f64.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f64).log(0.1).is_nan()); - assert_eq!((-0.0f64).log(2.0), neg_inf); - assert_eq!(0.0f64.log(7.0), neg_inf); -} - -#[test] -fn test_log2() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(10.0f64.log2(), 3.321928); - assert_approx_eq!(2.3f64.log2(), 1.201634); - assert_approx_eq!(1.0f64.exp().log2(), 1.442695); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f64).log2().is_nan()); - assert_eq!((-0.0f64).log2(), neg_inf); - assert_eq!(0.0f64.log2(), neg_inf); -} - -#[test] -fn test_log10() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(10.0f64.log10(), 1.0); - assert_approx_eq!(2.3f64.log10(), 0.361728); - assert_approx_eq!(1.0f64.exp().log10(), 0.434294); - assert_eq!(1.0f64.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f64).log10().is_nan()); - assert_eq!((-0.0f64).log10(), neg_inf); - assert_eq!(0.0f64.log10(), neg_inf); -} - -#[test] -fn test_asinh() { - assert_eq!(0.0f64.asinh(), 0.0f64); - assert_eq!((-0.0f64).asinh(), -0.0f64); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f64).asinh().is_sign_negative()); - // issue 63271 - assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); - assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f64, 60.0f64.sinh().asinh()); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f64, 1e-15f64.sinh().asinh() * 1e15f64); -} - -#[test] -fn test_acosh() { - assert_eq!(1.0f64.acosh(), 0.0f64); - assert!(0.999f64.acosh().is_nan()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64); - assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f64, 60.0f64.cosh().acosh()); -} - -#[test] -fn test_atanh() { - assert_eq!(0.0f64.atanh(), 0.0f64); - assert_eq!((-0.0f64).atanh(), -0.0f64); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(1.0f64.atanh(), inf); - assert_eq!((-1.0f64).atanh(), neg_inf); - assert!(2f64.atanh().atanh().is_nan()); - assert!((-2f64).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64); - assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64); -} - -#[test] -fn test_gamma() { - // precision can differ between platforms - assert_approx_eq!(1.0f64.gamma(), 1.0f64); - assert_approx_eq!(2.0f64.gamma(), 1.0f64); - assert_approx_eq!(3.0f64.gamma(), 2.0f64); - assert_approx_eq!(4.0f64.gamma(), 6.0f64); - assert_approx_eq!(5.0f64.gamma(), 24.0f64); - assert_approx_eq!(0.5f64.gamma(), consts::PI.sqrt()); - assert_approx_eq!((-0.5f64).gamma(), -2.0 * consts::PI.sqrt()); - assert_eq!(0.0f64.gamma(), f64::INFINITY); - assert_eq!((-0.0f64).gamma(), f64::NEG_INFINITY); - assert!((-1.0f64).gamma().is_nan()); - assert!((-2.0f64).gamma().is_nan()); - assert!(f64::NAN.gamma().is_nan()); - assert!(f64::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f64::INFINITY.gamma(), f64::INFINITY); - assert_eq!(171.71f64.gamma(), f64::INFINITY); -} - -#[test] -fn test_ln_gamma() { - assert_approx_eq!(1.0f64.ln_gamma().0, 0.0f64); - assert_eq!(1.0f64.ln_gamma().1, 1); - assert_approx_eq!(2.0f64.ln_gamma().0, 0.0f64); - assert_eq!(2.0f64.ln_gamma().1, 1); - assert_approx_eq!(3.0f64.ln_gamma().0, 2.0f64.ln()); - assert_eq!(3.0f64.ln_gamma().1, 1); - assert_approx_eq!((-0.5f64).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); - assert_eq!((-0.5f64).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - let pi: f64 = consts::PI; - let frac_pi_2: f64 = consts::FRAC_PI_2; - let frac_pi_3: f64 = consts::FRAC_PI_3; - let frac_pi_4: f64 = consts::FRAC_PI_4; - let frac_pi_6: f64 = consts::FRAC_PI_6; - let frac_pi_8: f64 = consts::FRAC_PI_8; - let frac_1_pi: f64 = consts::FRAC_1_PI; - let frac_2_pi: f64 = consts::FRAC_2_PI; - let frac_2_sqrtpi: f64 = consts::FRAC_2_SQRT_PI; - let sqrt2: f64 = consts::SQRT_2; - let frac_1_sqrt2: f64 = consts::FRAC_1_SQRT_2; - let e: f64 = consts::E; - let log2_e: f64 = consts::LOG2_E; - let log10_e: f64 = consts::LOG10_E; - let ln_2: f64 = consts::LN_2; - let ln_10: f64 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f64); - assert_approx_eq!(frac_pi_3, pi / 3f64); - assert_approx_eq!(frac_pi_4, pi / 4f64); - assert_approx_eq!(frac_pi_6, pi / 6f64); - assert_approx_eq!(frac_pi_8, pi / 8f64); - assert_approx_eq!(frac_1_pi, 1f64 / pi); - assert_approx_eq!(frac_2_pi, 2f64 / pi); - assert_approx_eq!(frac_2_sqrtpi, 2f64 / pi.sqrt()); - assert_approx_eq!(sqrt2, 2f64.sqrt()); - assert_approx_eq!(frac_1_sqrt2, 1f64 / 2f64.sqrt()); - assert_approx_eq!(log2_e, e.log2()); - assert_approx_eq!(log10_e, e.log10()); - assert_approx_eq!(ln_2, 2f64.ln()); - assert_approx_eq!(ln_10, 10f64.ln()); -} diff --git a/library/std/tests/floats/lib.rs b/library/std/tests/floats/lib.rs deleted file mode 100644 index 012349350b0b..000000000000 --- a/library/std/tests/floats/lib.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![feature(f16, f128, float_gamma, cfg_target_has_reliable_f16_f128)] -#![expect(internal_features)] // for reliable_f16_f128 - -use std::fmt; -use std::ops::{Add, Div, Mul, Rem, Sub}; - -/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. -macro_rules! assert_approx_eq { - ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; - ($a:expr, $b:expr, $lim:expr) => {{ - let (a, b) = (&$a, &$b); - let diff = (*a - *b).abs(); - assert!( - diff <= $lim, - "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", - lim = $lim - ); - }}; -} - -/// Helper function for testing numeric operations -pub fn test_num(ten: T, two: T) -where - T: PartialEq - + Add - + Sub - + Mul - + Div - + Rem - + fmt::Debug - + Copy, -{ - assert_eq!(ten.add(two), ten + two); - assert_eq!(ten.sub(two), ten - two); - assert_eq!(ten.mul(two), ten * two); - assert_eq!(ten.div(two), ten / two); - assert_eq!(ten.rem(two), ten % two); -} - -mod f128; -mod f16; -mod f32; -mod f64; diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index 4094b7acd874..8997b8ad192d 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -2291,6 +2291,26 @@ fn display_format_flags() { assert_eq!(format!("a{:#<5}b", Path::new("a").display()), "aa####b"); } +#[test] +fn display_path_with_padding_no_align() { + assert_eq!(format!("{:10}", Path::new("/foo/bar").display()), "/foo/bar "); +} + +#[test] +fn display_path_with_padding_align_left() { + assert_eq!(format!("{:<10}", Path::new("/foo/bar").display()), "/foo/bar "); +} + +#[test] +fn display_path_with_padding_align_right() { + assert_eq!(format!("{:>10}", Path::new("/foo/bar").display()), " /foo/bar"); +} + +#[test] +fn display_path_with_padding_align_center() { + assert_eq!(format!("{:^10}", Path::new("/foo/bar").display()), " /foo/bar "); +} + #[test] fn into_rc() { let orig = "hello/world"; diff --git a/library/std/tests/sync/oneshot.rs b/library/std/tests/sync/oneshot.rs index 8c47f35ebfea..6eaacfbc6497 100644 --- a/library/std/tests/sync/oneshot.rs +++ b/library/std/tests/sync/oneshot.rs @@ -89,15 +89,15 @@ fn send_before_recv_timeout() { assert!(sender.send(22i128).is_ok()); - let start = Instant::now(); - let timeout = Duration::from_secs(1); match receiver.recv_timeout(timeout) { Ok(22) => {} _ => panic!("expected Ok(22)"), } - assert!(start.elapsed() < timeout); + // FIXME(#152648): There previously was a timing assertion here. + // This was removed, because under load there's no guarantee that the main thread is + // scheduled and run before `timeout` expires } #[test] @@ -243,7 +243,9 @@ fn recv_deadline_passed() { } assert!(start.elapsed() >= timeout); - assert!(start.elapsed() < timeout * 3); + // FIXME(#152878): An upper-bound assertion on the elapsed time was removed, + // because CI runners can starve individual threads for a surprisingly long + // time, leading to flaky failures. } #[test] @@ -252,12 +254,16 @@ fn recv_time_passed() { let start = Instant::now(); let timeout = Duration::from_millis(100); + match receiver.recv_timeout(timeout) { Err(RecvTimeoutError::Timeout(_)) => {} _ => panic!("expected timeout error"), } + assert!(start.elapsed() >= timeout); - assert!(start.elapsed() < timeout * 3); + // FIXME(#152878): An upper-bound assertion on the elapsed time was removed, + // because CI runners can starve individual threads for a surprisingly long + // time, leading to flaky failures. } #[test] diff --git a/library/std/tests/volatile-fat-ptr.rs b/library/std/tests/volatile-fat-ptr.rs index b00277e7a411..406eb7c80afb 100644 --- a/library/std/tests/volatile-fat-ptr.rs +++ b/library/std/tests/volatile-fat-ptr.rs @@ -1,5 +1,3 @@ -#![allow(stable_features)] - use std::ptr::{read_volatile, write_volatile}; #[test] diff --git a/library/std_detect/src/lib.rs b/library/std_detect/src/lib.rs index 5e1d21bbfd17..f9d79df670a0 100644 --- a/library/std_detect/src/lib.rs +++ b/library/std_detect/src/lib.rs @@ -15,7 +15,7 @@ //! * `s390x`: [`is_s390x_feature_detected`] #![unstable(feature = "stdarch_internal", issue = "none")] -#![feature(staged_api, cfg_select, doc_cfg, allow_internal_unstable)] +#![feature(staged_api, doc_cfg, allow_internal_unstable)] #![deny(rust_2018_idioms)] #![allow(clippy::shadow_reuse)] #![cfg_attr(test, allow(unused_imports))] 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 de64839661d6..490f04020ee4 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs @@ -49,7 +49,7 @@ pub fn __crc32d(crc: u32, data: u64) -> u32 { #[inline(always)] #[target_feature(enable = "jsconv")] #[cfg_attr(test, assert_instr(fjcvtzs))] -#[stable(feature = "stdarch_aarch64_jscvt", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "stdarch_aarch64_jscvt", since = "1.95.0")] pub fn __jcvt(a: f64) -> i32 { unsafe extern "unadjusted" { #[cfg_attr( 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 8574aacee667..4df899a202cf 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 @@ -64,7 +64,7 @@ neon-unstable-feat-lut: &neon-unstable-feat-lut FnCall: [unstable, ['feature = "stdarch_neon_feat_lut"', 'issue = "138050"']] aarch64-stable-jscvt: &aarch64-stable-jscvt - FnCall: [stable, ['feature = "stdarch_aarch64_jscvt"', 'since = "CURRENT_RUSTC_VERSION"']] + FnCall: [stable, ['feature = "stdarch_aarch64_jscvt"', 'since = "1.95.0"']] # #[unstable(feature = "stdarch_neon_feat_lrcpc3", issue = "none")] neon-unstable-feat-lrcpc3: &neon-unstable-feat-lrcpc3 diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs index 74d99e0f1270..2772222a05c9 100644 --- a/library/test/src/formatters/junit.rs +++ b/library/test/src/formatters/junit.rs @@ -1,5 +1,5 @@ +use std::io; use std::io::prelude::Write; -use std::io::{self}; use std::time::Duration; use super::OutputFormatter; diff --git a/library/test/src/term.rs b/library/test/src/term.rs index d9880a776406..1e4c7bc879cf 100644 --- a/library/test/src/term.rs +++ b/library/test/src/term.rs @@ -12,8 +12,8 @@ #![deny(missing_docs)] +use std::io; use std::io::prelude::*; -use std::io::{self}; pub(crate) use terminfo::TerminfoTerminal; #[cfg(windows)] diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index f2e0cfd32ed6..cce6ca748ccc 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -1,12 +1,11 @@ #![no_std] #![unstable(feature = "panic_unwind", issue = "32837")] #![feature(cfg_emscripten_wasm_eh)] -#![feature(cfg_select)] #![feature(link_cfg)] #![feature(staged_api)] #![cfg_attr( all(target_family = "wasm", any(not(target_os = "emscripten"), emscripten_wasm_eh)), - feature(link_llvm_intrinsics, simd_wasm64) + feature(link_llvm_intrinsics, simd_wasm64, asm_experimental_arch) )] #![allow(internal_features)] #![allow(unused_features)] diff --git a/library/unwind/src/wasm.rs b/library/unwind/src/wasm.rs index cb6e90ba180b..37d93bdbb67d 100644 --- a/library/unwind/src/wasm.rs +++ b/library/unwind/src/wasm.rs @@ -2,6 +2,30 @@ #![allow(nonstandard_style)] +// Define the __cpp_exception tag that LLVM's wasm exception handling requires. +// In particular it is required to use either of: +// 1. the wasm_throw llvm intrinsic, or +// 2. the Rust try intrinsic. +// +// This must be provided since LLVM commit +// aee99e8015daa9f53ab1fd4e5b24cc4c694bdc4a which changed the tag from being +// weakly defined in each object file to being an external reference that must +// be linked from somewhere. +// +// We only define this for wasm32-unknown-unknown because on Emscripten/WASI +// targets, this symbol should be defined by the external toolchain. In +// particular, defining this on Emscripten would break Emscripten dynamic +// libraries. +#[cfg(all(target_os = "unknown", panic = "unwind"))] +core::arch::global_asm!( + ".globl __cpp_exception", + #[cfg(target_pointer_width = "64")] + ".tagtype __cpp_exception i64", + #[cfg(target_pointer_width = "32")] + ".tagtype __cpp_exception i32", + "__cpp_exception:", +); + #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq)] pub enum _Unwind_Reason_Code { diff --git a/rust-bors.toml b/rust-bors.toml index 9b632481c7c3..0efbb5e8fc61 100644 --- a/rust-bors.toml +++ b/rust-bors.toml @@ -25,7 +25,7 @@ labels_blocking_approval = [ "S-waiting-on-t-rustdoc-frontend", "S-waiting-on-t-clippy", # PR manually set to blocked - "S-blocked" + "S-blocked", ] # If CI runs quicker than this duration, consider it to be a failure @@ -41,7 +41,7 @@ approved = [ "-S-waiting-on-author", "-S-waiting-on-crater", "-S-waiting-on-review", - "-S-waiting-on-team" + "-S-waiting-on-team", ] unapproved = [ "+S-waiting-on-author", @@ -49,16 +49,16 @@ unapproved = [ "-S-waiting-on-bors", "-S-waiting-on-crater", "-S-waiting-on-review", - "-S-waiting-on-team" + "-S-waiting-on-team", ] try_failed = [ "+S-waiting-on-author", "-S-waiting-on-review", - "-S-waiting-on-crater" + "-S-waiting-on-crater", ] auto_build_succeeded = [ "+merged-by-bors", - "-S-waiting-on-bors" + "-S-waiting-on-bors", ] auto_build_failed = [ "+S-waiting-on-review", @@ -66,5 +66,5 @@ auto_build_failed = [ "-S-waiting-on-bors", "-S-waiting-on-author", "-S-waiting-on-crater", - "-S-waiting-on-team" + "-S-waiting-on-team", ] diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 960f003992fb..f9499d9927f2 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -472,18 +472,18 @@ dependencies = [ [[package]] name = "objc2-core-foundation" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ "bitflags", ] [[package]] name = "objc2-io-kit" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" dependencies = [ "libc", "objc2-core-foundation", @@ -743,9 +743,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.38.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe840c5b1afe259a5657392a4dbb74473a14c8db999c3ec2f4ae812e028a94da" +checksum = "1efc19935b4b66baa6f654ac7924c192f55b175c00a7ab72410fc24284dacda8" dependencies = [ "libc", "memchr", diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 008e0d37642b..0019e9e6d92e 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -57,7 +57,7 @@ walkdir = "2.4" xz2 = "0.1" # Dependencies needed by the build-metrics feature -sysinfo = { version = "0.38.0", default-features = false, optional = true, features = ["system"] } +sysinfo = { version = "0.38.2", default-features = false, optional = true, features = ["system"] } # Dependencies needed by the `tracing` feature chrono = { version = "0.4", default-features = false, optional = true, features = ["now", "std"] } diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index db7c7b69cba9..105e41ca45f6 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -696,7 +696,7 @@ class RustBuild(object): for download_info in tarballs_download_info: download_component(download_info) - # Unpack the tarballs in parallle. + # Unpack the tarballs in parallel. # In Python 2.7, Pool cannot be used as a context manager. pool_size = min(len(tarballs_download_info), get_cpus()) if self.verbose: diff --git a/src/bootstrap/defaults/bootstrap.compiler.toml b/src/bootstrap/defaults/bootstrap.compiler.toml index 9dcb20ed4832..10fa0b3aeb59 100644 --- a/src/bootstrap/defaults/bootstrap.compiler.toml +++ b/src/bootstrap/defaults/bootstrap.compiler.toml @@ -23,7 +23,7 @@ lto = "off" frame-pointers = true # Compiler contributors often want to build rustc even without any changes to # e.g. check that it builds locally and check the baseline behavior of a -# compiler built from latest `master` commit. +# compiler built from latest `main` commit. download-rustc = false [llvm] diff --git a/src/bootstrap/defaults/bootstrap.dist.toml b/src/bootstrap/defaults/bootstrap.dist.toml index bb0592ce947a..cce3f068aabc 100644 --- a/src/bootstrap/defaults/bootstrap.dist.toml +++ b/src/bootstrap/defaults/bootstrap.dist.toml @@ -24,6 +24,8 @@ channel = "auto-detect" download-rustc = false # Build the llvm-bitcode-linker llvm-bitcode-linker = true +# Required to make builds reproducible. +remap-debuginfo = true [dist] # Use better compression when preparing tarballs. diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index e79391a107aa..2ca19f082693 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/147935 +Last change is for: https://github.com/rust-lang/rust/pull/153179 diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 93c7faf4f015..44eab9b87783 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -71,7 +71,7 @@ fn main() { // check_version warnings are not printed during setup, or during CI let changelog_suggestion = if matches!(config.cmd, Subcommand::Setup { .. }) - || config.is_running_on_ci + || config.is_running_on_ci() || config.dry_run() { None diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index adba8f19894a..46d05b9d5d2f 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -543,6 +543,9 @@ pub fn std_cargo( // `MACOSX_DEPLOYMENT_TARGET`, `IPHONEOS_DEPLOYMENT_TARGET`, etc. let mut cmd = builder.rustc_cmd(cargo.compiler()); cmd.arg("--target").arg(target.rustc_target_arg()); + // FIXME(#152709): -Zunstable-options is to handle JSON targets. + // Remove when JSON targets are stabilized. + cmd.arg("-Zunstable-options").env("RUSTC_BOOTSTRAP", "1"); cmd.arg("--print=deployment-target"); let output = cmd.run_capture_stdout(builder).stdout(); diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index eee960027a9f..28a7afd6c61a 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -87,6 +87,12 @@ fn run(self, builder: &Builder<'_>) -> Option { // from a shared directory. builder.run_default_doc_steps(); + // In case no default doc steps are run for host, it is possible that `/doc` directory + // is never created. + if !builder.config.dry_run() { + t!(fs::create_dir_all(builder.doc_out(host))); + } + let dest = "share/doc/rust/html"; let mut tarball = Tarball::new(builder, "rust-docs", &host.triple); @@ -2557,7 +2563,7 @@ pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, ), )] pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) { - let dst_libdir = sysroot.join(builder.sysroot_libdir_relative(Compiler::new(1, target))); + let dst_libdir = sysroot.join(builder.libdir_relative(Compiler::new(1, target))); // We do not need to copy LLVM files into the sysroot if it is not // dynamically linked; it is already included into librustc_llvm // statically. @@ -3084,7 +3090,7 @@ fn run(self, builder: &Builder<'_>) -> Self::Output { return None; } - if builder.config.is_running_on_ci { + if builder.config.is_running_on_ci() { assert_eq!( builder.config.gcc_ci_mode, GccCiMode::BuildLocally, diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 325f54d78a50..a918ae929d2e 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -953,13 +953,6 @@ fn run(self, builder: &Builder<'_>) { cargo.rustdocflag("--extern-html-root-url"); cargo.rustdocflag("ena=https://docs.rs/ena/latest/"); - // Point std library crate links to local docs for offline usage. - for krate in STD_PUBLIC_CRATES { - cargo.rustdocflag("--extern-html-root-url"); - cargo.rustdocflag(&format!("{krate}=../")); - } - cargo.rustdocflag("--extern-html-root-takes-precedence"); - let mut to_open = None; let out_dir = builder.stage_out(build_compiler, Mode::Rustc).join(target).join("doc"); diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index d487995e98a0..53cb03a41fc4 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -92,7 +92,7 @@ fn update_rustfmt_version(build: &Builder<'_>) { fn get_modified_rs_files(build: &Builder<'_>) -> Result>, String> { // In CI `get_git_modified_files` returns something different to normal environment. // This shouldn't be called in CI anyway. - assert!(!build.config.is_running_on_ci); + assert!(!build.config.is_running_on_ci()); if !verify_rustfmt_version(build) { return Ok(None); @@ -142,7 +142,7 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { // `--all` is specified or we are in CI. We check all files in CI to avoid bugs in // `get_modified_rs_files` letting regressions slip through; we also care about CI time less // since this is still very fast compared to building the compiler. - let all = all || build.config.is_running_on_ci; + let all = all || build.config.is_running_on_ci(); let mut builder = ignore::types::TypesBuilder::new(); builder.add_defaults(); diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index c04a8cd6b7ab..0c607cb6ba6f 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -23,7 +23,7 @@ use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; use crate::utils::helpers::{ - self, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date, + self, exe, get_clang_cl_resource_dir, libdir, t, unhashed_basename, up_to_date, }; use crate::{CLang, GitRepo, Kind, trace}; @@ -561,7 +561,6 @@ fn run(self, builder: &Builder<'_>) -> LlvmResult { } }; - // FIXME(ZuseZ4): Do we need that for Enzyme too? // When building LLVM with LLVM_LINK_LLVM_DYLIB for macOS, an unversioned // libLLVM.dylib will be built. However, llvm-config will still look // for a versioned path like libLLVM-14.dylib. Manually create a symbolic @@ -773,7 +772,15 @@ fn configure_cmake( .define("CMAKE_CXX_COMPILER", sanitize_cc(&cxx)) .define("CMAKE_ASM_COMPILER", sanitize_cc(&cc)); - cfg.build_arg("-j").build_arg(builder.jobs().to_string()); + // If we are running under a FIFO jobserver, we should not pass -j to CMake; otherwise it + // overrides the jobserver settings and can lead to oversubscription. + let has_modern_jobserver = env::var("MAKEFLAGS") + .map(|flags| flags.contains("--jobserver-auth=fifo:")) + .unwrap_or(false); + + if !has_modern_jobserver { + cfg.build_arg("-j").build_arg(builder.jobs().to_string()); + } let mut cflags = ccflags.cflags.clone(); // FIXME(madsmtm): Allow `cmake-rs` to select flags by itself by passing // our flags via `.cflag`/`.cxxflag` instead. @@ -1159,7 +1166,7 @@ fn run(self, builder: &Builder<'_>) -> Self::Output { let llvm_version_major = llvm::get_llvm_version_major(builder, &host_llvm_config); let lib_ext = std::env::consts::DLL_EXTENSION; let libenzyme = format!("libEnzyme-{llvm_version_major}"); - let build_dir = out_dir.join("lib"); + let build_dir = out_dir.join(libdir(target)); let dylib = build_dir.join(&libenzyme).with_extension(lib_ext); trace!("checking build stamp to see if we need to rebuild enzyme artifacts"); @@ -1197,7 +1204,16 @@ fn run(self, builder: &Builder<'_>) -> Self::Output { // hard to spot more relevant issues. let mut cflags = CcFlags::default(); cflags.push_all("-Wno-deprecated"); - configure_cmake(builder, target, &mut cfg, true, LdFlags::default(), cflags, &[]); + + // 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"); + } + + configure_cmake(builder, target, &mut cfg, true, ldflags, cflags, &[]); // Re-use the same flags as llvm to control the level of debug information // generated by Enzyme. diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index fda9f3bbba3a..3b0e7b2e30af 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -741,7 +741,13 @@ fn run(self, builder: &Builder<'_>) { // Run it again for mir-opt-level 4 to catch some miscompilations. if builder.config.test_args().is_empty() { - cargo.env("MIRIFLAGS", "-O -Zmir-opt-level=4 -Cdebug-assertions=yes"); + cargo.env( + "MIRIFLAGS", + format!( + "{} -O -Zmir-opt-level=4 -Cdebug-assertions=yes", + env::var("MIRIFLAGS").unwrap_or_default() + ), + ); // Optimizations can change backtraces cargo.env("MIRI_SKIP_UI_CHECKS", "1"); // `MIRI_SKIP_UI_CHECKS` and `RUSTC_BLESS` are incompatible @@ -805,6 +811,20 @@ fn run(self, builder: &Builder<'_>) { &[], ); + // If we are testing stage 2+ cargo miri, make sure that it works with the in-tree cargo. + // We want to do this *somewhere* to ensure that Miri + nightly cargo actually works. + if stage >= 2 { + let built_cargo = builder + .ensure(tool::Cargo::from_build_compiler( + // Build stage 1 cargo here, we don't need it to be built in any special way, + // just that it is built from in-tree sources. + builder.compiler(0, builder.host_target), + builder.host_target, + )) + .tool_path; + cargo.env("CARGO", built_cargo); + } + // We're not using `prepare_cargo_test` so we have to do this ourselves. // (We're not using that as the test-cargo-miri crate is not known to bootstrap.) match builder.doc_tests { @@ -1320,6 +1340,9 @@ fn run(self, builder: &Builder<'_>) { if builder.config.cmd.bless() { cmd.arg("--bless"); } + if builder.config.is_running_on_ci() { + cmd.arg("--ci=true"); + } if let Some(s) = builder.config.cmd.extra_checks().or(builder.config.tidy_extra_checks.as_deref()) { @@ -3091,6 +3114,17 @@ fn run(self, builder: &Builder<'_>) { // does not set this directly, but relies on the rustc wrapper to set it, and we are not using // the wrapper -- hence we have to set it ourselves. cargo.rustflag("-Zforce-unstable-if-unmarked"); + // Miri is told to invoke the libtest runner and bootstrap sets unstable flags + // for that runner. That only works when RUSTC_BOOTSTRAP is set. Bootstrap sets + // that flag but Miri by default does not forward the host environment to the test. + // Here we set up MIRIFLAGS to forward that env var. + cargo.env( + "MIRIFLAGS", + format!( + "{} -Zmiri-env-forward=RUSTC_BOOTSTRAP", + env::var("MIRIFLAGS").unwrap_or_default() + ), + ); cargo } else { // Also prepare a sysroot for the target. @@ -3416,6 +3450,8 @@ fn distcheck_plain_source_tarball(builder: &Builder<'_>, plain_src_dir: &Path) { command("./configure") .arg("--set") .arg("rust.omit-git-hash=false") + .arg("--set") + .arg("rust.remap-debuginfo=false") .args(&configure_args) .arg("--enable-vendor") .current_dir(plain_src_dir) @@ -3500,7 +3536,7 @@ fn is_default_step(builder: &Builder<'_>) -> bool { // Bootstrap tests might not be perfectly self-contained and can depend // on the environment, so only run them by default in CI, not locally. // See `test::Bootstrap::should_run`. - builder.config.is_running_on_ci + builder.config.is_running_on_ci() } fn make_run(run: RunConfig<'_>) { @@ -3539,7 +3575,7 @@ fn is_default_step(builder: &Builder<'_>) -> bool { // Bootstrap tests might not be perfectly self-contained and can depend on the external // environment, submodules that are checked out, etc. // Therefore we only run them by default on CI. - builder.config.is_running_on_ci + builder.config.is_running_on_ci() } /// Tests the build system itself. diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index e3fa826c45af..16065cbdae1f 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -2,8 +2,6 @@ use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; -use build_helper::ci::CiEnv; - use super::{Builder, Kind}; use crate::core::build_steps::test; use crate::core::build_steps::tool::SourceType; @@ -551,9 +549,17 @@ pub fn bare_cargo( assert_eq!(target, compiler.host); } - // Remove make-related flags to ensure Cargo can correctly set things up - cargo.env_remove("MAKEFLAGS"); - cargo.env_remove("MFLAGS"); + // Bootstrap only supports modern FIFO jobservers. Older pipe-based jobservers can run into + // "invalid file descriptor" errors, as the jobserver file descriptors are not inherited by + // scripts like bootstrap.py, while the environment variable is propagated. So, we pass + // MAKEFLAGS only if we detect a FIFO jobserver, otherwise we clear it. + let has_modern_jobserver = env::var("MAKEFLAGS") + .map(|flags| flags.contains("--jobserver-auth=fifo:")) + .unwrap_or(false); + + if !has_modern_jobserver { + cargo.env_remove("MAKEFLAGS"); + } cargo } @@ -773,6 +779,10 @@ fn cargo( .rustc_cmd(compiler) .arg("--target") .arg(target.rustc_target_arg()) + // FIXME(#152709): -Zunstable-options is to handle JSON targets. + // Remove when JSON targets are stabilized. + .arg("-Zunstable-options") + .env("RUSTC_BOOTSTRAP", "1") .arg("--print=file-names") .arg("--crate-type=proc-macro") .arg("-") @@ -999,8 +1009,18 @@ fn cargo( cargo.env("UPDATE_EXPECT", "1"); } - if !mode.is_tool() { - cargo.env("RUSTC_FORCE_UNSTABLE", "1"); + // Set an environment variable that tells the rustc/rustdoc wrapper + // binary to pass `-Zforce-unstable-if-unmarked` to the real compiler. + match mode { + // Any library crate that's part of the sysroot should be marked unstable + // (including third-party dependencies), unless it uses a staged_api + // `#![stable(..)]` attribute to explicitly mark itself stable. + Mode::Std | Mode::Codegen | Mode::Rustc => { + cargo.env("RUSTC_FORCE_UNSTABLE", "1"); + } + + // For everything else, crate stability shouldn't matter, so don't set a flag. + Mode::ToolBootstrap | Mode::ToolRustcPrivate | Mode::ToolStd | Mode::ToolTarget => {} } if let Some(x) = self.crt_static(target) { @@ -1316,7 +1336,7 @@ fn cargo( // Try to use a sysroot-relative bindir, in case it was configured absolutely. cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative()); - if CiEnv::is_ci() { + if self.config.is_running_on_ci() { // Tell cargo to use colored output for nicer logs in CI, even // though CI isn't printing to a terminal. // Also set an explicit `TERM=xterm` so that cargo doesn't warn diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap index 3adf952d66e0..294623f07386 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap @@ -79,7 +79,6 @@ expression: bench - Set({bench::compiler/rustc_public}) - Set({bench::compiler/rustc_public_bridge}) - Set({bench::compiler/rustc_query_impl}) - - Set({bench::compiler/rustc_query_system}) - Set({bench::compiler/rustc_resolve}) - Set({bench::compiler/rustc_sanitizers}) - Set({bench::compiler/rustc_serialize}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap index 1d6e63696b06..d5da908c8a44 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap @@ -61,7 +61,6 @@ expression: build compiler - Set({build::compiler/rustc_public}) - Set({build::compiler/rustc_public_bridge}) - Set({build::compiler/rustc_query_impl}) - - Set({build::compiler/rustc_query_system}) - Set({build::compiler/rustc_resolve}) - Set({build::compiler/rustc_sanitizers}) - Set({build::compiler/rustc_serialize}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap index 6fc2e190290e..242a2272b4d1 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap @@ -63,7 +63,6 @@ expression: check - Set({check::compiler/rustc_public}) - Set({check::compiler/rustc_public_bridge}) - Set({check::compiler/rustc_query_impl}) - - Set({check::compiler/rustc_query_system}) - Set({check::compiler/rustc_resolve}) - Set({check::compiler/rustc_sanitizers}) - Set({check::compiler/rustc_serialize}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap index c0456f7f84d3..dab86b792127 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap @@ -63,7 +63,6 @@ expression: check compiler - Set({check::compiler/rustc_public}) - Set({check::compiler/rustc_public_bridge}) - Set({check::compiler/rustc_query_impl}) - - Set({check::compiler/rustc_query_system}) - Set({check::compiler/rustc_resolve}) - Set({check::compiler/rustc_sanitizers}) - Set({check::compiler/rustc_serialize}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap index 10f36ffa6748..e43d5380a398 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap @@ -63,7 +63,6 @@ expression: check compiletest --include-default-paths - Set({check::compiler/rustc_public}) - Set({check::compiler/rustc_public_bridge}) - Set({check::compiler/rustc_query_impl}) - - Set({check::compiler/rustc_query_system}) - Set({check::compiler/rustc_resolve}) - Set({check::compiler/rustc_sanitizers}) - Set({check::compiler/rustc_serialize}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap index 492a10d3862a..827f2f8b60ac 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap @@ -78,7 +78,6 @@ expression: clippy - Set({clippy::compiler/rustc_public}) - Set({clippy::compiler/rustc_public_bridge}) - Set({clippy::compiler/rustc_query_impl}) - - Set({clippy::compiler/rustc_query_system}) - Set({clippy::compiler/rustc_resolve}) - Set({clippy::compiler/rustc_sanitizers}) - Set({clippy::compiler/rustc_serialize}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap index 41889cd12480..d380cb416acf 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap @@ -63,7 +63,6 @@ expression: fix - Set({fix::compiler/rustc_public}) - Set({fix::compiler/rustc_public_bridge}) - Set({fix::compiler/rustc_query_impl}) - - Set({fix::compiler/rustc_query_system}) - Set({fix::compiler/rustc_resolve}) - Set({fix::compiler/rustc_sanitizers}) - Set({fix::compiler/rustc_serialize}) 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 51e2c270e3ba..ac2f315d39d9 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 @@ -129,7 +129,6 @@ expression: test - Set({test::compiler/rustc_public}) - Set({test::compiler/rustc_public_bridge}) - Set({test::compiler/rustc_query_impl}) - - Set({test::compiler/rustc_query_system}) - Set({test::compiler/rustc_resolve}) - Set({test::compiler/rustc_sanitizers}) - Set({test::compiler/rustc_serialize}) 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 bc828c162bb0..09adbb0041ae 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 @@ -128,7 +128,6 @@ expression: test --skip=coverage - Set({test::compiler/rustc_public}) - Set({test::compiler/rustc_public_bridge}) - Set({test::compiler/rustc_query_impl}) - - Set({test::compiler/rustc_query_system}) - Set({test::compiler/rustc_resolve}) - Set({test::compiler/rustc_sanitizers}) - Set({test::compiler/rustc_serialize}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap index ceb910e4cb36..b5fccfcb966b 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap @@ -92,7 +92,6 @@ expression: test --skip=tests - Set({test::compiler/rustc_public}) - Set({test::compiler/rustc_public_bridge}) - Set({test::compiler/rustc_query_impl}) - - Set({test::compiler/rustc_query_system}) - Set({test::compiler/rustc_resolve}) - Set({test::compiler/rustc_sanitizers}) - Set({test::compiler/rustc_serialize}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap index f0e8f1aee2c7..9ad8914f58e3 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap @@ -72,7 +72,6 @@ expression: test --skip=tests --skip=coverage-map --skip=coverage-run --skip=lib - Set({test::compiler/rustc_public}) - Set({test::compiler/rustc_public_bridge}) - Set({test::compiler/rustc_query_impl}) - - Set({test::compiler/rustc_query_system}) - Set({test::compiler/rustc_resolve}) - Set({test::compiler/rustc_sanitizers}) - Set({test::compiler/rustc_serialize}) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 61db494c8c18..f34b284dbb9c 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength use std::env::VarError; use std::{panic, thread}; @@ -1815,7 +1816,7 @@ fn check_compiler_no_explicit_stage() { insta::assert_snapshot!( ctx.config("check") .path("compiler") - .render_steps(), @"[check] rustc 0 -> rustc 1 (74 crates)"); + .render_steps(), @"[check] rustc 0 -> rustc 1 (73 crates)"); } #[test] @@ -1841,7 +1842,7 @@ fn check_compiler_stage_1() { ctx.config("check") .path("compiler") .stage(1) - .render_steps(), @"[check] rustc 0 -> rustc 1 (74 crates)"); + .render_steps(), @"[check] rustc 0 -> rustc 1 (73 crates)"); } #[test] @@ -1851,11 +1852,11 @@ fn check_compiler_stage_2() { ctx.config("check") .path("compiler") .stage(2) - .render_steps(), @r" + .render_steps(), @" [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [check] rustc 1 -> rustc 2 (74 crates) + [check] rustc 1 -> rustc 2 (73 crates) "); } @@ -1866,12 +1867,12 @@ fn check_cross_compile() { ctx.config("check") .targets(&[TEST_TRIPLE_1]) .hosts(&[TEST_TRIPLE_1]) - .render_steps(), @r" + .render_steps(), @" [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [check] rustc 1 -> std 1 - [check] rustc 1 -> rustc 2 (74 crates) + [check] rustc 1 -> rustc 2 (73 crates) [check] rustc 1 -> rustc 2 [check] rustc 1 -> Rustdoc 2 [check] rustc 1 -> rustc_codegen_cranelift 2 @@ -1967,7 +1968,7 @@ fn check_library_skip_without_download_rustc() { ctx.config("check") .paths(&["library", "compiler"]) .args(&args) - .render_steps(), @"[check] rustc 0 -> rustc 1 (74 crates)"); + .render_steps(), @"[check] rustc 0 -> rustc 1 (73 crates)"); } #[test] @@ -2854,6 +2855,151 @@ fn clippy_bootstrap() { .render_steps(), @"[clippy] rustc 0 -> bootstrap 1 "); } + #[test] + fn install_plain() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("install") + .args(&[ + // Using backslashes fails with `--set` + "--set", &format!("install.prefix={}", ctx.normalized_dir()), + "--set", &format!("install.bindir={}", ctx.normalized_dir()), + "--set", &format!("install.libdir={}", ctx.normalized_dir()), + "--set", &format!("install.datadir={}", ctx.normalized_dir()), + "--set", &format!("install.mandir={}", ctx.normalized_dir()), + "--set", &format!("install.sysconfdir={}", ctx.normalized_dir()), + "--build", "x86_64-unknown-linux-gnu", + "--host", "x86_64-unknown-linux-gnu", + "--target", "x86_64-unknown-linux-gnu", + ]) + .get_steps() + .render_with(RenderConfig { + normalize_host: false + }), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) + [build] rustc 1 -> std 1 + [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] rustc 1 -> std 1 + [build] rustdoc 2 + [build] rustc 0 -> GenerateCopyright 1 + [dist] rustc + "); + } + + #[test] + fn install_src() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("install") + .path("src") + .args(&[ + // Using backslashes fails with `--set` + "--set", &format!("install.prefix={}", ctx.normalized_dir()), + "--set", &format!("install.bindir={}", ctx.normalized_dir()), + "--set", &format!("install.libdir={}", ctx.normalized_dir()), + "--set", &format!("install.datadir={}", ctx.normalized_dir()), + "--set", &format!("install.mandir={}", ctx.normalized_dir()), + "--set", &format!("install.sysconfdir={}", ctx.normalized_dir()), + "--build", "x86_64-unknown-linux-gnu", + "--host", "x86_64-unknown-linux-gnu", + "--target", "x86_64-unknown-linux-gnu", + ]) + .get_steps() + .render_with(RenderConfig { + normalize_host: false + }), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) + [build] rustc 1 -> std 1 + [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 <> + "); + } + + #[test] + fn install_src_no_docs() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("install") + .path("src") + .args(&[ + // Using backslashes fails with `--set` + "--set", &format!("install.prefix={}", ctx.normalized_dir()), + "--set", &format!("install.bindir={}", ctx.normalized_dir()), + "--set", &format!("install.libdir={}", ctx.normalized_dir()), + "--set", &format!("install.datadir={}", ctx.normalized_dir()), + "--set", &format!("install.mandir={}", ctx.normalized_dir()), + "--set", &format!("install.sysconfdir={}", ctx.normalized_dir()), + "--set", "build.docs=false", + "--build", "x86_64-unknown-linux-gnu", + "--host", "x86_64-unknown-linux-gnu", + "--target", "x86_64-unknown-linux-gnu", + ]) + .get_steps() + .render_with(RenderConfig { + normalize_host: false + }), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 0 -> RustInstaller 1 + [dist] docs + [dist] src <> + "); + } + #[test] fn install_extended() { let ctx = TestCtx::new(); @@ -2861,12 +3007,16 @@ fn install_extended() { ctx.config("install") .args(&[ // Using backslashes fails with `--set` - "--set", &format!("install.prefix={}", ctx.dir().display()).replace("\\", "/"), - "--set", &format!("install.sysconfdir={}", ctx.dir().display()).replace("\\", "/"), + "--set", &format!("install.prefix={}", ctx.normalized_dir()), + "--set", &format!("install.bindir={}", ctx.normalized_dir()), + "--set", &format!("install.libdir={}", ctx.normalized_dir()), + "--set", &format!("install.datadir={}", ctx.normalized_dir()), + "--set", &format!("install.mandir={}", ctx.normalized_dir()), + "--set", &format!("install.sysconfdir={}", ctx.normalized_dir()), "--set", "build.extended=true", // For Cranelift to be disted "--build", "x86_64-unknown-linux-gnu", - "--host", "x86_64-unknown-linux-gnu" + "--host", "x86_64-unknown-linux-gnu", ]) .get_steps() .render_with(RenderConfig { @@ -2930,6 +3080,45 @@ fn install_extended() { "); } + #[test] + fn cargo_miri_stage_1() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .args(&["cargo-miri"]) + .stage(1) + .get_steps() + .render(), @" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 0 -> miri 1 + [build] rustc 0 -> cargo-miri 1 + [build] rustdoc 1 + [build] rustc 1 -> std 1 + "); + } + + #[test] + fn cargo_miri_stage_2() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("test") + .args(&["cargo-miri"]) + .stage(2) + .get_steps() + .render(), @" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 1 -> miri 2 + [build] rustc 1 -> cargo-miri 2 + [build] rustdoc 2 + [build] rustc 2 -> std 2 + [build] rustc 0 -> cargo 1 + "); + } + // Check that `x run miri --target FOO` actually builds miri for the host. #[test] fn run_miri() { diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 61eef3c01592..17f256188e1b 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -88,7 +88,7 @@ /// filled out from the decoded forms of the structs below. For documentation /// on each field, see the corresponding fields in /// `bootstrap.example.toml`. -#[derive(Default, Clone)] +#[derive(Clone)] pub struct Config { pub change_id: Option, pub bypass_bootstrap_lock: bool, @@ -298,6 +298,7 @@ pub struct Config { // These are either the stage0 downloaded binaries or the locally installed ones. pub initial_cargo: PathBuf, pub initial_rustc: PathBuf, + pub initial_rustdoc: PathBuf, pub initial_cargo_clippy: Option, pub initial_sysroot: PathBuf, pub initial_rustfmt: Option, @@ -318,7 +319,7 @@ pub struct Config { /// Default value for `--extra-checks` pub tidy_extra_checks: Option, - pub is_running_on_ci: bool, + pub ci_env: CiEnv, /// Cache for determining path modifications pub path_modification_cache: Arc, PathFreshness>>>, @@ -456,6 +457,7 @@ pub(crate) fn parse_inner( build_dir: build_build_dir, cargo: mut build_cargo, rustc: mut build_rustc, + rustdoc: build_rustdoc, rustfmt: build_rustfmt, cargo_clippy: build_cargo_clippy, docs: build_docs, @@ -728,7 +730,11 @@ pub(crate) fn parse_inner( ); } - let is_running_on_ci = flags_ci.unwrap_or(CiEnv::is_ci()); + let ci_env = match flags_ci { + Some(true) => CiEnv::GitHubActions, + Some(false) => CiEnv::None, + None => CiEnv::current(), + }; let dwn_ctx = DownloadContext { path_modification_cache: path_modification_cache.clone(), src: &src, @@ -739,7 +745,7 @@ pub(crate) fn parse_inner( stage0_metadata: &stage0_metadata, llvm_assertions, bootstrap_cache_path: &build_bootstrap_cache_path, - is_running_on_ci, + ci_env, }; let initial_rustc = build_rustc.unwrap_or_else(|| { @@ -747,6 +753,9 @@ pub(crate) fn parse_inner( default_stage0_rustc_path(&out) }); + let initial_rustdoc = build_rustdoc + .unwrap_or_else(|| initial_rustc.with_file_name(exe("rustdoc", host_target))); + let initial_sysroot = t!(PathBuf::from_str( command(&initial_rustc) .args(["--print", "sysroot"]) @@ -1168,7 +1177,7 @@ pub(crate) fn parse_inner( // CI should always run stage 2 builds, unless it specifically states otherwise #[cfg(not(test))] - if flags_stage.is_none() && is_running_on_ci { + if flags_stage.is_none() && ci_env.is_running_in_ci() { match flags_cmd { Subcommand::Test { .. } | Subcommand::Miri { .. } @@ -1295,6 +1304,7 @@ pub(crate) fn parse_inner( ccache, change_id: toml.change_id.inner, channel, + ci_env, clippy_info, cmd: flags_cmd, codegen_tests: rust_codegen_tests.unwrap_or(true), @@ -1343,9 +1353,9 @@ pub(crate) fn parse_inner( initial_cargo, initial_cargo_clippy: build_cargo_clippy, initial_rustc, + initial_rustdoc, initial_rustfmt, initial_sysroot, - is_running_on_ci, jemalloc: rust_jemalloc.unwrap_or(false), jobs: Some(threads_from_config(flags_jobs.or(build_jobs).unwrap_or(0))), json_output: flags_json_output, @@ -1500,6 +1510,10 @@ pub fn dry_run(&self) -> bool { self.exec_ctx.dry_run() } + pub fn is_running_on_ci(&self) -> bool { + self.ci_env.is_running_in_ci() + } + pub fn is_explicit_stage(&self) -> bool { self.explicit_stage_from_cli || self.explicit_stage_from_config } @@ -1666,7 +1680,7 @@ pub(crate) fn download_rustc_commit(&self) -> Option<&str> { if !self.llvm_from_ci { // This happens when LLVM submodule is updated in CI, we should disable ci-rustc without an error // to not break CI. For non-CI environments, we should return an error. - if self.is_running_on_ci { + if self.is_running_on_ci() { println!("WARNING: LLVM submodule has changes, `download-rustc` will be disabled."); return None; } else { @@ -1788,8 +1802,7 @@ pub fn check_path_modifications(&self, paths: &[&'static str]) -> PathFreshness .unwrap() .entry(paths.to_vec()) .or_insert_with(|| { - check_path_modifications(&self.src, &self.git_config(), paths, CiEnv::current()) - .unwrap() + check_path_modifications(&self.src, &self.git_config(), paths, self.ci_env).unwrap() }) .clone() } @@ -2223,7 +2236,7 @@ pub fn download_ci_rustc_commit<'a>( return None; } - if dwn_ctx.is_running_on_ci { + if dwn_ctx.is_running_on_ci() { eprintln!("CI rustc commit matches with HEAD and we are in CI."); eprintln!( "`rustc.download-ci` functionality will be skipped as artifacts are not available." @@ -2267,7 +2280,7 @@ pub fn check_path_modifications_<'a>( dwn_ctx.src, &git_config(dwn_ctx.stage0_metadata), paths, - CiEnv::current(), + dwn_ctx.ci_env, ) .unwrap() }) @@ -2322,7 +2335,7 @@ pub fn parse_download_ci_llvm<'a>( } #[cfg(not(test))] - if b && dwn_ctx.is_running_on_ci && CiEnv::is_rust_lang_managed_ci_job() { + if b && dwn_ctx.is_running_on_ci() && CiEnv::is_rust_lang_managed_ci_job() { // On rust-lang CI, we must always rebuild LLVM if there were any modifications to it panic!( "`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead." @@ -2413,7 +2426,7 @@ pub(crate) fn update_submodule<'a>( let actual_hash = recorded .split_whitespace() .nth(2) - .unwrap_or_else(|| panic!("unexpected output `{recorded}`")); + .unwrap_or_else(|| panic!("unexpected output `{recorded}` when updating {relative_path}")); if actual_hash == checked_out_hash { // already checked out diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index e19604d4ab12..277ede8d7745 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -42,7 +42,7 @@ fn download_ci_llvm() { .config("check") .with_default_toml_config("llvm.download-ci-llvm = \"if-unchanged\"") .create_config(); - if if_unchanged_config.llvm_from_ci && if_unchanged_config.is_running_on_ci { + if if_unchanged_config.llvm_from_ci && if_unchanged_config.is_running_on_ci() { let has_changes = if_unchanged_config.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); assert!( @@ -491,13 +491,14 @@ fn test_exclude() { #[test] fn test_ci_flag() { let config = TestCtx::new().config("check").arg("--ci").arg("false").create_config(); - assert!(!config.is_running_on_ci); + assert!(!config.is_running_on_ci()); let config = TestCtx::new().config("check").arg("--ci").arg("true").create_config(); - assert!(config.is_running_on_ci); + assert!(config.is_running_on_ci()); + // If --ci flag is not added, is_running_on_ci() relies on if it is run on actual CI or not. let config = TestCtx::new().config("check").create_config(); - assert_eq!(config.is_running_on_ci, CiEnv::is_ci()); + assert_eq!(config.is_running_on_ci(), CiEnv::is_ci()); } #[test] diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs index 192f87558720..27bf753f6914 100644 --- a/src/bootstrap/src/core/config/toml/build.rs +++ b/src/bootstrap/src/core/config/toml/build.rs @@ -25,6 +25,7 @@ struct Build { build_dir: Option = "build-dir", cargo: Option = "cargo", rustc: Option = "rustc", + rustdoc: Option = "rustdoc", rustfmt: Option = "rustfmt", cargo_clippy: Option = "cargo-clippy", docs: Option = "docs", diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index bf8d0cf4d534..389956f14599 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -6,6 +6,7 @@ use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex, OnceLock}; +use build_helper::ci::CiEnv; use build_helper::git::PathFreshness; use xz2::bufread::XzDecoder; @@ -411,7 +412,13 @@ pub(crate) struct DownloadContext<'a> { pub stage0_metadata: &'a build_helper::stage0_parser::Stage0, pub llvm_assertions: bool, pub bootstrap_cache_path: &'a Option, - pub is_running_on_ci: bool, + pub ci_env: CiEnv, +} + +impl<'a> DownloadContext<'a> { + pub fn is_running_on_ci(&self) -> bool { + self.ci_env.is_running_in_ci() + } } impl<'a> AsRef> for DownloadContext<'a> { @@ -432,7 +439,7 @@ fn from(value: &'a Config) -> Self { stage0_metadata: &value.stage0_metadata, llvm_assertions: value.llvm_assertions, bootstrap_cache_path: &value.bootstrap_cache_path, - is_running_on_ci: value.is_running_on_ci, + ci_env: value.ci_env, } } } @@ -668,6 +675,7 @@ fn fix_bin_or_dylib(out: &Path, fname: &Path, exec_ctx: &ExecutionContext) { // the `.nix-deps` location. // // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). + // cc.lib: Needed similarly for `libstdc++.so.6`. // zlib: Needed as a system dependency of `libLLVM-*.so`. // patchelf: Needed for patching ELF binaries (see doc comment above). let nix_deps_dir = out.join(".nix-deps"); @@ -679,6 +687,7 @@ fn fix_bin_or_dylib(out: &Path, fname: &Path, exec_ctx: &ExecutionContext) { zlib patchelf stdenv.cc.bintools + stdenv.cc.cc.lib ]; } "; @@ -981,7 +990,7 @@ fn download_file<'a>( match url.split_once("://").map(|(proto, _)| proto) { Some("http") | Some("https") => download_http_with_retries( dwn_ctx.host_target, - dwn_ctx.is_running_on_ci, + dwn_ctx.is_running_on_ci(), dwn_ctx.exec_ctx, &tempfile, url, diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index e5327ab79ee2..ca8af279b92b 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -37,18 +37,6 @@ pub struct Finder { /// when the newly-bumped stage 0 compiler now knows about the formerly-missing targets. const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined - "x86_64-unknown-linux-gnuasan", - "thumbv7a-none-eabi", - "thumbv7a-none-eabihf", - "thumbv7r-none-eabi", - "thumbv7r-none-eabihf", - "thumbv8r-none-eabihf", - "armv6-none-eabi", - "armv6-none-eabihf", - "thumbv6-none-eabi", - "aarch64v8r-unknown-none", - "aarch64v8r-unknown-none-softfloat", - "s390x-unknown-none-softfloat", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index dfa29b5aa3cd..b9a914f53cec 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -340,13 +340,6 @@ pub enum Mode { } impl Mode { - pub fn is_tool(&self) -> bool { - match self { - Mode::ToolBootstrap | Mode::ToolRustcPrivate | Mode::ToolStd | Mode::ToolTarget => true, - Mode::Std | Mode::Codegen | Mode::Rustc => false, - } - } - pub fn must_support_dlopen(&self) -> bool { match self { Mode::Std | Mode::Codegen => true, @@ -542,9 +535,7 @@ pub fn new(mut config: Config) -> Build { initial_lld, initial_relative_libdir, initial_rustc: config.initial_rustc.clone(), - initial_rustdoc: config - .initial_rustc - .with_file_name(exe("rustdoc", config.host_target)), + initial_rustdoc: config.initial_rustdoc.clone(), initial_cargo: config.initial_cargo.clone(), initial_sysroot: config.initial_sysroot.clone(), local_rebuild: config.local_rebuild, diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index 0662ae304ac0..d010226f0dfd 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -223,7 +223,7 @@ fn default_compiler( let root = if let Some(path) = build.wasi_sdk_path.as_ref() { path } else { - if build.config.is_running_on_ci { + if build.config.is_running_on_ci() { panic!("ERROR: WASI_SDK_PATH must be configured for a -wasi target on CI"); } println!("WARNING: WASI_SDK_PATH not set, using default cc/cxx compiler"); diff --git a/src/bootstrap/src/utils/metrics.rs b/src/bootstrap/src/utils/metrics.rs index 9b1ccc32cb61..e685c64733c6 100644 --- a/src/bootstrap/src/utils/metrics.rs +++ b/src/bootstrap/src/utils/metrics.rs @@ -222,7 +222,7 @@ pub(crate) fn persist(&self, build: &Build) { format_version: CURRENT_FORMAT_VERSION, system_stats, invocations, - ci_metadata: get_ci_metadata(CiEnv::current()), + ci_metadata: get_ci_metadata(build.config.ci_env), }; t!(std::fs::create_dir_all(dest.parent().unwrap())); diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 55eba6c696c5..1d133a9c9e2f 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -179,7 +179,7 @@ fn render_test_outcome(&mut self, outcome: Outcome<'_>, test: &TestOutcome) { if self.builder.config.verbose_tests { self.render_test_outcome_verbose(outcome, test); - } else if self.builder.config.is_running_on_ci { + } else if self.builder.config.is_running_on_ci() { self.render_test_outcome_ci(outcome, test); } else { self.render_test_outcome_terse(outcome, test); diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index 764b89086cf2..e2a689c0f0cc 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -35,6 +35,11 @@ pub fn dir(&self) -> &Path { self.directory.path() } + /// Using backslashes fails with `--set` + pub fn normalized_dir(&self) -> String { + self.dir().to_string_lossy().replace("\\", "/") + } + /// Starts a new invocation of bootstrap that executes `kind` as its top level command /// (i.e. `x `). Returns a builder that configures the created config through CLI flags. pub fn config(&self, kind: &str) -> ConfigBuilder { diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile index 04b46226acfa..7bc91243a5e2 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile @@ -29,19 +29,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -ENV RUSTBUILD_FORCE_CLANG_BASED_TESTS 1 +ENV RUSTBUILD_FORCE_CLANG_BASED_TESTS="1" # llvm.use-linker conflicts with downloading CI LLVM -ENV NO_DOWNLOAD_CI_LLVM 1 +ENV NO_DOWNLOAD_CI_LLVM="1" -ENV RUST_CONFIGURE_ARGS \ - --build=aarch64-unknown-linux-gnu \ +ENV RUST_CONFIGURE_ARGS="--build=aarch64-unknown-linux-gnu \ --enable-debug \ --enable-lld \ --set llvm.use-linker=lld \ --set target.aarch64-unknown-linux-gnu.linker=clang \ --set target.aarch64-unknown-linux-gnu.cc=clang \ - --set target.aarch64-unknown-linux-gnu.cxx=clang++ + --set target.aarch64-unknown-linux-gnu.cxx=clang++" # This job appears to be checking two separate things: # - That we can build the compiler with `--enable-debug` @@ -52,6 +51,5 @@ ENV RUST_CONFIGURE_ARGS \ # Currently we only run the subset of tests with "clang" in their name. # - See also FIXME(#132034) -ENV SCRIPT \ - python3 ../x.py --stage 2 build && \ - python3 ../x.py --stage 2 test tests/run-make tests/run-make-cargo +ENV SCRIPT="python3 ../x.py --stage 2 build && \ + python3 ../x.py --stage 2 test tests/run-make tests/run-make-cargo" diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile index 095624d6fb71..5dbca7e7b675 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile @@ -37,20 +37,19 @@ 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=aarch64-unknown-linux-gnu \ +ENV RUST_CONFIGURE_ARGS="--build=aarch64-unknown-linux-gnu \ --llvm-root=/usr/lib/llvm-20 \ --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/ 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/host-aarch64/aarch64-gnu/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile index 4b61fd94a6cf..87bfc0766fbd 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile @@ -21,10 +21,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -ENV RUST_CONFIGURE_ARGS \ - --build=aarch64-unknown-linux-gnu \ +ENV RUST_CONFIGURE_ARGS="--build=aarch64-unknown-linux-gnu \ --enable-sanitizers \ --enable-profiler \ - --enable-compiler-docs -ENV SCRIPT python3 ../x.py --stage 2 test && \ - python3 ../x.py --stage 2 test src/tools/cargo + --enable-compiler-docs" +ENV SCRIPT="python3 ../x.py --stage 2 test && \ + python3 ../x.py --stage 2 test src/tools/cargo" diff --git a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile index 3abca36fe70d..750cafaca0b2 100644 --- a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile +++ b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile @@ -76,8 +76,7 @@ ENV HOSTS=aarch64-unknown-linux-gnu ENV CPATH=/usr/include/aarch64-linux-gnu/:$CPATH -ENV RUST_CONFIGURE_ARGS \ - --build=aarch64-unknown-linux-gnu \ +ENV RUST_CONFIGURE_ARGS="--build=aarch64-unknown-linux-gnu \ --enable-full-tools \ --enable-profiler \ --enable-sanitizers \ @@ -93,12 +92,12 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.jemalloc \ --set rust.bootstrap-override-lld=true \ --set rust.lto=thin \ - --set rust.codegen-units=1 + --set rust.codegen-units=1" -ENV SCRIPT python3 ../x.py build --set rust.debug=true opt-dist && \ +ENV SCRIPT="python3 ../x.py build --set rust.debug=true opt-dist && \ ./build/$HOSTS/stage1-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ - --host $HOSTS --target $HOSTS --include-default-paths build-manifest bootstrap + --host $HOSTS --target $HOSTS --include-default-paths build-manifest bootstrap enzyme" ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=clang -ENV LIBCURL_NO_PKG_CONFIG 1 -ENV DIST_REQUIRE_ALL_TOOLS 1 +ENV LIBCURL_NO_PKG_CONFIG="1" +ENV DIST_REQUIRE_ALL_TOOLS="1" diff --git a/src/ci/docker/host-x86_64/arm-android/Dockerfile b/src/ci/docker/host-x86_64/arm-android/Dockerfile index bc311be05808..21f038bffe59 100644 --- a/src/ci/docker/host-x86_64/arm-android/Dockerfile +++ b/src/ci/docker/host-x86_64/arm-android/Dockerfile @@ -32,9 +32,9 @@ ENV PATH=$PATH:/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin ENV TARGETS=arm-linux-androideabi -ENV RUST_CONFIGURE_ARGS --android-ndk=/android/ndk/ +ENV RUST_CONFIGURE_ARGS="--android-ndk=/android/ndk/" -ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target $TARGETS +ENV SCRIPT="python3 ../x.py --stage 2 test --host= --target $TARGETS" COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile b/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile index 015caafc5bd6..9a3545dbad8d 100644 --- a/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile @@ -82,7 +82,7 @@ RUN sh /scripts/sccache.sh COPY static/gitconfig /etc/gitconfig -ENV RUST_CONFIGURE_ARGS --qemu-armhf-rootfs=/tmp/rootfs -ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target arm-unknown-linux-gnueabihf +ENV RUST_CONFIGURE_ARGS="--qemu-armhf-rootfs=/tmp/rootfs" +ENV SCRIPT="python3 ../x.py --stage 2 test --host= --target arm-unknown-linux-gnueabihf" ENV NO_CHANGE_USER=1 diff --git a/src/ci/docker/host-x86_64/disabled/dist-aarch64-android/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-aarch64-android/Dockerfile index dea445c295c9..b3c46d5590f0 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-aarch64-android/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-aarch64-android/Dockerfile @@ -13,13 +13,12 @@ ENV DEP_Z_ROOT=/android/ndk/arm64-21/sysroot/usr/ ENV HOSTS=aarch64-linux-android -ENV RUST_CONFIGURE_ARGS \ - --aarch64-linux-android-ndk=/android/ndk/arm64-21 \ +ENV RUST_CONFIGURE_ARGS="--aarch64-linux-android-ndk=/android/ndk/arm64-21 \ --disable-rpath \ --enable-extended \ - --enable-cargo-openssl-static + --enable-cargo-openssl-static" -ENV SCRIPT python3 ../x.py dist --target $HOSTS --host $HOSTS +ENV SCRIPT="python3 ../x.py dist --target $HOSTS --host $HOSTS" COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile index f986c38ea02d..4403611f2f04 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile @@ -19,11 +19,10 @@ ENV DEP_Z_ROOT=/android/ndk/arm-14/sysroot/usr/ ENV HOSTS=armv7-linux-androideabi -ENV RUST_CONFIGURE_ARGS \ - --armv7-linux-androideabi-ndk=/android/ndk/arm \ +ENV RUST_CONFIGURE_ARGS="--armv7-linux-androideabi-ndk=/android/ndk/arm \ --disable-rpath \ --enable-extended \ - --enable-cargo-openssl-static + --enable-cargo-openssl-static" # We support api level 14, but api level 21 is required to build llvm. To # overcome this problem we use a ndk with api level 21 to build llvm and then @@ -32,12 +31,11 @@ ENV RUST_CONFIGURE_ARGS \ # level 14), the default linker behavior is to generate an error, to allow the # build to finish we use --warn-unresolved-symbols. Note that the missing # symbols does not affect std, only the compiler (llvm) and cargo (openssl). -ENV SCRIPT \ - python3 ../x.py --stage 2 build src/llvm --host $HOSTS --target $HOSTS && \ - (export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \ +ENV SCRIPT="python3 ../x.py --stage 2 build src/llvm --host $HOSTS --target $HOSTS && \ + (export RUSTFLAGS=\"-C link-arg=-Wl,--warn-unresolved-symbols\"; \ rm /android/ndk/arm && \ ln -s /android/ndk/arm-14 /android/ndk/arm && \ - python3 ../x.py dist --host $HOSTS --target $HOSTS) + python3 ../x.py dist --host $HOSTS --target $HOSTS)" COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile index 4dfbc7256078..f36901794b9c 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile @@ -19,11 +19,10 @@ ENV DEP_Z_ROOT=/android/ndk/x86-14/sysroot/usr/ ENV HOSTS=i686-linux-android -ENV RUST_CONFIGURE_ARGS \ - --i686-linux-android-ndk=/android/ndk/x86 \ +ENV RUST_CONFIGURE_ARGS="--i686-linux-android-ndk=/android/ndk/x86 \ --disable-rpath \ --enable-extended \ - --enable-cargo-openssl-static + --enable-cargo-openssl-static" # We support api level 14, but api level 21 is required to build llvm. To # overcome this problem we use a ndk with api level 21 to build llvm and then @@ -32,12 +31,11 @@ ENV RUST_CONFIGURE_ARGS \ # level 14), the default linker behavior is to generate an error, to allow the # build to finish we use --warn-unresolved-symbols. Note that the missing # symbols does not affect std, only the compiler (llvm) and cargo (openssl). -ENV SCRIPT \ - python3 ../x.py --stage 2 build src/llvm --host $HOSTS --target $HOSTS && \ - (export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \ +ENV SCRIPT="python3 ../x.py --stage 2 build src/llvm --host $HOSTS --target $HOSTS && \ + (export RUSTFLAGS=\"-C link-arg=-Wl,--warn-unresolved-symbols\"; \ rm /android/ndk/x86 && \ ln -s /android/ndk/x86-14 /android/ndk/x86 && \ - python3 ../x.py dist --host $HOSTS --target $HOSTS) + python3 ../x.py dist --host $HOSTS --target $HOSTS)" COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/disabled/dist-m68k-linux/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-m68k-linux/Dockerfile index cbac2310d0df..8444e94b2a28 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-m68k-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-m68k-linux/Dockerfile @@ -24,5 +24,5 @@ RUN sh /scripts/sccache.sh ENV HOSTS=m68k-unknown-linux-gnu -ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS="--host=$HOSTS --enable-extended" +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/host-x86_64/disabled/dist-powerpcspe-linux/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-powerpcspe-linux/Dockerfile index 34c487412145..2dd10d30a070 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-powerpcspe-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-powerpcspe-linux/Dockerfile @@ -23,5 +23,5 @@ RUN sh /scripts/sccache.sh ENV HOSTS=powerpc-unknown-linux-gnuspe -ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS="--enable-extended --disable-docs" +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/host-x86_64/disabled/dist-sparc64-linux/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-sparc64-linux/Dockerfile index a8e7583ccb01..e61251cbe047 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-sparc64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-sparc64-linux/Dockerfile @@ -23,5 +23,5 @@ RUN sh /scripts/sccache.sh ENV HOSTS=sparc64-unknown-linux-gnu -ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS="--enable-extended --disable-docs" +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/host-x86_64/disabled/dist-x86_64-android/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-x86_64-android/Dockerfile index d44779763e54..b92d5729cbb8 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-x86_64-android/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-x86_64-android/Dockerfile @@ -13,13 +13,12 @@ ENV DEP_Z_ROOT=/android/ndk/x86_64-21/sysroot/usr/ ENV HOSTS=x86_64-linux-android -ENV RUST_CONFIGURE_ARGS \ - --x86_64-linux-android-ndk=/android/ndk/x86_64-21 \ +ENV RUST_CONFIGURE_ARGS="--x86_64-linux-android-ndk=/android/ndk/x86_64-21 \ --disable-rpath \ --enable-extended \ - --enable-cargo-openssl-static + --enable-cargo-openssl-static" -ENV SCRIPT python3 ../x.py dist --target $HOSTS --host $HOSTS +ENV SCRIPT="python3 ../x.py dist --target $HOSTS --host $HOSTS" COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/Dockerfile index 5d594a80581b..7839c8d9494f 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/Dockerfile @@ -33,5 +33,5 @@ ENV \ ENV HOSTS=x86_64-unknown-dragonfly -ENV RUST_CONFIGURE_ARGS --enable-extended -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS="--enable-extended" +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile index 637b5fa22f97..6692e82847c3 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile @@ -43,10 +43,10 @@ RUN sh /scripts/sccache.sh ENV HOST=x86_64-unknown-haiku ENV TARGET=target.$HOST -ENV RUST_CONFIGURE_ARGS --disable-jemalloc \ +ENV RUST_CONFIGURE_ARGS="--disable-jemalloc \ --set=$TARGET.cc=x86_64-unknown-haiku-gcc \ --set=$TARGET.cxx=x86_64-unknown-haiku-g++ \ - --set=$TARGET.llvm-config=/bin/llvm-config-haiku -ENV EXTERNAL_LLVM 1 + --set=$TARGET.llvm-config=/bin/llvm-config-haiku" +ENV EXTERNAL_LLVM="1" -ENV SCRIPT python3 ../x.py dist --host=$HOST --target=$HOST +ENV SCRIPT="python3 ../x.py dist --host=$HOST --target=$HOST" diff --git a/src/ci/docker/host-x86_64/disabled/dist-x86_64-redox/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-x86_64-redox/Dockerfile index e9188b42f5d7..6a647ac162e4 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-x86_64-redox/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-x86_64-redox/Dockerfile @@ -18,5 +18,5 @@ ENV \ CC_x86_64_unknown_redox=x86_64-unknown-redox-gcc \ CXX_x86_64_unknown_redox=x86_64-unknown-redox-g++ -ENV RUST_CONFIGURE_ARGS --enable-extended -ENV SCRIPT python3 ../x.py dist --host='' --target x86_64-unknown-redox +ENV RUST_CONFIGURE_ARGS="--enable-extended" +ENV SCRIPT="python3 ../x.py dist --host= --target x86_64-unknown-redox" diff --git a/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile b/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile index a52c3839196f..fb647dedbc21 100644 --- a/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile @@ -91,9 +91,8 @@ RUN sh /scripts/sccache.sh # Avoid "fatal: detected dubious ownership in repository at '/checkout'" error RUN git config --global --add safe.directory /checkout -ENV RUST_CONFIGURE_ARGS \ - --qemu-riscv64-rootfs=/tmp/rootfs \ - --set target.riscv64gc-unknown-linux-gnu.linker=riscv64-linux-gnu-gcc -ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target riscv64gc-unknown-linux-gnu +ENV RUST_CONFIGURE_ARGS="--qemu-riscv64-rootfs=/tmp/rootfs \ + --set target.riscv64gc-unknown-linux-gnu.linker=riscv64-linux-gnu-gcc" +ENV SCRIPT="python3 ../x.py --stage 2 test --host= --target riscv64gc-unknown-linux-gnu" ENV NO_CHANGE_USER=1 diff --git a/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile index 996dacd71247..f0d54b0d33d6 100644 --- a/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile @@ -27,9 +27,8 @@ ENV CC_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-gcc \ ENV HOSTS=arm-unknown-linux-gnueabi -ENV RUST_CONFIGURE_ARGS \ - --enable-full-tools \ +ENV RUST_CONFIGURE_ARGS="--enable-full-tools \ --disable-docs \ --enable-sanitizers \ - --enable-profiler -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS + --enable-profiler" +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/host-x86_64/dist-arm-linux-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux-musl/Dockerfile index dc35285dbab9..99667d68ab7a 100644 --- a/src/ci/docker/host-x86_64/dist-arm-linux-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-arm-linux-musl/Dockerfile @@ -18,11 +18,10 @@ RUN sh /scripts/sccache.sh ENV HOSTS=aarch64-unknown-linux-musl -ENV RUST_CONFIGURE_ARGS \ - --enable-full-tools \ +ENV RUST_CONFIGURE_ARGS="--enable-full-tools \ --disable-docs \ --musl-root-aarch64=/usr/local/aarch64-linux-musl \ --enable-sanitizers \ --enable-profiler \ - --set target.aarch64-unknown-linux-musl.crt-static=false -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS + --set target.aarch64-unknown-linux-musl.crt-static=false" +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile index 475542b33568..08b0c2ad0453 100644 --- a/src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile @@ -25,5 +25,5 @@ ENV CC_arm_unknown_linux_gnueabihf=arm-unknown-linux-gnueabihf-gcc \ ENV HOSTS=arm-unknown-linux-gnueabihf -ENV RUST_CONFIGURE_ARGS --enable-full-tools --enable-profiler --disable-docs -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS="--enable-full-tools --enable-profiler --disable-docs" +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile index e718437aaaa3..c86bdbbc41b4 100644 --- a/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile @@ -25,5 +25,5 @@ ENV CC_armv7_unknown_linux_gnueabihf=armv7-unknown-linux-gnueabihf-gcc \ ENV HOSTS=armv7-unknown-linux-gnueabihf -ENV RUST_CONFIGURE_ARGS --enable-full-tools --enable-profiler --disable-docs -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS="--enable-full-tools --enable-profiler --disable-docs" +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile b/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile index 2a23d8aec237..9425f043b127 100644 --- a/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile @@ -40,14 +40,13 @@ ENV \ AR_armv7_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ CXX_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang++.sh -ENV RUST_CONFIGURE_ARGS \ - --enable-profiler \ +ENV RUST_CONFIGURE_ARGS="--enable-profiler \ --disable-docs \ --tools=cargo,clippy,rustdocs,rustfmt,rust-analyzer,rust-analyzer-proc-macro-srv,analysis,src,wasm-component-ld \ --enable-extended \ - --enable-sanitizers + --enable-sanitizers" -ENV SCRIPT python3 ../x.py dist --host=$TARGETS --target $TARGETS +ENV SCRIPT="python3 ../x.py dist --host=$TARGETS --target $TARGETS" COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh index c78b76a53f44..46d34cd001a9 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh @@ -9,6 +9,7 @@ python3 ../x.py build --set rust.debug=true opt-dist --include-default-paths \ build-manifest \ bootstrap \ + enzyme \ rustc_codegen_gcc # Use GCC for building GCC components, as it seems to behave badly when built with Clang diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 6c35105225c0..d62fe1a1aa92 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -337,6 +337,7 @@ docker \ --env DEPLOY \ --env DEPLOY_ALT \ --env CI \ + --env GIT_DISCOVERY_ACROSS_FILESYSTEM=1 \ --env GITHUB_ACTIONS \ --env GITHUB_REF \ --env GITHUB_STEP_SUMMARY="/checkout/obj/${SUMMARY_FILE}" \ diff --git a/src/ci/docker/scripts/stage_2_test_set1.sh b/src/ci/docker/scripts/stage_2_test_set1.sh index 3baff4b52215..ce33c6d72d67 100755 --- a/src/ci/docker/scripts/stage_2_test_set1.sh +++ b/src/ci/docker/scripts/stage_2_test_set1.sh @@ -4,6 +4,15 @@ set -ex # Run a subset of tests. Used to run tests in parallel in multiple jobs. +# When this job partition is run as part of PR CI, skip tidy to allow revealing more failures. The +# dedicated `tidy` job failing won't block other PR CI jobs from completing, and so tidy failures +# shouldn't inhibit revealing other failures in PR CI jobs. +if [ "$PR_CI_JOB" == "1" ]; then + echo "PR_CI_JOB set; skipping tidy" + SKIP_TIDY="--skip tidy" +fi + ../x.py --stage 2 test \ + ${SKIP_TIDY:+$SKIP_TIDY} \ --skip compiler \ --skip src diff --git a/src/ci/docker/scripts/stage_2_test_set2.sh b/src/ci/docker/scripts/stage_2_test_set2.sh index 872d758dce38..06b80b200a8c 100755 --- a/src/ci/docker/scripts/stage_2_test_set2.sh +++ b/src/ci/docker/scripts/stage_2_test_set2.sh @@ -4,7 +4,16 @@ set -ex # Run a subset of tests. Used to run tests in parallel in multiple jobs. +# When this job partition is run as part of PR CI, skip tidy to allow revealing more failures. The +# dedicated `tidy` job failing won't block other PR CI jobs from completing, and so tidy failures +# shouldn't inhibit revealing other failures in PR CI jobs. +if [ "$PR_CI_JOB" == "1" ]; then + echo "PR_CI_JOB set; skipping tidy" + SKIP_TIDY="--skip tidy" +fi + ../x.py --stage 2 test \ + ${SKIP_TIDY:+$SKIP_TIDY} \ --skip tests \ --skip coverage-map \ --skip coverage-run \ diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 2300904f22c1..0687d142a350 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -706,12 +706,13 @@ auto: # i686 has no dedicated job, build it here because this job is fast - name: dist-aarch64-llvm-mingw env: - SCRIPT: python x.py dist bootstrap --include-default-paths + SCRIPT: python x.py dist bootstrap enzyme --include-default-paths RUST_CONFIGURE_ARGS: >- --build=aarch64-pc-windows-gnullvm --target=aarch64-pc-windows-gnullvm,i686-pc-windows-gnullvm --enable-full-tools --enable-profiler + --enable-llvm-link-shared DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift CC_i686_pc_windows_gnullvm: i686-w64-mingw32-clang @@ -719,11 +720,12 @@ auto: - name: dist-x86_64-llvm-mingw env: - SCRIPT: python x.py dist bootstrap --include-default-paths + SCRIPT: python x.py dist bootstrap enzyme --include-default-paths RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-gnullvm --enable-full-tools --enable-profiler + --enable-llvm-link-shared DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows diff --git a/src/ci/run.sh b/src/ci/run.sh index b486f0525f40..215292965e65 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -116,7 +116,6 @@ if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then else RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp" fi - RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.remap-debuginfo" if [ "$DEPLOY_ALT" != "" ] && isLinux; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --debuginfo-level=2" @@ -139,6 +138,8 @@ if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-debug-assertions" fi else + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.remap-debuginfo=false" + # We almost always want debug assertions enabled, but sometimes this takes too # long for too little benefit, so we just turn them off. if [ "$NO_DEBUG_ASSERTIONS" = "" ]; then diff --git a/src/ci/scripts/free-disk-space-linux.sh b/src/ci/scripts/free-disk-space-linux.sh index ac3c9cfb28b8..ac06f3fad88c 100755 --- a/src/ci/scripts/free-disk-space-linux.sh +++ b/src/ci/scripts/free-disk-space-linux.sh @@ -4,6 +4,11 @@ set -euo pipefail # Free disk space on Linux GitHub action runners # Script inspired by https://github.com/jlumbroso/free-disk-space + +# we need ~50GB of space +space_target_kb=$((50 * 1024 * 1024)) + + isX86() { local arch arch=$(uname -m) @@ -247,12 +252,87 @@ cleanSwap() { free -h } +sufficientSpaceEarlyExit() { + local available_space_kb=$(df -k . --output=avail | tail -n 1) + + if [ "$available_space_kb" -ge "$space_target_kb" ]; then + echo "Sufficient disk space available (${available_space_kb}KB >= ${space_target_kb}KB). Skipping cleanup." + exit 0 + fi +} + +# Try to find a different drive to put our data on so we don't need to run cleanup. +# The availability of the disks we're probing isn't guaranteed, +# so this is opportunistic. +checkAlternative() { + local gha_alt_disk="/mnt" + + local available_space_kb=$(df -k "$gha_alt_disk" --output=avail | tail -n 1) + + # mount options that trade durability for performance + # ignore-tidy-linelength + local mntopts="defaults,discard,journal_async_commit,barrier=0,noauto_da_alloc,lazytime,data=writeback" + + # GHA has a 2nd disk mounted at /mnt that is almost empty. + # Check if it's a valid mountpoint and it has enough available space. + if mountpoint "$gha_alt_disk" && [ "$available_space_kb" -ge "$space_target_kb" ]; then + local blkdev=$(df -k "$gha_alt_disk" --output=source | tail -n 1) + echo "Sufficient space available on $blkdev mounted at $gha_alt_disk" + # see cleanSwap(), swapfile may be mounted under /mnt + sudo swapoff -a || true + + # unmount from original location + sudo umount "$gha_alt_disk" + + # remount under the obj dir which is used by docker scripts to write most + # of our build output. And apply optimized mount options while we're at it. + mkdir ./obj + if ! sudo mount $blkdev ./obj -o $mntopts; then + sudo dmesg | tail -n 20 # kernel log should have more details for mount failures + echo "::warning::Failed to remount $blkdev to ./obj with options: $mntopts" + return + fi + + # ensure current user can access everything. + # later scripts assume they have recursive access to obj + sudo chown -R "$USER":"$USER" ./obj + + # Exit from this script to avoid wasting time removing disk space, + # as we already have enough disk space in the alternative drive. + exit 0 + fi + + # ephemeral NVMe drives on AWS + for dev in /dev/nvme*n1; do + # check that it's a blockdev, not mounted and doesn't have partitions + if [ -b "$dev" ] && [ "$(mount | grep "$dev" | wc -l)" -eq 0 ] && [ "$(lsblk -no PARTTYPE "$dev" | grep . | wc -l)" -eq 0 ]; then + echo "Found unused block device $dev, creating filesystem" + if ! sudo mkfs.ext4 -E lazy_itable_init=1,lazy_journal_init=1 "$dev" ; then + sudo dmesg | tail -n 5 # kernel log should have more details for mkfs failures + echo "Failed to create ext4 filesystem on $dev" + continue + fi + mkdir ./obj + sudo mount "$dev" ./obj -o $mntopts + sudo chown -R "$USER":"$USER" ./obj + + exit 0 + fi + done +} + + + # Display initial disk space stats AVAILABLE_INITIAL=$(getAvailableSpace) printDF "BEFORE CLEAN-UP:" echo "" + +sufficientSpaceEarlyExit +checkAlternative + execAndMeasureSpaceChange cleanPackages "Unused packages" execAndMeasureSpaceChange cleanDocker "Docker images" execAndMeasureSpaceChange cleanSwap "Swap storage" diff --git a/src/doc/embedded-book b/src/doc/embedded-book index fe88fbb68391..99d0341ff4e0 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit fe88fbb68391a465680dd91109f0a151a1676f3e +Subproject commit 99d0341ff4e06757490af8fceee790c4ede50bc0 diff --git a/src/doc/reference b/src/doc/reference index addd0602c819..50a1075e879b 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit addd0602c819b6526b9cc97653b0fadca395528c +Subproject commit 50a1075e879be75aeec436252c84eef0fad489f4 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index bac931ef1673..5383db524711 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit bac931ef1673af63fb60c3d691633034713cca20 +Subproject commit 5383db524711c0c9c43c3ca9e5e706089672ed6a diff --git a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs index 6720267e14f3..4038f112d59f 100644 --- a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs @@ -159,6 +159,9 @@ fn lengthen_lines(content: &str, limit: usize) -> String { if in_html_div { continue; } + if line.trim_end().ends_with("
") { + continue; + } if ignore(line, in_code_block) || REGEX_SPLIT.is_match(line) { continue; } diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 209f4226eae7..b6e1b2bc55df 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -44e34e1ac6d7e69b40856cf1403d3da145319c30 +c78a29473a68f07012904af11c92ecffa68fcc75 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 a5dfd9a0e832..0623d176403e 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 @@ -1,12 +1,12 @@ # What Bootstrapping does [*Bootstrapping*][boot] is the process of using a compiler to compile itself. -More accurately, it means using an older compiler to compile a newer version of -the same compiler. +More accurately, it means using an older compiler to compile a newer version of the same compiler. This raises a chicken-and-egg paradox: where did the first compiler come from? -It must have been written in a different language. In Rust's case it was -[written in OCaml][ocaml-compiler]. However, it was abandoned long ago, and the +It must have been written in a different language. +In Rust's case, it was [written in OCaml][ocaml-compiler]. +However, it was abandoned long ago, and the only way to build a modern version of `rustc` is with a slightly less modern version. This is exactly how [`./x.py`] works: it downloads the current beta release of @@ -14,8 +14,8 @@ This is exactly how [`./x.py`] works: it downloads the current beta release of [`./x.py`]: https://github.com/rust-lang/rust/blob/HEAD/x.py -Note that this documentation mostly covers user-facing information. See -[bootstrap/README.md][bootstrap-internals] to read about bootstrap internals. +Note that this documentation mostly covers user-facing information. +See [bootstrap/README.md][bootstrap-internals] to read about bootstrap internals. [bootstrap-internals]: https://github.com/rust-lang/rust/blob/HEAD/src/bootstrap/README.md @@ -28,7 +28,8 @@ Note that this documentation mostly covers user-facing information. See - Stage 2: the truly current compiler - Stage 3: the same-result test -Compiling `rustc` is done in stages. Here's a diagram, adapted from Jynn +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. @@ -36,8 +37,7 @@ The `A`, `B`, `C`, and `D` show the ordering of the stages of bootstrapping. Blue nodes are downloaded, yellow nodes are built with the `stage0` compiler, and green nodes are built with the `stage1` -compiler. +lightgreen; color: black">green nodes are built with the `stage1` compiler. [rustconf22-talk]: https://www.youtube.com/watch?v=oUIjG-y4zaA @@ -61,8 +61,8 @@ graph TD ### Stage 0: the pre-compiled compiler The stage0 compiler is by default the very recent _beta_ `rustc` compiler and its -associated dynamic libraries, which `./x.py` will download for you. (You can -also configure `./x.py` to change stage0 to something else.) +associated dynamic libraries, which `./x.py` will download for you. +(You can also configure `./x.py` to change stage0 to something else.) The precompiled stage0 compiler is then used only to compile [`src/bootstrap`] and [`compiler/rustc`] with precompiled stage0 std. @@ -72,15 +72,15 @@ Therefore, to use a compiler with a std that is freshly built from the tree, you 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`). Both are staged, but in a staggered manner. +'target' or 'object' libraries (`std` and `rustc`). +Both are staged, but in a staggered manner. [`compiler/rustc`]: https://github.com/rust-lang/rust/tree/HEAD/compiler/rustc [`src/bootstrap`]: https://github.com/rust-lang/rust/tree/HEAD/src/bootstrap ### Stage 1: from current code, by an earlier compiler -The rustc source code is then compiled with the `stage0` compiler to produce the -`stage1` compiler. +The rustc source code is then compiled with the `stage0` compiler to produce the `stage1` compiler. ### Stage 2: the truly current compiler @@ -88,7 +88,8 @@ We then rebuild the compiler using `stage1` compiler with in-tree std to produce compiler. 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 +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`. @@ -96,8 +97,8 @@ 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`. -The `stage2` compiler is the one distributed with `rustup` and all other install -methods. However, it takes a very long time to build because one must first +The `stage2` compiler is the one distributed with `rustup` and all other install methods. +However, it takes a very long time to build because one must first build the new compiler with an older compiler and then use that to build the new compiler with itself. @@ -106,14 +107,14 @@ See [Building the compiler](../how-to-build-and-run.html#building-the-compiler). ### Stage 3: the same-result test -Stage 3 is optional. To sanity check our new compiler we can build the libraries -with the `stage2` compiler. The result ought to be identical to before, unless -something has broken. +Stage 3 is optional. +To sanity check our new compiler, we can build the libraries with the `stage2` compiler. +The result ought to be identical to before, unless something has broken. ### Building the stages -The script [`./x`] tries to be helpful and pick the stage you most likely meant -for each subcommand. Here are some `x` commands with their default stages: +The script [`./x`] tries to be helpful and pick the stage you most likely meant for each subcommand. +Here are some `x` commands with their default stages: - `check`: `--stage 1` - `clippy`: `--stage 1` @@ -126,8 +127,7 @@ for each subcommand. Here are some `x` commands with their default stages: You can always override the stage by passing `--stage N` explicitly. -For more information about stages, [see -below](#understanding-stages-of-bootstrap). +For more information about stages, [see below](#understanding-stages-of-bootstrap). [`./x`]: https://github.com/rust-lang/rust/blob/HEAD/x @@ -135,22 +135,23 @@ below](#understanding-stages-of-bootstrap). 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. Additionally, the compiler makes heavy use of -`nightly` features (`#![feature(...)]`). How can we resolve this problem? +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. +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`. -2. The build system sets `RUSTC_BOOTSTRAP=1`. This special variable means to + 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(...)]` - with a compiler that's not `nightly`. _Setting `RUSTC_BOOTSTRAP=1` should - never be used except when bootstrapping the compiler._ + with a compiler that's not `nightly`. + _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 @@ -165,15 +166,14 @@ This is a detailed look into the separate bootstrap stages. 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. +- 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_. #### Build artifacts -Anything you can build with `./x` is a _build artifact_. Build artifacts -include, but are not limited to: +Anything you can build with `./x` is a _build artifact_. +Build artifacts include, but are not limited to: - binaries, like `stage0-rustc/rustc-main` - shared objects, like `stage0-sysroot/rustlib/libstd-6fae108520cf72fe.so` @@ -184,26 +184,26 @@ include, but are not limited to: #### Examples -- `./x test tests/ui` means to build the `stage1` compiler and run `compiletest` - on it. If you're working on the compiler, this is normally the test command - you want. +- `./x test tests/ui` means to build the `stage1` compiler and run `compiletest` on it. + If you're working on the compiler, this is normally the test command you want. - `./x test --stage 0 library/std` means to run tests on the standard library - without building `rustc` from source ('build with `stage0`, then test the - artifacts'). If you're working on the standard library, this is normally the - test command you want. + without building `rustc` from source ('build with `stage0`, then test the artifacts'). + If you're working on the standard library, this is normally the test command you want. - `./x build --stage 0` means to build with the stage0 `rustc`. - `./x doc --stage 1` means to document using the stage0 `rustdoc`. #### Examples of what *not* to do - `./x test --stage 0 tests/ui` is not useful: it runs tests on the _beta_ - compiler and doesn't build `rustc` from source. Use `test tests/ui` instead, + compiler and doesn't build `rustc` from source. + Use `test tests/ui` instead, which builds `stage1` from source. - `./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). + 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` + `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 @@ -219,71 +219,72 @@ In each stage besides 0, two major steps are performed: 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 -compiler/rustc` is linking the stage N artifacts to the `std` built by the stage -N compiler. +compiler/rustc` is linking the stage N artifacts to the `std` built by the stage N compiler. ### Stages and `std` 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`) +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`). -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! +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. -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 +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. ### Cross-compiling rustc -*Cross-compiling* is the process of compiling code that will run on another -architecture. 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. +*Cross-compiling* is the process of compiling code that will run on another architecture. +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 sound because `stage1` +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. So it should be identical (and therefore +code you currently have checked out. +So it should be identical (and therefore ABI-compatible) to the `std` that `stage2/rustc` would compile. -However, when cross-compiling, `stage1` `std` will only run on the host. So the -`stage2` compiler has to recompile `std` for the target. +However, when cross-compiling, `stage1` `std` will only run on the host. +So, the `stage2` compiler has to recompile `std` for the target. (See in the table how `stage2` only builds non-host `std` targets). ### 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. 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. That includes (but is not limited to): +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. +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 +- 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`. -All the artifacts listed so far are *compiler* runtime dependencies. You can see -them with `rustc --print sysroot`: +All the artifacts listed so far are *compiler* runtime dependencies. +You can see them with `rustc --print sysroot`: ``` $ ls $(rustc --print sysroot)/lib @@ -293,8 +294,8 @@ librustc_driver-4f0cc9f50e53f0ba.so libtracing_attributes-e4be92c35ab2a33b.so librustc_macros-5f0ec4a119c6ac86.so rustlib ``` -There are also runtime dependencies for the standard library! These are in -`lib/rustlib/`, not `lib/` directly. +There are also runtime dependencies for the standard library! +These are in `lib/rustlib/`, not `lib/` directly. ``` $ ls $(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/lib | head -n 5 @@ -306,48 +307,50 @@ 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 +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. #### `-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 +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). The `-Z force-unstable-if-unmarked` flag has a variety of purposes to help -enforce that the correct crates are marked as `unstable`. It was introduced -primarily to allow rustc and the standard library to link to arbitrary crates on -crates.io which do not themselves use `staged_api`. `rustc` also relies on this +enforce that the correct crates are marked as `unstable`. +It was introduced primarily to allow rustc and the standard library to link to arbitrary crates on +crates.io which do not themselves use `staged_api`. +`rustc` also relies on this flag to mark all of its crates as `unstable` with the `rustc_private` feature so that each crate does not need to be carefully marked with `unstable`. This flag is automatically applied to all of `rustc` and the standard library by -the bootstrap scripts. This is needed because the compiler and all of its +the bootstrap scripts. +This is needed because the compiler and all of its dependencies are shipped in `sysroot` to all users. 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 +- 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`. Code which does not use `-Z force-unstable-if-unmarked` should include the -`#![feature(rustc_private)]` crate attribute to access these forced-unstable -crates. This is needed for things which link `rustc` itself, such as `Miri` or -`clippy`. +`#![feature(rustc_private)]` crate attribute to access these forced-unstable crates. +This is needed for things which link `rustc` itself, such as `Miri` or `clippy`. You can find more discussion about sysroots in: -- The [rustdoc PR] explaining why it uses `extern crate` for dependencies loaded - from `sysroot` +- The [rustdoc PR] explaining why it uses `extern crate` for dependencies loaded from `sysroot` - [Discussions about sysroot on Zulip](https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/deps.20in.20sysroot/) - [Discussions about building rustdoc out of @@ -358,31 +361,33 @@ You can find more discussion about sysroots in: ## Passing flags to commands invoked by `bootstrap` Conveniently `./x` allows you to pass stage-specific flags to `rustc` and -`cargo` when bootstrapping. The `RUSTFLAGS_BOOTSTRAP` environment variable is +`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. Finally, `MAGIC_EXTRA_RUSTFLAGS` bypasses the +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. - `RUSTDOCFLAGS`, `RUSTDOCFLAGS_BOOTSTRAP` and `RUSTDOCFLAGS_NOT_BOOTSTRAP` are analogous to `RUSTFLAGS`, but for `rustdoc`. - `CARGOFLAGS` will pass arguments to cargo itself (e.g. `--timings`). - `CARGOFLAGS_BOOTSTRAP` and `CARGOFLAGS_NOT_BOOTSTRAP` work analogously to - `RUSTFLAGS_BOOTSTRAP`. -- `--test-args` will pass arguments through to the test runner. For `tests/ui`, - this is `compiletest`. For unit tests and doc tests this is the `libtest` - runner. + `CARGOFLAGS_BOOTSTRAP` and `CARGOFLAGS_NOT_BOOTSTRAP` work analogously to `RUSTFLAGS_BOOTSTRAP`. +- `--test-args` will pass arguments through to the test runner. + For `tests/ui`, + this is `compiletest`. + For unit tests and doc tests, this is the `libtest` runner. Most test runners accept `--help`, 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. Otherwise, you get an error like the following: +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. +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 @@ -390,14 +395,12 @@ thread 'main' panicked at 'RUSTC_STAGE was not set: NotPresent', library/core/sr 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. +`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. 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. +method][env-vars] of configuring `C` compilers and `C` flags via environment variables. [cc-rs crate]: https://github.com/rust-lang/cc-rs [env-vars]: https://docs.rs/cc/latest/cc/#external-configuration-via-environment-variables @@ -406,8 +409,7 @@ variables. 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). -When you execute `x build --dry-run` command, the build output will be something -like the following: +When you execute `x build --dry-run` command, the build output will be something like the following: ```text Building stage0 library artifacts (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu) @@ -434,10 +436,10 @@ This copies the library and compiler artifacts from `cargo` into ### Assembling stage1 compiler This copies the libraries we built in "building `stage0` ... artifacts" into the -`stage1` compiler's `lib/` directory. These are the host libraries that the -compiler itself uses to run. These aren't actually used by artifacts the new -compiler generates. This step also copies the `rustc` and `rustdoc` binaries we -generated into `build/$HOST/stage/bin`. +`stage1` compiler's `lib/` directory. +These are the host libraries that the compiler itself uses to run. +These aren't actually used by artifacts the new compiler generates. +This step also copies the `rustc` and `rustdoc` binaries we generated into `build/$HOST/stage/bin`. The `stage1/bin/rustc` is a fully functional compiler built with stage0 (precompiled) compiler and std. To use a compiler built entirely from source with the in-tree compiler and std, you need to build the diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index 0014ba0e9a94..959d0687b7fb 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -442,6 +442,9 @@ If you're using the flake, make sure to also update it with the following comman nix flake update --flake ./src/tools/nix-dev-shell ``` +The shell creates a command named `x` that runs the `./x.py` script with all dependencies +set up correctly. + ### Note Note that when using nix on a not-NixOS distribution, it may be necessary to set diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index d4c599a6edf7..25f7eb27eeea 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -275,16 +275,16 @@ Here are some notable ones: |----------------|-------------| | `rustc_def_path` | Dumps the [`def_path_str`] of an item. | | `rustc_dump_def_parents` | Dumps the chain of `DefId` parents of certain definitions. | +| `rustc_dump_inferred_outlives` | Dumps implied bounds of an item. More precisely, the [`inferred_outlives_of`] an item. | | `rustc_dump_item_bounds` | Dumps the [`item_bounds`] of an item. | +| `rustc_dump_object_lifetime_defaults` | Dumps the [object lifetime defaults] of an item. | | `rustc_dump_predicates` | Dumps the [`predicates_of`] an item. | +| `rustc_dump_variances` | Dumps the [variances] of an item. | | `rustc_dump_vtable` | Dumps the vtable layout of an impl, or a type alias of a dyn type. | | `rustc_hidden_type_of_opaques` | Dumps the [hidden type of each opaque types][opaq] in the crate. | | `rustc_layout` | [See this section](#debugging-type-layouts). | -| `rustc_object_lifetime_default` | Dumps the [object lifetime defaults] of an item. | -| `rustc_outlives` | Dumps implied bounds of an item. More precisely, the [`inferred_outlives_of`] an item. | | `rustc_regions` | Dumps NLL closure region requirements. | | `rustc_symbol_name` | Dumps the mangled & demangled [`symbol_name`] of an item. | -| `rustc_variances` | Dumps the [variances] of an item. | Right below you can find elaborate explainers on a selected few. diff --git a/src/doc/rustc-dev-guide/src/const-generics.md b/src/doc/rustc-dev-guide/src/const-generics.md index 344d9b1d26de..3f84b99fb637 100644 --- a/src/doc/rustc-dev-guide/src/const-generics.md +++ b/src/doc/rustc-dev-guide/src/const-generics.md @@ -5,16 +5,20 @@ 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`], this is equivalent to `TyKind::Alias` and in the long term should be renamed (as well as introducing an `AliasConstKind` to parallel `ty::AliasKind`). -- [`ConstKind::Value`], this is the final value of a `ty::Const` after monomorphization. This is similar-ish to fully concrete to things like `TyKind::Str` or `TyKind::ADT`. +- [`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::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`. For a complete list of *all* kinds of const arguments and how they are actually represented in the type system, see the [`ConstKind`] type. -Inference Variables are quite boring and treated equivalently to type inference variables almost everywhere. Const Parameters are also similarly boring and equivalent to uses of type parameters almost everywhere. However, there are some interesting subtleties with how they are handled during parsing, name resolution, and AST lowering: [ambig-unambig-ty-and-consts]. +Inference Variables are quite boring and treated equivalently to type inference variables almost everywhere. +Const Parameters are also similarly boring and equivalent to uses of type parameters almost everywhere. +However, there are some interesting subtleties with how they are handled during parsing, name resolution, and AST lowering: [ambig-unambig-ty-and-consts]. ## Anon Consts -Anon Consts (short for anonymous const items) are how arbitrary expression are represented in const generics, for example an array length of `1 + 1` or `foo()` or even just `0`. These are unique to const generics and have no real type equivalent. +Anon Consts (short for anonymous const items) are how arbitrary expression are represented in const generics, for example an array length of `1 + 1` or `foo()` or even just `0`. +These are unique to const generics and have no real type equivalent. ### Desugaring @@ -31,7 +35,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::Unevaluated`]). Anon consts do not inherit any generic parameters of the item they are inside of: ```rust @@ -46,13 +50,19 @@ const ANON: usize = 1 + 1; type Alias = [T; ANON]; ``` -Note how the `ANON` const has no generic parameters or where clauses, even though `Alias` has both a type parameter `T` and a where clauses `T: Sized`. This desugaring is part of how we enforce that anon consts can't make use of generic parameters. +Note how the `ANON` const has no generic parameters or where clauses, even though `Alias` has both a type parameter `T` and a where clauses `T: Sized`. +This desugaring is part of how we enforce that anon consts can't make use of generic parameters. While it's useful to think of anon consts as being desugared to real const items, the compiler does not actually implement things this way. -At AST lowering time we do not yet know the *type* of the anon const, so we can't desugar to a real HIR item with an explicitly written type. To work around this we have [`DefKind::AnonConst`] and [`hir::Node::AnonConst`] which are used to represent these anonymous const items that can't actually be desugared. +At AST lowering time we do not yet know the *type* of the anon const, so we can't desugar to a real HIR item with an explicitly written type. +To work around this, we have [`DefKind::AnonConst`] and [`hir::Node::AnonConst`], +which are used to represent these anonymous const items that can't actually be desugared. -The types of these anon consts are obtainable from the [`type_of`] query. However, the `type_of` query does not actually contain logic for computing the type (infact it just ICEs when called), instead HIR Ty lowering is responsible for *feeding* the value of the `type_of` query for any anon consts that get lowered. HIR Ty lowering can determine the type of the anon const by looking at the type of the Const Parameter that the anon const is an argument to. +The types of these anon consts are obtainable from the [`type_of`] query. +However, the `type_of` query does not actually contain logic for computing the type (and, in fact, it just ICEs when called). +Instead, HIR Ty lowering is responsible for *feeding* the value of the `type_of` query for any anon consts that get lowered. +HIR Ty lowering can determine the type of the anon const by looking at the type of the Const Parameter that the anon const is an argument to. TODO: write a chapter on query feeding and link it here @@ -68,11 +78,12 @@ const ANON = 1 + 1; type Alias = [u8; ANON]; ``` -Where 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`. Effectively setting the type of the `ANON` const item during some later part of the compiler rather than when constructing the HIR. +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). -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 @@ -103,16 +114,19 @@ where (): Trait {} ``` -The second point is particularly subtle as it is very easy to get HIR Ty lowering wrong and not properly enforce that anon consts can't use generic parameters. The existing check is too conservative and accidentally permits some generic parameters to wind up in the body of the anon const [#144547](https://github.com/rust-lang/rust/issues/144547). +The second point is particularly subtle as it is very easy to get HIR Ty lowering wrong and not properly enforce that anon consts can't use generic parameters. +The existing check is too conservative and accidentally permits some generic parameters to wind up in the body of the anon const [#144547](https://github.com/rust-lang/rust/issues/144547). Erroneously allowing generic parameters in anon consts can sometimes lead to ICEs but can also lead to accepting illformed programs. -The third point is also somewhat subtle, by not inheriting any of the where clauses of the parent item we can't wind up with the trait solving inferring inference variables to generic parameters based off where clauses in scope that mention generic parameters. For example inferring `?x=T` from the expression `<() as Trait>::ASSOC` and an in scope where clause of `(): Trait`. +The third point is also somewhat subtle, by not inheriting any of the where clauses of the parent item we can't wind up with the trait solving inferring inference variables to generic parameters based off where clauses in scope that mention generic parameters. +For example, inferring `?x=T` from the expression `<() as Trait>::ASSOC` and an in-scope where clause of `(): Trait`. This also makes it much more likely that the compiler will ICE or atleast incidentally emit some kind of error if we *do* accidentally allow generic parameters in an anon const, as the anon const will have none of the necessary information in its environment to properly handle the generic parameters. #### Array repeat expressions -The one exception to all of the above is repeat counts of array expressions. As a *backwards compatibility hack* we allow the repeat count const argument to use generic parameters. +The one exception to all of the above is repeat counts of array expressions. +As a *backwards compatibility hack*, we allow the repeat count const argument to use generic parameters. ```rust fn foo() { @@ -124,14 +138,17 @@ However, to avoid most of the problems involved in allowing generic parameters i 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`. +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`. The implementation for this FCW can be found here: [`const_eval_resolve_for_typeck`] ### Incompatibilities with `generic_const_parameter_types` -Supporting const paramters such as `const N: [u8; M]` or `const N: Foo` does not work very nicely with the current anon consts setup. There are two reasons for this: -1. As anon consts cannot use generic parameters, their type *also* can't reference generic parameters. This means it is fundamentally not possible to use an anon const as an argument to a const parameeter whose type still references generic parameters. +Supporting const parameters such as `const N: [u8; M]` or `const N: Foo` does not work very nicely with the current anon consts setup. +There are two reasons for this: +1. As anon consts cannot use generic parameters, their type *also* can't reference generic parameters. + This means it is fundamentally not possible to use an anon const as an argument to a const parameter whose type still references generic parameters. ```rust #![feature(adt_const_params, generic_const_parameter_types)] @@ -144,7 +161,8 @@ Supporting const paramters such as `const N: [u8; M]` or `const N: Foo` does } ``` -2. We currently require knowing the type of anon consts when lowering them during HIR ty lowering. With generic const parameter types it may be the case that the currently known type contains inference variables (ie may not be fully known yet). +2. We currently require knowing the type of anon consts when lowering them during HIR ty lowering. + With generic const parameter types it may be the case that the currently known type contains inference variables (ie may not be fully known yet). ```rust #![feature(adt_const_params, generic_const_parameter_types)] @@ -158,22 +176,27 @@ Supporting const paramters such as `const N: [u8; M]` or `const N: Foo` does } ``` -It is currently unclear what the right way to make `generic_const_parameter_types` work nicely with the rest of const generics is. +It is currently unclear what the right way to make `generic_const_parameter_types` work nicely with the rest of const generics is. -`generic_const_exprs` would have allowed for anon consts with types referencing generic parameters, but that design wound up unworkable. +`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. ## Checking types of Const Arguments -In order for a const argument to be well formed it must have the same type as the const parameter it is an argument to. For example a const argument of type `bool` for an array length is not well formed, as an array's length parameter has type `usize`. +In order for a const argument to be well formed it must have the same type as the const parameter it is an argument to. +For example, a const argument of type `bool` for an array length is not well formed, as an array's length parameter has type `usize`. ```rust type Alias = [u8; B]; -//~^ ERROR: +//~^ ERROR: ``` -To check this we have [`ClauseKind::ConstArgHasType(ty::Const, Ty)`][const_arg_has_type], where for each Const Parameter defined on an item we also desugar an equivalent `ConstArgHasType` clause into its list of where cluases. This ensures that whenever we check wellformedness of anything by proving all of its clauses, we also check happen to check that all of the Const Arguments have the correct type. +To check this, we have [`ClauseKind::ConstArgHasType(ty::Const, Ty)`][const_arg_has_type], where, +for each Const Parameter defined on an item, +we also desugar an equivalent `ConstArgHasType` clause into its list of where cluases. +This ensures that whenever we check wellformedness of anything by proving all of its clauses, +we also check happen to check that all of the Const Arguments have the correct type. ```rust fn foo() {} @@ -186,13 +209,15 @@ where N: usize, {} ``` -Proving `ConstArgHasType` goals is implemented by first computing the type of the const argument, then equating it with the provided type. A rough outline of how the type of a Const Argument may be computed: +Proving `ConstArgHasType` goals is implemented by first computing the type of the const argument, then equating it with the provided type. +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 - 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. However, we do *indirectly* check the types of Const Arguments a different way in some cases. +`ConstArgHasType` is *the* soundness critical way that we check Const Arguments have the correct type. +However, we do *indirectly* check the types of Const Arguments a different way in some cases. ```rust type Alias = [u8; true]; @@ -203,7 +228,10 @@ const ANON: usize = true; type Alias = [u8; ANON]; ``` -By feeding the type of an anon const with the type of the Const Parameter we guarantee that the `ConstArgHasType` goal involving the anon const will succeed. In cases where the type of the anon const doesn't match the type of the Const Parameter what actually happens is a *type checking* error when type checking the anon const's body. +By feeding the type of an anon const with the type of the Const Parameter, +we guarantee that the `ConstArgHasType` goal involving the anon const will succeed. +In cases where the type of the anon const doesn't match the type of the Const Parameter, +what actually happens is a *type checking* error when type checking the anon const's body. 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`. diff --git a/src/doc/rustc-dev-guide/src/debugging-support-in-rustc.md b/src/doc/rustc-dev-guide/src/debugging-support-in-rustc.md index f2193e8abf98..e6984417086d 100644 --- a/src/doc/rustc-dev-guide/src/debugging-support-in-rustc.md +++ b/src/doc/rustc-dev-guide/src/debugging-support-in-rustc.md @@ -19,8 +19,9 @@ According to Wikipedia > other programs (the "target" program). Writing a debugger from scratch for a language requires a lot of work, especially if -debuggers have to be supported on various platforms. GDB and LLDB, however, can be -extended to support debugging a language. This is the path that Rust has chosen. +debuggers have to be supported on various platforms. +GDB and LLDB, however, can be extended to support debugging a language. +This is the path that Rust has chosen. This document's main goal is to document the said debuggers support in Rust compiler. ### DWARF @@ -35,7 +36,8 @@ According to the [DWARF] standard website > as well as in stand-alone environments. DWARF reader is a program that consumes the DWARF format and creates debugger compatible output. -This program may live in the compiler itself. DWARF uses a data structure called +This program may live in the compiler itself. + DWARF uses a data structure called Debugging Information Entry (DIE) which stores the information as "tags" to denote functions, variables etc., e.g., `DW_TAG_variable`, `DW_TAG_pointer_type`, `DW_TAG_subprogram` etc. You can also invent your own tags and attributes. @@ -45,7 +47,8 @@ You can also invent your own tags and attributes. [PDB] (Program Database) is a file format created by Microsoft that contains debug information. PDBs can be consumed by debuggers such as WinDbg/CDB and other tools to display debug information. A PDB contains multiple streams that describe debug information about a specific binary such -as types, symbols, and source files used to compile the given binary. CodeView is another +as types, symbols, and source files used to compile the given binary. +CodeView is another format which defines the structure of [symbol records] and [type records] that appear within PDB streams. @@ -61,15 +64,18 @@ and can parse only a subset of Rust expressions. GDB parser was written from scratch and has no relation to any other parser, including that of rustc. -GDB has Rust-like value and type output. It can print values and types in a way -that look like Rust syntax in the output. Or when you print a type as [ptype] in GDB, -it also looks like Rust source code. Checkout the documentation in the [manual for GDB/Rust]. +GDB has Rust-like value and type output. +It can print values and types in a way that look like Rust syntax in the output. +Or when you print a type as [ptype] in GDB, +it also looks like Rust source code. +Checkout the documentation in the [manual for GDB/Rust]. #### Parser extensions Expression parser has a couple of extensions in it to facilitate features that you cannot do -with Rust. Some limitations are listed in the [manual for GDB/Rust]. There is some special -code in the DWARF reader in GDB to support the extensions. +with Rust. +Some limitations are listed in the [manual for GDB/Rust]. +There is some special code in the DWARF reader in GDB to support the extensions. A couple of examples of DWARF reader support needed are as follows: @@ -81,12 +87,14 @@ A couple of examples of DWARF reader support needed are as follows: 2. Dissect trait objects: DWARF extension where the trait object's description in the DWARF also points to a stub description of the corresponding vtable which in turn points to the - concrete type for which this trait object exists. This means that you can do a `print *object` + concrete type for which this trait object exists. + This means that you can do a `print *object` for that trait object, and GDB will understand how to find the correct type of the payload in the trait object. **TODO**: Figure out if the following should be mentioned in the GDB-Rust document rather than -this guide page so there is no duplication. This is regarding the following comments: +this guide page so there is no duplication. +This is regarding the following comments: [This comment by Tom](https://github.com/rust-lang/rustc-dev-guide/pull/316#discussion_r284027340) > gdb's Rust extensions and limitations are documented in the gdb manual: @@ -102,7 +110,8 @@ document so there is no duplication etc.? #### Rust expression parser -This expression parser is written in C++. It is a type of [Recursive Descent parser]. +This expression parser is written in C++. +It is a type of [Recursive Descent parser]. It implements slightly less of the Rust language than GDB. LLDB has Rust-like value and type output. @@ -114,19 +123,21 @@ LLDB has Rust-like value and type output. ### WinDbg/CDB Microsoft provides [Windows Debugging Tools] such as the Windows Debugger (WinDbg) and -the Console Debugger (CDB) which both support debugging programs written in Rust. These -debuggers parse the debug info for a binary from the `PDB`, if available, to construct a +the Console Debugger (CDB) which both support debugging programs written in Rust. +These debuggers parse the debug info for a binary from the `PDB`, if available, to construct a visualization to serve up in the debugger. #### Natvis Both WinDbg and CDB support defining and viewing custom visualizations for any given type -within the debugger using the Natvis framework. The Rust compiler defines a set of Natvis +within the debugger using the Natvis framework. +The Rust compiler defines a set of Natvis files that define custom visualizations for a subset of types in the standard libraries such -as, `std`, `core`, and `alloc`. These Natvis files are embedded into `PDBs` generated by the +as, `std`, `core`, and `alloc`. +These Natvis files are embedded into `PDBs` generated by the `*-pc-windows-msvc` target triples to automatically enable these custom visualizations when -debugging. This default can be overridden by setting the `strip` rustc flag to either `debuginfo` -or `symbols`. +debugging. +This default can be overridden by setting the `strip` rustc flag to either `debuginfo` or `symbols`. Rust has support for embedding Natvis files for crates outside of the standard libraries by using the `#[debugger_visualizer]` attribute. @@ -147,17 +158,17 @@ We have some DWARF extensions that the Rust compiler emits and the debuggers und are _not_ in the DWARF standard. * Rust compiler will emit DWARF for a virtual table, and this `vtable` object will have a - `DW_AT_containing_type` that points to the real type. This lets debuggers dissect a trait object - pointer to correctly find the payload. E.g., here's such a DIE, from a test case in the gdb - repository: + `DW_AT_containing_type` that points to the real type. + This lets debuggers dissect a trait object pointer to correctly find the payload. + Here is an example of such a DIE, from a test case in the gdb repository: - ```asm - <1><1a9>: Abbrev Number: 3 (DW_TAG_structure_type) - <1aa> DW_AT_containing_type: <0x1b4> - <1ae> DW_AT_name : (indirect string, offset: 0x23d): vtable - <1b2> DW_AT_byte_size : 0 - <1b3> DW_AT_alignment : 8 - ``` + ```asm + <1><1a9>: Abbrev Number: 3 (DW_TAG_structure_type) + <1aa> DW_AT_containing_type: <0x1b4> + <1ae> DW_AT_name : (indirect string, offset: 0x23d): vtable + <1b2> DW_AT_byte_size : 0 + <1b3> DW_AT_alignment : 8 + ``` * The other extension is that the Rust compiler can emit a tagless discriminated union. See [DWARF feature request] for this item. @@ -165,7 +176,8 @@ are _not_ in the DWARF standard. ### Current limitations of DWARF * Traits - require a bigger change than normal to DWARF, on how to represent Traits in DWARF. -* DWARF provides no way to differentiate between Structs and Tuples. Rust compiler emits +* DWARF provides no way to differentiate between Structs and Tuples. + Rust compiler emits fields with `__0` and debuggers look for a sequence of such names to overcome this limitation. For example, in this case the debugger would look at a field via `x.__0` instead of `x.0`. This is resolved via the Rust parser in the debugger so now you can do `x.0`. @@ -189,40 +201,46 @@ According to Wikipedia, [System Integrity Protection] is > files and directories against modifications by processes without a specific "entitlement", > even when executed by the root user or a user with root privileges (sudo). -It prevents processes using `ptrace` syscall. If a process wants to use `ptrace` it has to be -code signed. The certificate that signs it has to be trusted on your machine. +It prevents processes using `ptrace` syscall. +If a process wants to use `ptrace` it has to be code signed. +The certificate that signs it has to be trusted on your machine. See [Apple developer documentation for System Integrity Protection]. -We may need to sign up with Apple and get the keys to do this signing. Tom has looked into if -Mozilla cannot do this because it is at the maximum number of -keys it is allowed to sign. Tom does not know if Mozilla could get more keys. +We may need to sign up with Apple and get the keys to do this signing. +Tom has looked into if Mozilla cannot do this because it is at the maximum number of +keys it is allowed to sign. +Tom does not know if Mozilla could get more keys. Alternatively, Tom suggests that maybe a Rust legal entity is needed to get the keys via Apple. -This problem is not technical in nature. If we had such a key we could sign GDB as well and -ship that. +This problem is not technical in nature. +If we had such a key, we could sign GDB as well and ship that. ### DWARF and Traits -Rust traits are not emitted into DWARF at all. The impact of this is calling a method `x.method()` -does not work as is. The reason being that method is implemented by a trait, as opposed -to a type. That information is not present so finding trait methods is missing. +Rust traits are not emitted into DWARF at all. +The impact of this is calling a method `x.method()` does not work as-is. +The reason being that method is implemented by a trait, as opposed to a type. +That information is not present, so finding trait methods is missing. -DWARF has a notion of interface types (possibly added for Java). Tom's idea was to use this -interface type as traits. +DWARF has a notion of interface types (possibly added for Java). +Tom's idea was to use this interface type as traits. -DWARF only deals with concrete names, not the reference types. So, a given implementation of a -trait for a type would be one of these interfaces (`DW_tag_interface` type). Also, the type for -which it is implemented would describe all the interfaces this type implements. This requires a -DWARF extension. +DWARF only deals with concrete names, not the reference types. +So, a given implementation of a +trait for a type would be one of these interfaces (`DW_tag_interface` type). +Also, the type for which it is implemented would describe all the interfaces this type implements. +This requires a DWARF extension. Issue on GitHub: [https://github.com/rust-lang/rust/issues/33014] ## Typical process for a Debug Info change (LLVM) -LLVM has Debug Info (DI) builders. This is the primary thing that Rust calls into. +LLVM has Debug Info (DI) builders. +This is the primary thing that Rust calls into. This is why we need to change LLVM first because that is emitted first and not DWARF directly. -This is a kind of metadata that you construct and hand-off to LLVM. For the Rustc/LLVM hand-off +This is a kind of metadata that you construct and hand-off to LLVM. +For the Rustc/LLVM hand-off, some LLVM DI builder methods are called to construct representation of a type. The steps of this process are as follows: @@ -246,7 +264,8 @@ The steps of this process are as follows: ### Procedural macro stepping A deeply profound question is that how do you actually debug a procedural macro? -What is the location you emit for a macro expansion? Consider some of the following cases - +What is the location you emit for a macro expansion? +Consider some of the following cases - * You can emit location of the invocation of the macro. * You can emit the location of the definition of the macro. @@ -254,9 +273,10 @@ What is the location you emit for a macro expansion? Consider some of the follow RFC: [https://github.com/rust-lang/rfcs/pull/2117] -Focus is to let macros decide what to do. This can be achieved by having some kind of attribute -that lets the macro tell the compiler where the line marker should be. This affects where you -set the breakpoints and what happens when you step it. +Focus is to let macros decide what to do. +This can be achieved by having some kind of attribute +that lets the macro tell the compiler where the line marker should be. +This affects where you set the breakpoints and what happens when you step it. ## Source file checksums in debug info @@ -264,16 +284,20 @@ Both DWARF and CodeView (PDB) support embedding a cryptographic hash of each sou contributed to the associated binary. The cryptographic hash can be used by a debugger to verify that the source file matches the -executable. If the source file does not match, the debugger can provide a warning to the user. +executable. +If the source file does not match, the debugger can provide a warning to the user. The hash can also be used to prove that a given source file has not been modified since it was -used to compile an executable. Because MD5 and SHA1 both have demonstrated vulnerabilities, +used to compile an executable. +Because MD5 and SHA1 both have demonstrated vulnerabilities, using SHA256 is recommended for this application. The Rust compiler stores the hash for each source file in the corresponding `SourceFile` in -the `SourceMap`. The hashes of input files to external crates are stored in `rlib` metadata. +the `SourceMap`. +The hashes of input files to external crates are stored in `rlib` metadata. -A default hashing algorithm is set in the target specification. This allows the target to +A default hashing algorithm is set in the target specification. +This allows the target to specify the best hash available, since not all targets support all hash algorithms. The hashing algorithm for a target can also be overridden with the `-Z source-file-checksum=` @@ -304,19 +328,21 @@ Clang always embeds an MD5 checksum, though this does not appear in documentatio * New demangler in `libiberty` (gcc source tree). * New demangler in LLVM or LLDB. -**TODO**: Check the location of the demangler source. [#1157](https://github.com/rust-lang/rustc-dev-guide/issues/1157) +**TODO**: Check the location of the demangler source. +[#1157](https://github.com/rust-lang/rustc-dev-guide/issues/1157) #### Reuse Rust compiler for expressions -This is an important idea because debuggers by and large do not try to implement type -inference. You need to be much more explicit when you type into the debugger than your -actual source code. So, you cannot just copy and paste an expression from your source -code to debugger and expect the same answer but this would be nice. This can be helped -by using compiler. +This is an important idea because debuggers by and large do not try to implement type inference. +You need to be much more explicit when you type into the debugger than your actual source code. +So, you cannot just copy and paste an expression from your source +code to debugger and expect the same answer, but this would be nice. +This can be helped by using compiler. -It is certainly doable but it is a large project. You certainly need a bridge to the -debugger because the debugger alone has access to the memory. Both GDB (gcc) and LLDB (clang) -have this feature. LLDB uses Clang to compile code to JIT and GDB can do the same with GCC. +It is certainly doable, but it is a large project. +You certainly need a bridge to the debugger because the debugger alone has access to the memory. +Both GDB (gcc) and LLDB (clang) have this feature. +LLDB uses Clang to compile code to JIT and GDB can do the same with GCC. Both debuggers expression evaluation implement both a superset and a subset of Rust. They implement just the expression language, diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index 1ed19663118f..bdd7a3dfa9b8 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -26,31 +26,35 @@ LL | more code - Level (`error`, `warning`, etc.). It indicates the severity of the message. (See [diagnostic levels](#diagnostic-levels)) -- Code (for example, for "mismatched types", it is `E0308`). It helps - users get more information about the current error through an extended - description of the problem in the error code index. Not all diagnostic have a - code. For example, diagnostics created by lints don't have one. -- Message. It is the main description of the problem. It should be general and - able to stand on its own, so that it can make sense even in isolation. -- Diagnostic window. This contains several things: +- Code (for example, for "mismatched types", it is `E0308`). + It helps users get more information about the current error through an extended + description of the problem in the error code index. + Not all diagnostic have a code. + For example, diagnostics created by lints don't have one. +- Message. + It is the main description of the problem. + It should be general and able to stand on its own, so that it can make sense even in isolation. +- Diagnostic window. + This contains several things: - The path, line number and column of the beginning of the primary span. - The users' affected code and its surroundings. - - Primary and secondary spans underlying the users' code. These spans can - optionally contain one or more labels. + - 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. + 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 + 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. -- 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 - order of the explanation might not correspond with the order of the code. If - the order of the explanation can be "order free", leveraging secondary labels +- 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 + order of the explanation might not correspond with the order of the 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 @@ -69,22 +73,23 @@ error: the identifier `foo.bar` is invalid ### Error codes and explanations -Most errors have an associated error code. Error codes are linked to long-form +Most errors have an associated error code. +Error codes are linked to long-form explanations which contains an example of how to trigger the error and in-depth -details about the error. They may be viewed with the `--explain` flag, or via -the [error index]. +details about the error. +They may be viewed with the `--explain` flag, or via the [error index]. As a general rule, give an error a code (with an associated explanation) if the -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, +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] 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. +[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]. @@ -94,29 +99,27 @@ process. ### Lints versus fixed diagnostics -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. +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. 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. -- [future-incompatible lints]: - these are silenceable lints. +- 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. +- [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. Some cases, such as warnings -with CLI flags, will require the use of hard-coded warnings. +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 use an error-level lint instead of a fixed error. @@ -125,56 +128,54 @@ use an error-level lint instead of a fixed error. ## Diagnostic output style guide -- Write in plain simple English. If your message, when shown on a – possibly +- Write in plain simple English. + If your message, when shown on a – possibly 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 messages should be succinct. 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. +- Error messages should be succinct. + 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. - Errors should document the span of code where they occur (use [`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. + `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 -- Try not to emit multiple error messages for the same error. This may require - detecting duplicates. +- 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, consult with the compiler team to add new attributes for library code that - allow adding more information. For example see - [`#[rustc_on_unimplemented]`](#rustc_on_unimplemented). Use these - annotations when available! + 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. -- 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 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. ### Lint naming -From [RFC 0344], lint names should be consistent, with the following -guidelines: +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 +*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). - Lint names should state the bad thing being checked for, e.g. `deprecated`, - so that `#[allow(deprecated)]` (items) reads correctly. Thus `ctypes` is not - an appropriate name; `improper_ctypes` is. + so that `#[allow(deprecated)]` (items) reads correctly. + Thus, `ctypes` is not an appropriate name; `improper_ctypes` is. - Lints that apply to arbitrary items (like the stability lints) should just - mention what they check for: use `deprecated` rather than - `deprecated_items`. This keeps lint names short. (Again, think "allow - *lint-name* items".) + mention what they check for: use `deprecated` rather than `deprecated_items`. + 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`. @@ -197,65 +198,64 @@ Guidelines for different diagnostic levels: - `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. Some - examples of when it is appropriate to issue a warning: + 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. - - Unnecessary syntax that can be removed without affecting the semantics of - the code. For example, unused code, or unnecessary `unsafe`. + 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 - language technically allows, and is not ready or confident enough to make - an error. For example `unused_comparisons` (out of bounds comparisons) or + language technically allows, and is not ready or confident enough to make an error. + Examples are `unused_comparisons` (out of bounds comparisons) or `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. - - 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. + - 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. - `help`: emitted following an `error` or `warning` to give additional - information to the user about how to solve their problem. These messages - often include a suggestion string and [`rustc_errors::Applicability`] - confidence level to guide automated source fixes by tools. See the - [Suggestions](#suggestions) section for more details. + information to the user about how to solve their problem. + These messages often include a suggestion string and [`rustc_errors::Applicability`] + confidence level to guide automated source fixes by tools. + See the [Suggestions](#suggestions) section for more details. The error or warning portion should *not* suggest how to fix the problem, only the "help" sub-diagnostic should. - `note`: emitted to give more context and identify additional circumstances - and parts of the code that caused the warning or error. For example, the - borrow checker will note any previous conflicting borrows. + and parts of the code that caused the warning or error. + For example, the borrow checker will note any previous conflicting borrows. `help` vs `note`: `help` should be used to show changes the user can - possibly make to fix the problem. `note` should be used for everything else, + possibly make to fix the problem. + `note` should be used for everything else, such as other context, information and facts, online resources to read, etc. Not to be confused with *lint levels*, whose guidelines are: - `forbid`: Lints should never default to `forbid`. -- `deny`: Equivalent to `error` diagnostic level. Some examples: +- `deny`: Equivalent to `error` diagnostic level. + Some examples: - - A future-incompatible or edition-based lint that has graduated from the - warning level. + - 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. -- `warn`: Equivalent to the `warning` diagnostic level. See `warning` above - for guidelines. +- `warn`: Equivalent to the `warning` diagnostic level. + See `warning` above for guidelines. - `allow`: Examples of the kinds of lints that should default to `allow`: - The lint has a too high false positive rate. - The lint is too opinionated. - The lint is experimental. - The lint is used for enforcing something that is not normally enforced. - For example, the `unsafe_code` lint can be used to prevent usage of unsafe - code. + For example, the `unsafe_code` lint can be used to prevent usage of unsafe code. More information about lint levels can be found in the [rustc book][rustc-lint-levels] and the [reference][reference-diagnostics]. @@ -270,16 +270,15 @@ book][rustc-lint-levels] and the [reference][reference-diagnostics]. There are three main ways to find where a given error is emitted: -- `grep` for either a sub-part of the error message/label or error code. This - usually works well and is straightforward, but there are some cases where +- `grep` for either a sub-part of the error message/label or error code. + This usually works well and is straightforward, but there are some cases where the code emitting the error is removed from the code where the error is - constructed behind a relatively deep call-stack. Even then, it is a good way - to get your bearings. + 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. Change the `1` to - something else if you wish to trigger on a later error. + 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: - Some calls get elided from the stack trace because they get inlined in the compiled `rustc`. @@ -296,7 +295,8 @@ order things are happening. ## `Span` [`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 +location in the code being compiled. +`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 @@ -310,21 +310,21 @@ similar methods on the `SourceMap`. ## Error messages -The [`rustc_errors`][errors] crate defines most of the utilities used for -reporting errors. +The [`rustc_errors`][errors] crate defines most of the utilities used for reporting errors. [errors]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/index.html -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]. Within the trait implementation, the APIs -described below can be used as normal. +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]. +Within the trait implementation, the APIs described below can be used as normal. [diagnostic-structs]: ./diagnostics/diagnostic-structs.md -[`DiagCtxt`][DiagCtxt] has methods that create and emit errors. These methods +[`DiagCtxt`][DiagCtxt] has methods that create and emit errors. +These methods usually have names like `span_err` or `struct_span_err` or `span_warn`, etc... There are lots of them; they emit different types of "errors", such as warnings, errors, fatal errors, suggestions, etc. @@ -332,10 +332,10 @@ warnings, errors, fatal errors, suggestions, etc. [DiagCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html 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, +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]. +[`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 @@ -344,8 +344,8 @@ identifiers for translatable diagnostics be used for new diagnostics (see [translation]: ./diagnostics/translation.md `Diag` allows you to add related notes and suggestions to an error -before emitting it by calling the [`emit`][emit] method. (Failing to either -emit or [cancel][cancel] a `Diag` will result in an ICE.) See the +before emitting it by calling the [`emit`][emit] method. +(Failing to either emit or [cancel] a `Diag` will result in an ICE.) See the [docs][diag] for more info on what you can do. [spanerr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.span_err @@ -384,7 +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. To this end, +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 is passed) as JSON for consumption by tools like [`rustfix`][rustfix]. @@ -395,25 +396,22 @@ is passed) as JSON for consumption by tools like [`rustfix`][rustfix]. 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. The last argument provides a hint to tools whether -the suggestion is mechanically applicable or not. +Be conservative when choosing the level. +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 replace their current content. -The message that accompanies them should be understandable in the following -contexts: +The message that accompanies them should be understandable in the following contexts: - shown as an independent sub-diagnostic (this is the default output) - shown as a label pointing at the affected span (this is done automatically if some heuristics for verbosity are met) - shown as a `help` sub-diagnostic with no content (used for cases where the -suggestion is obvious from the text, but we still want to let tools to apply -them) -- not shown (used for _very_ obvious cases, but we still want to allow tools to -apply them) +suggestion is obvious from the text, but we still want to let tools to apply them) +- not shown (used for _very_ obvious cases, but we still want to allow tools to apply them) [span_suggestion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html#method.span_suggestion @@ -474,8 +472,8 @@ The possible values of [`Applicability`][appl] are: - `MachineApplicable`: Can be applied mechanically. - `HasPlaceholders`: Cannot be applied mechanically because it has placeholder - text in the suggestions. For example: ```try adding a type: `let x: - ` ```. + text in the suggestions. + For example: ```try adding a type: `let x: ` ```. - `MaybeIncorrect`: Cannot be applied mechanically because the suggestion may or may not be a good one. - `Unspecified`: Cannot be applied mechanically because we don't know which @@ -485,43 +483,40 @@ The possible values of [`Applicability`][appl] are: ### Suggestion Style Guide -- Suggestions should not be a question. In particular, language like "did you - mean" should be avoided. Sometimes, it's unclear why a particular suggestion - is being made. In these cases, it's better to be upfront about what the - suggestion is. +- Suggestions should not be a question. + In particular, language like "did you mean" should be avoided. + Sometimes, it's unclear why a particular suggestion is being made. + In these cases, it's better to be upfront about what the suggestion is. - Compare "did you mean: `Foo`" vs. "there is a struct with a similar name: `Foo`". + Compare "did you mean: `Foo`" vs. + "there is a struct with a similar name: `Foo`". - The message should not contain any phrases like "the following", "as shown", etc. Use the span to convey what is being talked about. -- The message may contain further instruction such as "to do xyz, use" or "to do - xyz, use abc". -- The message may contain a name of a function, variable, or type, but avoid - whole expressions. +- The message may contain further instruction such as "to do xyz, use" or "to do xyz, use abc". +- The message may contain a name of a function, variable, or type, but avoid whole expressions. ## Lints -The compiler linting infrastructure is defined in the [`rustc_middle::lint`][rlint] -module. +The compiler linting infrastructure is defined in the [`rustc_middle::lint`][rlint] module. [rlint]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/index.html ### When do lints run? -Different lints will run at different times based on what information the lint -needs to do its job. Some lints get grouped into *passes* where the lints -within a pass are processed together via a single visitor. Some of the passes -are: +Different lints will run at different times based on what information the lint needs to do its job. +Some lints get grouped into *passes* where the lints +within a pass are processed together via a single visitor. +Some of the passes are: -- Pre-expansion pass: Works on [AST nodes] before [macro expansion]. This - should generally be avoided. +- Pre-expansion pass: Works on [AST nodes] before [macro expansion]. + This should generally be avoided. - Example: [`keyword_idents`] checks for identifiers that will become - keywords in future editions, but is sensitive to identifiers used in - macros. + 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]. These lints are for purely - syntactical lints. + 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. @@ -532,22 +527,24 @@ are: uninitialized values) is a late lint because it needs type information to figure out whether a type allows being left uninitialized. -- MIR pass: Works on [MIR nodes]. This isn't quite the same as other passes; +- MIR pass: Works on [MIR nodes]. + This isn't quite the same as other passes; lints that work on MIR nodes have their own methods for running. - 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 -a specific `check` function). 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 +a specific `check` function). +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. -Some of these inline lints fire before the linting system is ready. Those -lints will be *buffered* where they are held until later phases of the -compiler when the linting system is ready. See [Linting early in the -compiler](#linting-early-in-the-compiler). +Some of these inline lints fire before the linting system is ready. +Those lints will be *buffered* where they are held until later phases of the +compiler when the linting system is ready. +See [Linting early in the compiler](#linting-early-in-the-compiler). [AST nodes]: the-parser.md @@ -564,30 +561,28 @@ compiler](#linting-early-in-the-compiler). ### Lint definition terms -Lints are managed via the [`LintStore`][LintStore] and get registered in -various ways. The following terms refer to the different classes of lints +Lints are managed via the [`LintStore`][LintStore] and get registered in various ways. +The following terms refer to the different classes of lints generally based on how they are registered. - *Built-in* lints are defined inside the compiler source. - *Driver-registered* lints are registered when the compiler driver is created - by an external driver. This is the mechanism used by Clippy, for example. + by an external driver. + This is the mechanism used by Clippy, for example. - *Tool* lints are lints with a path prefix like `clippy::` or `rustdoc::`. - *Internal* lints are the `rustc::` scoped tool lints that only run on the - rustc source tree itself and are defined in the compiler source like a - regular built-in lint. + rustc source tree itself and are defined in the compiler source like a regular built-in lint. -More information about lint registration can be found in the [LintStore] -chapter. +More information about lint registration can be found in the [LintStore] chapter. [LintStore]: diagnostics/lintstore.md ### Declaring a lint -The built-in compiler lints are defined in the [`rustc_lint`][builtin] -crate. Lints that need to be implemented in other crates are defined in -[`rustc_lint_defs`]. You should prefer to place lints in `rustc_lint` if -possible. One benefit is that it is close to the dependency root, so it can be -much faster to work on. +The built-in compiler lints are defined in the [`rustc_lint`][builtin] crate. +Lints that need to be implemented in other crates are defined in [`rustc_lint_defs`]. +You should prefer to place lints in `rustc_lint` if possible. +One benefit is that it is close to the dependency root, so it can be much faster to work on. [builtin]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/index.html [`rustc_lint_defs`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/index.html @@ -596,12 +591,11 @@ 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). 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. +as the linter walks the AST. +You can then choose to emit lints in a very similar way to compile errors. -You also declare the metadata of a particular lint via the [`declare_lint!`] -macro. This macro includes the name, the default level, a short description, and some -more details. +You also declare the metadata of a particular lint via the [`declare_lint!`] macro. +This macro includes the name, the default level, a short description, and some more details. Note that the lint and the lint pass must be registered with the compiler. @@ -673,7 +667,8 @@ example-use-loop = denote infinite loops with `loop {"{"} ... {"}"}` ### Edition-gated lints -Sometimes we want to change the behavior of a lint in a new edition. To do this, +Sometimes we want to change the behavior of a lint in a new edition. +To do this, we just add the transition to our invocation of `declare_lint!`: ```rust,ignore @@ -692,8 +687,8 @@ See [Edition-specific lints](./guides/editions.md#edition-specific-lints) for mo ### Feature-gated lints -Lints belonging to a feature should only be usable if the feature is enabled in the -crate. To support this, lint declarations can contain a feature gate like so: +Lints belonging to a feature should only be usable if the feature is enabled in the crate. +To support this, lint declarations can contain a feature gate like so: ```rust,ignore declare_lint! { @@ -710,10 +705,10 @@ 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. In general, future-incompatible code -exists for two reasons: -* The user has written unsound code that the compiler mistakenly accepted. While -it is within Rust's backwards compatibility guarantees to fix the soundness hole +written 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. + 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". @@ -723,8 +718,7 @@ typically seen in the various "edition compatibility" lint groups (e.g., `rust_2 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": +A future-incompatible lint should be declared with the `@future_incompatible` additional "field": ```rust,ignore declare_lint! { @@ -739,7 +733,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" +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 parameters will no longer compile in Rust 2018 and beyond. @@ -750,14 +745,14 @@ 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. +`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 +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. @@ -771,9 +766,10 @@ store.register_renamed("single_use_lifetime", "single_use_lifetimes"); ### Lint groups -Lints can be turned on in groups. These groups are declared in the -[`register_builtins`][rbuiltins] function in [`rustc_lint::lib`][builtin]. The -`add_lint_group!` macro is used to declare a new group. +Lints can be turned on in groups. +These groups are declared in the +[`register_builtins`][rbuiltins] function in [`rustc_lint::lib`][builtin]. +The `add_lint_group!` macro is used to declare a new group. [rbuiltins]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_builtins.html @@ -787,15 +783,17 @@ add_lint_group!(sess, NON_UPPER_CASE_GLOBALS); ``` -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 +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. -Some lint groups are created automatically in `LintStore::register_lints`. For instance, +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. Editions also have their own lint groups +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. @@ -806,10 +804,10 @@ has been initialized (e.g. during parsing or macro expansion). This is problematic because we need to have computed lint levels to know whether we should emit a warning or an error or nothing at all. -To solve this problem, we buffer the lints until the linting system is -processed. [`Session`][sessbl] and [`ParseSess`][parsebl] both have -`buffer_lint` methods that allow you to buffer a lint for later. The linting -system automatically takes care of handling buffered lints later. +To solve this problem, we buffer the lints until the linting system is processed. +[`Session`][sessbl] and [`ParseSess`][parsebl] both have +`buffer_lint` methods that allow you to buffer a lint for later. +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 @@ -820,19 +818,20 @@ 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. That's troublesome! +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. +That's troublesome! -To solve this, `rustc_ast` defines its own buffered lint type, which -`ParseSess::buffer_lint` uses. After macro expansion, these buffered lints are +To solve this, `rustc_ast` defines its own buffered lint type, which `ParseSess::buffer_lint` uses. +After macro expansion, these buffered lints are then dumped into the `Session::buffered_lints` used by the rest of the compiler. ## JSON diagnostic output The compiler accepts an `--error-format json` flag to output -diagnostics as JSON objects (for the benefit of tools such as `cargo -fix`). It looks like this: +diagnostics as JSON objects (for the benefit of tools such as `cargo fix`). +It looks like this: ```console $ rustc json_error_demo.rs --error-format json @@ -844,10 +843,9 @@ $ rustc json_error_demo.rs --error-format json 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?) +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?) 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 @@ -860,15 +858,16 @@ The "human" readable and the json format emitter can be found under The JSON emitter defines [its own `Diagnostic` struct](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/json/struct.Diagnostic.html) -(and sub-structs) for the JSON serialization. Don't confuse this with +(and sub-structs) for the JSON serialization. +Don't confuse this with [`errors::Diag`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html)! ## `#[rustc_on_unimplemented]` This attribute allows trait definitions to modify error messages when an implementation was -expected but not found. The string literals in the attribute are format strings and can be -formatted with named parameters. See the Formatting -section below for what parameters are permitted. +expected but not found. +The string literals in the attribute are format strings and can be formatted with named parameters. +See the Formatting section below for what parameters are permitted. ```rust,ignore #[rustc_on_unimplemented(message = "an iterator over \ @@ -942,21 +941,22 @@ 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. + 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. This flag can also be matched on, see below. + 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"`: - - `cause`: Match against one variant of the `ObligationCauseCode` - enum. Only `"MainFunctionType"` is supported. - - `from_desugaring`: Match against a particular variant of the `DesugaringKind` - enum. The desugaring is identified by its variant name, for example + - `cause`: Match against one variant of the `ObligationCauseCode` enum. + Only `"MainFunctionType"` is supported. + - `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"`. - + The compiler can provide several values to match on, for example: - the self_ty, pretty printed with and without type arguments resolved. - `"{integral}"`, if self_ty is an integral of which the type is known. @@ -1014,14 +1014,15 @@ pub trait From: Sized { } ``` -### Formatting +### Formatting The string literals are format strings that accept parameters wrapped in braces but positional and listed parameters and format specifiers are not accepted. The following parameter names are valid: - `Self` and all generic parameters of the trait. - `This`: the name of the trait the attribute is on, without generics. -- `Trait`: the name of the "sugared" trait. See `TraitRefPrintSugared`. +- `Trait`: the name of the "sugared" trait. + See `TraitRefPrintSugared`. - `ItemContext`: the kind of `hir::Node` we're in, things like `"an async block"`, `"a function"`, `"an async function"`, etc. @@ -1042,7 +1043,7 @@ fn main() { } ``` -Will format the message into +Will format the message into ```text "Self = `i8`, T = `i32`, this = `From`, trait = `From`, context = `a function`" ``` 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 2260b1ec4df1..a3a7a11258c8 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md @@ -1,23 +1,21 @@ # Diagnostic and subdiagnostic structs -rustc has three diagnostic traits that can be used to create diagnostics: -`Diagnostic`, `LintDiagnostic`, and `Subdiagnostic`. +rustc has two diagnostic traits that can be used to create diagnostics: +`Diagnostic` and `Subdiagnostic`. For simple diagnostics, derived impls can be used, e.g. `#[derive(Diagnostic)]`. They are only suitable for simple diagnostics that -don't require much logic in deciding whether or not to add additional -subdiagnostics. +don't require much logic in deciding whether or not to add additional subdiagnostics. In cases where diagnostics require more complex or dynamic behavior, such as conditionally adding subdiagnostics, customizing the rendering logic, or selecting messages at runtime, you will need to manually implement -the corresponding trait (`Diagnostic`, `LintDiagnostic`, or `Subdiagnostic`). +the corresponding trait (`Diagnostic` or `Subdiagnostic`). This approach provides greater flexibility and is recommended for diagnostics that go beyond simple, static structures. Diagnostic can be translated into different languages. -## `#[derive(Diagnostic)]` and `#[derive(LintDiagnostic)]` +## `#[derive(Diagnostic)]` -Consider the [definition][defn] of the "field already declared" diagnostic -shown below: +Consider the [definition][defn] of the "field already declared" diagnostic shown below: ```rust,ignore #[derive(Diagnostic)] @@ -32,47 +30,47 @@ pub struct FieldAlreadyDeclared { } ``` -`Diagnostic` can only be derived on structs and enums. +`Diagnostic` can only be derived on structs and enums. Attributes that are placed on the type for structs are placed on each -variants for enums (or vice versa). Each `Diagnostic` has to have one +variants for enums (or vice versa). +Each `Diagnostic` has to have one attribute, `#[diag(...)]`, applied to the struct or each enum variant. If an error has an error code (e.g. "E0624"), then that can be specified using -the `code` sub-attribute. Specifying a `code` isn't mandatory, but if you are +the `code` sub-attribute. +Specifying a `code` isn't mandatory, but if you are porting a diagnostic that uses `Diag` to use `Diagnostic` then you should keep the code if there was one. -`#[diag(..)]` must provide a message as the first positional argument. -The message is written in English, but might be translated to the locale requested by the user. See -[translation documentation](./translation.md) to learn more about how +`#[diag(..)]` must provide a message as the first positional argument. +The message is written in English, but might be translated to the locale requested by the user. +See [translation documentation](./translation.md) to learn more about how translatable error messages are written and how they are generated. Every field of the `Diagnostic` which does not have an annotation is -available in Fluent messages as a variable, like `field_name` in the example -above. Fields can be annotated `#[skip_arg]` if this is undesired. +available in Fluent messages as a variable, like `field_name` in the example above. +Fields can be annotated `#[skip_arg]` if this is undesired. Using the `#[primary_span]` attribute on a field (that has type `Span`) -indicates the primary span of the diagnostic which will have the main message -of the diagnostic. +indicates the primary span of the diagnostic which will have the main message of the diagnostic. Diagnostics are more than just their primary message, they often include -labels, notes, help messages and suggestions, all of which can also be -specified on a `Diagnostic`. +labels, notes, help messages and suggestions, all of which can also be specified on a `Diagnostic`. `#[label]`, `#[help]`, `#[warning]` and `#[note]` can all be applied to fields which have the -type `Span`. Applying any of these attributes will create the corresponding -subdiagnostic with that `Span`. These attributes take a diagnostic message as an argument. +type `Span`. +Applying any of these attributes will create the corresponding subdiagnostic with that `Span`. +These attributes take a diagnostic message as an argument. Other types have special behavior when used in a `Diagnostic` derive: - Any attribute applied to an `Option` will only emit a subdiagnostic if the option is `Some(..)`. -- Any attribute applied to a `Vec` will be repeated for each element of the - vector. +- Any attribute applied to a `Vec` will be repeated for each element of the vector. `#[help]`, `#[warning]` and `#[note]` can also be applied to the struct itself, in which case -they work exactly like when applied to fields except the subdiagnostic won't -have a `Span`. These attributes can also be applied to fields of type `()` for +they work exactly like when applied to fields except the subdiagnostic won't have a `Span`. +These attributes can also be applied to fields of type `()` for the same effect, which when combined with the `Option` type can be used to represent optional `#[note]`/`#[help]`/`#[warning]` subdiagnostics. @@ -84,8 +82,8 @@ Suggestions can be emitted using one of four field attributes: - `#[suggestion_verbose("message", code = "...", applicability = "...")]` Suggestions must be applied on either a `Span` field or a `(Span, -MachineApplicability)` field. Similarly to other field attributes, a message -needs to be provided which will be shown to the user. +MachineApplicability)` field. +Similarly to other field attributes, a message needs to be provided which will be shown to the user. `code` specifies the code that should be suggested as a replacement and is a format string (e.g. `{field_name}` would be replaced by the value of the `field_name` field of the struct). @@ -113,8 +111,8 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a> for FieldAlreadyDeclared { } ``` -Now that we've defined our diagnostic, how do we [use it][use]? It's quite -straightforward, just create an instance of the struct and pass it to +Now that we've defined our diagnostic, how do we [use it][use]? +It's quite straightforward, just create an instance of the struct and pass it to `emit_err` (or `emit_warning`): ```rust,ignore @@ -125,9 +123,8 @@ tcx.dcx().emit_err(FieldAlreadyDeclared { }); ``` -### Reference for `#[derive(Diagnostic)]` and `#[derive(LintDiagnostic)]` -`#[derive(Diagnostic)]` and `#[derive(LintDiagnostic)]` support the -following attributes: +### Reference for `#[derive(Diagnostic)]` +`#[derive(Diagnostic)]` supports the following attributes: - `#[diag("message", code = "...")]` - _Applied to struct or enum variant._ @@ -164,17 +161,16 @@ following attributes: - Value is the suggestion message that will be shown to the user. - See [translation documentation](./translation.md). - `code = "..."`/`code("...", ...)` (_Mandatory_) - - One or multiple format strings indicating the code to be suggested as a - replacement. Multiple values signify multiple possible replacements. + - One or multiple format strings indicating the code to be suggested as a replacement. + Multiple values signify multiple possible replacements. - `applicability = "..."` (_Optional_) - String which must be one of `machine-applicable`, `maybe-incorrect`, `has-placeholders` or `unspecified`. - `#[subdiagnostic]` - - _Applied to a type that implements `Subdiagnostic` (from - `#[derive(Subdiagnostic)]`)._ + - _Applied to a type that implements `Subdiagnostic` (from `#[derive(Subdiagnostic)]`)._ - Adds the subdiagnostic represented by the subdiagnostic struct. - `#[primary_span]` (_Optional_) - - _Applied to `Span` fields on `Subdiagnostic`s. Not used for `LintDiagnostic`s._ + - _Applied to `Span` fields on `Subdiagnostic`s. - Indicates the primary span of the diagnostic. - `#[skip_arg]` (_Optional_) - _Applied to any field._ @@ -182,14 +178,14 @@ following attributes: ## `#[derive(Subdiagnostic)]` It is common in the compiler to write a function that conditionally adds a -specific subdiagnostic to an error if it is applicable. Oftentimes these -subdiagnostics could be represented using a diagnostic struct even if the -overall diagnostic could not. In this circumstance, the `Subdiagnostic` +specific subdiagnostic to an error if it is applicable. +Oftentimes these subdiagnostics could be represented using a diagnostic struct even if the +overall diagnostic could not. +In this circumstance, the `Subdiagnostic` derive can be used to represent a partial diagnostic (e.g a note, label, help or suggestion) as a struct. -Consider the [definition][subdiag_defn] of the "expected return type" label -shown below: +Consider the [definition][subdiag_defn] of the "expected return type" label shown below: ```rust #[derive(Subdiagnostic)] @@ -208,10 +204,10 @@ pub enum ExpectedReturnTypeLabel<'tcx> { } ``` -Like `Diagnostic`, `Subdiagnostic` can be derived for structs or -enums. Attributes that are placed on the type for structs are placed on each -variants for enums (or vice versa). Each `Subdiagnostic` should have one -attribute applied to the struct or each variant, one of: +Like `Diagnostic`, `Subdiagnostic` can be derived for structs or enums. +Attributes that are placed on the type for structs are placed on each +variants for enums (or vice versa). +Each `Subdiagnostic` should have one attribute applied to the struct or each variant, one of: - `#[label(..)]` for defining a label - `#[note(..)]` for defining a note @@ -224,15 +220,14 @@ See [translation documentation](./translation.md) to learn more about how translatable error messages are generated. Using the `#[primary_span]` attribute on a field (with type `Span`) will denote -the primary span of the subdiagnostic. A primary span is only necessary for a -label or suggestion, which can not be spanless. +the primary span of the subdiagnostic. +A primary span is only necessary for a label or suggestion, which can not be spanless. Every field of the type/variant which does not have an annotation is available -in Fluent messages as a variable. Fields can be annotated `#[skip_arg]` if this -is undesired. +in Fluent messages as a variable. +Fields can be annotated `#[skip_arg]` if this is undesired. -Like `Diagnostic`, `Subdiagnostic` supports `Option` and -`Vec` fields. +Like `Diagnostic`, `Subdiagnostic` supports `Option` and `Vec` fields. Suggestions can be emitted using one of four attributes on the type/variant: @@ -241,8 +236,7 @@ Suggestions can be emitted using one of four attributes on the type/variant: - `#[suggestion_short("...", code = "...", applicability = "...")]` - `#[suggestion_verbose("...", code = "...", applicability = "...")]` -Suggestions require `#[primary_span]` be set on a field and can have the -following sub-attributes: +Suggestions require `#[primary_span]` be set on a field and can have the following sub-attributes: - The first positional argument specifies the message which will be shown to the user. - `code` specifies the code that should be suggested as a replacement and is a @@ -276,8 +270,7 @@ impl<'tcx> Subdiagnostic for ExpectedReturnTypeLabel<'tcx> { Once defined, a subdiagnostic can be used by passing it to the `subdiagnostic` function ([example][subdiag_use_1] and [example][subdiag_use_2]) on a -diagnostic or by assigning it to a `#[subdiagnostic]`-annotated field of a -diagnostic struct. +diagnostic or by assigning it to a `#[subdiagnostic]`-annotated field of a diagnostic struct. ### Argument sharing and isolation @@ -310,22 +303,24 @@ Additionally, subdiagnostics can access arguments from the main diagnostic with `#[derive(Subdiagnostic)]` supports the following attributes: - `#[label("message")]`, `#[help("message")]`, `#[warning("message")]` or `#[note("message")]` - - _Applied to struct or enum variant. Mutually exclusive with struct/enum variant attributes._ + - _Applied to struct or enum variant. + Mutually exclusive with struct/enum variant attributes._ - _Mandatory_ - Defines the type to be representing a label, help or note. - Message (_Mandatory_) - The diagnostic message that will be shown to the user. - See [translation documentation](./translation.md). - `#[suggestion{,_hidden,_short,_verbose}("message", code = "...", applicability = "...")]` - - _Applied to struct or enum variant. Mutually exclusive with struct/enum variant attributes._ + - _Applied to struct or enum variant. + Mutually exclusive with struct/enum variant attributes._ - _Mandatory_ - Defines the type to be representing a suggestion. - Message (_Mandatory_) - The diagnostic message that will be shown to the user. - See [translation documentation](./translation.md). - `code = "..."`/`code("...", ...)` (_Mandatory_) - - One or multiple format strings indicating the code to be suggested as a - replacement. Multiple values signify multiple possible replacements. + - One or multiple format strings indicating the code to be suggested as a replacement. + Multiple values signify multiple possible replacements. - `applicability = "..."` (_Optional_) - _Mutually exclusive with `#[applicability]` on a field._ - Value is the applicability of the suggestion. @@ -335,7 +330,8 @@ Additionally, subdiagnostics can access arguments from the main diagnostic with - `has-placeholders` - `unspecified` - `#[multipart_suggestion{,_hidden,_short,_verbose}("message", applicability = "...")]` - - _Applied to struct or enum variant. Mutually exclusive with struct/enum variant attributes._ + - _Applied to struct or enum variant. + Mutually exclusive with struct/enum variant attributes._ - _Mandatory_ - Defines the type to be representing a multipart suggestion. - Message (_Mandatory_): see `#[suggestion]` @@ -348,8 +344,7 @@ to multipart suggestions) - _Applied to `Span` fields._ - Indicates the span to be one part of the multipart suggestion. - `code = "..."` (_Mandatory_) - - Value is a format string indicating the code to be suggested as a - replacement. + - Value is a format string indicating the code to be suggested as a replacement. - `#[applicability]` (_Optional_; only applicable to (simple and multipart) suggestions) - _Applied to `Applicability` fields._ - Indicates the applicability of the suggestion. diff --git a/src/doc/rustc-dev-guide/src/diagnostics/translation.md b/src/doc/rustc-dev-guide/src/diagnostics/translation.md index 018f33119375..cf95727e2a67 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/translation.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/translation.md @@ -5,7 +5,8 @@ rustc's current diagnostics translation infrastructure (as of October 2024 ) unfortunately causes some friction for compiler contributors, and the current infrastructure is mostly pending a redesign that better addresses needs of both -compiler contributors and translation teams. Note that there is no current +compiler contributors and translation teams. +Note that there is no current active redesign proposals (as of October 2024 )! @@ -14,13 +15,13 @@ Please see the tracking issue for status updates. The translation infra is waiting for a yet-to-be-proposed redesign and thus rework, we are not -mandating usage of current translation infra. Use the infra if you *want to* or +mandating usage of current translation infra. +Use the infra if you *want to* or otherwise makes the code cleaner, but otherwise sidestep the translation infra if you need more flexibility. -rustc's diagnostic infrastructure supports translatable diagnostics using -[Fluent]. +rustc's diagnostic infrastructure supports translatable diagnostics using [Fluent]. ## Writing translatable diagnostics @@ -28,11 +29,10 @@ There are two ways of writing translatable diagnostics: 1. For simple diagnostics, using a diagnostic (or subdiagnostic) derive. ("Simple" diagnostics being those that don't require a lot of logic in - deciding to emit subdiagnostics and can therefore be represented as - diagnostic structs). See [the diagnostic and subdiagnostic structs - documentation](./diagnostic-structs.md). + deciding to emit subdiagnostics and can therefore be represented as diagnostic structs). + See [the diagnostic and subdiagnostic structs documentation](./diagnostic-structs.md). 2. Using typed identifiers with `Diag` APIs (in - `Diagnostic` or `Subdiagnostic` or `LintDiagnostic` implementations). + `Diagnostic` or `Subdiagnostic` implementations). When adding or changing a translatable diagnostic, you don't need to worry about the translations. @@ -42,14 +42,15 @@ Only updating the original English message is required. Fluent is built around the idea of "asymmetric localization", which aims to decouple the expressiveness of translations from the grammar of the source -language (English in rustc's case). Prior to translation, rustc's diagnostics +language (English in rustc's case). +Prior to translation, rustc's diagnostics relied heavily on interpolation to build the messages shown to the users. Interpolated strings are hard to translate because writing a natural-sounding translation might require more, less, or just different interpolation than the -English string, all of which would require changes to the compiler's source -code to support. +English string, all of which would require changes to the compiler's source code to support. -Diagnostic messages are defined in Fluent resources. A combined set of Fluent +Diagnostic messages are defined in Fluent resources. +A combined set of Fluent resources for a given locale (e.g. `en-US`) is known as Fluent bundle. ```fluent @@ -57,9 +58,9 @@ typeck_address_of_temporary_taken = cannot take address of a temporary ``` In the above example, `typeck_address_of_temporary_taken` is the identifier for -a Fluent message and corresponds to the diagnostic message in English. Other -Fluent resources can be written which would correspond to a message in another -language. Each diagnostic therefore has at least one Fluent message. +a Fluent message and corresponds to the diagnostic message in English. +Other Fluent resources can be written which would correspond to a message in another language. +Each diagnostic therefore has at least one Fluent message. ```fluent typeck_address_of_temporary_taken = cannot take address of a temporary @@ -68,13 +69,14 @@ typeck_address_of_temporary_taken = cannot take address of a temporary By convention, diagnostic messages for subdiagnostics are specified as "attributes" on Fluent messages (additional related messages, denoted by the -`.` syntax). In the above example, `label` is an attribute of +`.` syntax). +In the above example, `label` is an attribute of `typeck_address_of_temporary_taken` which corresponds to the message for the label added to this diagnostic. Diagnostic messages often interpolate additional context into the message shown -to the user, such as the name of a type or of a variable. Additional context to -Fluent messages is provided as an "argument" to the diagnostic. +to the user, such as the name of a type or of a variable. +Additional context to Fluent messages is provided as an "argument" to the diagnostic. ```fluent typeck_struct_expr_non_exhaustive = @@ -82,19 +84,19 @@ typeck_struct_expr_non_exhaustive = ``` In the above example, the Fluent message refers to an argument named `what` -which is expected to exist (how arguments are provided to diagnostics is -discussed in detail later). +which is expected to exist (how arguments are provided to diagnostics is discussed in detail later). -You can consult the [Fluent] documentation for other usage examples of Fluent -and its syntax. +You can consult the [Fluent] documentation for other usage examples of Fluent and its syntax. ### Guideline for message naming -Usually, fluent uses `-` for separating words inside a message name. However, -`_` is accepted by fluent as well. As `_` fits Rust's use cases better, due to +Usually, fluent uses `-` for separating words inside a message name. +However, +`_` is accepted by fluent as well. +As `_` fits Rust's use cases better, due to the identifiers on the Rust side using `_` as well, inside rustc, `-` is not -allowed for separating words, and instead `_` is recommended. The only exception -is for leading `-`s, for message names like `-passes_see_issue`. +allowed for separating words, and instead `_` is recommended. +The only exception is for leading `-`s, for message names like `-passes_see_issue`. ### Guidelines for writing translatable messages @@ -104,22 +106,22 @@ argument (not just the information required in the English message). As the compiler team gain more experience writing diagnostics that have all of the information necessary to be translated into different languages, this page -will be updated with more guidance. For now, the [Fluent] documentation has +will be updated with more guidance. +For now, the [Fluent] documentation has excellent examples of translating messages into different locales and the information that needs to be provided by the code to do so. ### Compile-time validation and typed identifiers -rustc's `#[derive(Diagnostic)]` macro performs compile-time validation of Fluent -messages. Compile-time validation of Fluent resources will emit any parsing errors +rustc's `#[derive(Diagnostic)]` macro performs compile-time validation of Fluent messages. +Compile-time validation of Fluent resources will emit any parsing errors from Fluent resources while building the compiler, preventing invalid Fluent -resources from causing panics in the compiler. Compile-time validation also -emits an error if multiple Fluent messages have the same identifier. +resources from causing panics in the compiler. +Compile-time validation also emits an error if multiple Fluent messages have the same identifier. ## Internals -Various parts of rustc's diagnostic internals are modified in order to support -translation. +Various parts of rustc's diagnostic internals are modified in order to support translation. ### Messages @@ -127,10 +129,10 @@ All of rustc's traditional diagnostic APIs (e.g. `struct_span_err` or `note`) take any message that can be converted into a `DiagMessage`. [`rustc_error_messages::DiagMessage`] can represent legacy non-translatable -diagnostic messages and translatable messages. Non-translatable messages are -just `String`s. Translatable messages are just a `&'static str` with the -identifier of the Fluent message (sometimes with an additional `&'static str` -with an attribute). +diagnostic messages and translatable messages. +Non-translatable messages are just `String`s. +Translatable messages are just a `&'static str` with the +identifier of the Fluent message (sometimes with an additional `&'static str` with an attribute). `DiagMessage` never needs to be interacted with directly: `DiagMessage` constants are created for each diagnostic message in a @@ -139,8 +141,7 @@ either be created in the macro-generated code of a diagnostic derive. `DiagMessage` implements `Into` for any type that can be converted into a string, and converts these into -non-translatable diagnostics - this keeps all existing diagnostic calls -working. +non-translatable diagnostics - this keeps all existing diagnostic calls working. ### Arguments @@ -151,56 +152,14 @@ Diagnostics have a `set_arg` function that can be used to provide this additional context to a diagnostic. Arguments have both a name (e.g. "what" in the earlier example) and a value. -Argument values are represented using the `DiagArgValue` type, which is -just a string or a number. rustc types can implement `IntoDiagArg` with +Argument values are represented using the `DiagArgValue` type, which is just a string or a number. +rustc types can implement `IntoDiagArg` with conversion into a string or a number, and common types like `Ty<'tcx>` already have such implementations. `set_arg` calls are handled transparently by diagnostic derives but need to be added manually when using diagnostic builder APIs. -### Loading - -rustc makes a distinction between the "fallback bundle" for `en-US` that is used -by default and when another locale is missing a message; and the primary fluent -bundle which is requested by the user. - -Diagnostic emitters implement the `Emitter` trait which has two functions for -accessing the fallback and primary fluent bundles (`fallback_fluent_bundle` and -`fluent_bundle` respectively). - -`Emitter` also has member functions with default implementations for performing -translation of a `DiagMessage` using the results of -`fallback_fluent_bundle` and `fluent_bundle`. - -All of the emitters in rustc load the fallback Fluent bundle lazily, only -reading Fluent resources and parsing them when an error message is first being -translated (for performance reasons - it doesn't make sense to do this if no -error is being emitted). `rustc_error_messages::fallback_fluent_bundle` returns -a `std::lazy::Lazy` which is provided to emitters and evaluated -in the first call to `Emitter::fallback_fluent_bundle`. - -The primary Fluent bundle (for the user's desired locale) is expected to be -returned by `Emitter::fluent_bundle`. This bundle is used preferentially when -translating messages, the fallback bundle is only used if the primary bundle is -missing a message or not provided. - -There are no locale bundles distributed with the compiler, -but mechanisms are implemented for loading them. - -- `-Ztranslate-additional-ftl` can be used to load a specific resource as the - primary bundle for testing purposes. -- `-Ztranslate-lang` can be provided a language identifier (something like - `en-US`) and will load any Fluent resources found in - `$sysroot/share/locale/$locale/` directory (both the user provided - sysroot and any sysroot candidates). - -Primary bundles are not currently loaded lazily and if requested will be loaded -at the start of compilation regardless of whether an error occurs. Lazily -loading primary bundles is possible if it can be assumed that loading a bundle -won't fail. Bundle loading can fail if a requested locale is missing, Fluent -files are malformed, or a message is duplicated in multiple resources. - [Fluent]: https://projectfluent.org [`compiler/rustc_borrowck/messages.ftl`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_borrowck/messages.ftl [`compiler/rustc_parse/messages.ftl`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_parse/messages.ftl diff --git a/src/doc/rustc-dev-guide/src/feature-gate-check.md b/src/doc/rustc-dev-guide/src/feature-gate-check.md index c5a499f5708c..59e50837c52e 100644 --- a/src/doc/rustc-dev-guide/src/feature-gate-check.md +++ b/src/doc/rustc-dev-guide/src/feature-gate-check.md @@ -4,9 +4,9 @@ For the how-to steps to add, remove, rename, or stabilize feature gates, see [Feature gates][feature-gates]. Feature gates prevent usage of unstable language and library features without a -nightly-only `#![feature(...)]` opt-in. This chapter documents the implementation -of feature gating: where gates are defined, how they are enabled, and how usage -is verified. +nightly-only `#![feature(...)]` opt-in. +This chapter documents the implementation +of feature gating: where gates are defined, how they are enabled, and how usage is verified. @@ -15,15 +15,14 @@ is verified. All feature gate definitions are located in the `rustc_feature` crate: - **Unstable features** are declared in [`rustc_feature/src/unstable.rs`] via - the `declare_features!` macro. This associates features with issue numbers and - tracking metadata. + the `declare_features!` macro. + This associates features with issue numbers and tracking metadata. - **Accepted features** (stabilized) are listed in [`rustc_feature/src/accepted.rs`]. - **Removed features** (explicitly disallowed) are listed in [`rustc_feature/src/removed.rs`]. - **Gated built-in attributes and cfgs** are declared in [`rustc_feature/src/builtin_attrs.rs`]. -The [`rustc_feature::Features`] type represents the **active feature set** for a -crate. Helpers like `enabled`, `incomplete`, and `internal` are used during -compilation to check status. +The [`rustc_feature::Features`] type represents the **active feature set** for a crate. +Helpers like `enabled`, `incomplete`, and `internal` are used during compilation to check status. ## Collecting Features @@ -31,11 +30,10 @@ Before AST validation or expansion, `rustc` collects crate-level `#![feature(...)]` attributes to build the active `Features` set. - The collection happens in [`rustc_expand/src/config.rs`] in [`features`]. -- Each `#![feature]` entry is classified against the `unstable`, `accepted`, and - `removed` tables: +- Each `#![feature]` entry is classified against the `unstable`, `accepted`, and `removed` tables: - **Removed** features cause an immediate error. - - **Accepted** features are recorded but do not require nightly. On - stable/beta, `maybe_stage_features` in + - **Accepted** features are recorded but do not require nightly. + On stable/beta, `maybe_stage_features` in [`rustc_ast_passes/src/feature_gate.rs`] emits the non-nightly diagnostic and lists stable features, which is where the "already stabilized" messaging comes from. @@ -43,13 +41,14 @@ Before AST validation or expansion, `rustc` collects crate-level - Unknown features are treated as **library features** and validated later. - With `-Z allow-features=...`, any **unstable** or **unknown** feature not in the allowlist is rejected. -- [`RUSTC_BOOTSTRAP`] feeds into `UnstableFeatures::from_environment`. This - variable controls whether the compiler is treated as "nightly", allowing +- [`RUSTC_BOOTSTRAP`] feeds into `UnstableFeatures::from_environment`. + This variable controls whether the compiler is treated as "nightly", allowing feature gates to be bypassed during bootstrapping or explicitly disabled (`-1`). ## Parser Gating -Some syntax is detected and gated during parsing. The parser records spans for +Some syntax is detected and gated during parsing. +The parser records spans for later checking to keep diagnostics consistent and deferred until after parsing. - [`rustc_session/src/parse.rs`] defines [`GatedSpans`] and the `gate` method. @@ -77,8 +76,7 @@ in `check_crate` and its AST visitor. `check_crate` iterates over `sess.psess.gated_spans`: -- The `gate_all!` macro emits diagnostics for each gated span if the feature is - not enabled. +- The `gate_all!` macro emits diagnostics for each gated span if the feature is not enabled. - Some gates have extra logic (e.g., `yield` can be allowed by `coroutines` or `gen_blocks`). - Legacy gates (e.g., `box_patterns`, `try_blocks`) may use a separate path that @@ -92,8 +90,7 @@ easier to validate after expansion. - The visitor uses helper macros (`gate!`, `gate_alt!`, `gate_multi!`) to check: 1. Is the feature enabled? 2. Does `span.allows_unstable` permit it (for internal compiler macros)? -- Examples include `trait_alias`, `decl_macro`, `extern types`, and various - `impl Trait` forms. +- Examples include `trait_alias`, `decl_macro`, `extern types`, and various `impl Trait` forms. ## Attributes and `cfg` @@ -101,8 +98,7 @@ Beyond syntax, rustc also gates attributes and `cfg` options. ### Built-in attributes -- [`rustc_ast_passes::check_attribute`] inspects attributes against - `BUILTIN_ATTRIBUTE_MAP`. +- [`rustc_ast_passes::check_attribute`] inspects attributes against `BUILTIN_ATTRIBUTE_MAP`. - If the attribute is `AttributeGate::Gated` and the feature isn’t enabled, `feature_err` is emitted. @@ -121,9 +117,9 @@ Diagnostic helpers are located in [`rustc_session/src/parse.rs`]. - `feature_err` and `feature_warn` emit standardized diagnostics, attaching the tracking issue number where possible. - `Span::allows_unstable` in [`rustc_span/src/lib.rs`] checks if a span originates - from a macro marked with `#[allow_internal_unstable]`. This allows internal - macros to use unstable features on stable channels while enforcing gates for - user code. + from a macro marked with `#[allow_internal_unstable]`. + This allows internal + macros to use unstable features on stable channels while enforcing gates for user code. [`rustc_feature/src/unstable.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_feature/src/unstable.rs [`rustc_feature/src/removed.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_feature/src/removed.rs diff --git a/src/doc/rustc-dev-guide/src/feature-gates.md b/src/doc/rustc-dev-guide/src/feature-gates.md index 76bf111fe772..12166577ae30 100644 --- a/src/doc/rustc-dev-guide/src/feature-gates.md +++ b/src/doc/rustc-dev-guide/src/feature-gates.md @@ -1,7 +1,6 @@ # Feature gates -This chapter is intended to provide basic help for adding, removing, and -modifying feature gates. +This chapter is intended to provide basic help for adding, removing, and modifying feature gates. For how rustc enforces and checks feature gates in the compiler pipeline, see [Feature Gate Checking][feature-gate-check]. @@ -67,9 +66,8 @@ to follow when [removing a feature gate][removing]): Some("renamed to `$new_feature_name`")) ``` -3. Add a feature gate declaration with the new name to - `rustc_feature/src/unstable.rs`. It should look very similar to the old - declaration: +3. Add a feature gate declaration with the new name to `rustc_feature/src/unstable.rs`. + It should look very similar to the old declaration: ```rust,ignore /// description of feature @@ -79,9 +77,8 @@ to follow when [removing a feature gate][removing]): ## Stabilizing a feature -See ["Updating the feature-gate listing"] in the "Stabilizing Features" chapter -for instructions. There are additional steps you will need to take beyond just -updating the declaration! +See ["Updating the feature-gate listing"] in the "Stabilizing Features" chapter for instructions. +There are additional steps you will need to take beyond just updating the declaration! ["Stability in code"]: ./implementing-new-features.md#stability-in-code diff --git a/src/doc/rustc-dev-guide/src/git.md b/src/doc/rustc-dev-guide/src/git.md index e85e6bd70850..bf31e79a9a15 100644 --- a/src/doc/rustc-dev-guide/src/git.md +++ b/src/doc/rustc-dev-guide/src/git.md @@ -308,8 +308,8 @@ reapplied to the most recent version of `main`. In other words, Git tries to pretend that the changes you made to the old version of `main` were instead made to the new version of `main`. -During this process, you should expect to -encounter at least one "rebase conflict". This happens when Git's attempt to +During this process, you should expect to encounter at least one "rebase conflict". +This happens when Git's attempt to reapply the changes fails because your changes conflicted with other changes that have been made. You can tell that this happened because you'll see lines in the output that look like @@ -410,6 +410,13 @@ because they only represent "fixups" and not real changes. For example, `git rebase --interactive HEAD~2` will allow you to edit the two commits only. +For pull requests in `rust-lang/rust`, you can ask [bors] to squash by commenting +`@bors squash` on the PR. +By default, [bors] combines all commit messages in the PR. +To customize the commit message, use `@bors squash [msg|message=]`. + +[bors]: https://github.com/rust-lang/bors + ### `git range-diff` After completing a rebase, and before pushing up your changes, you may want to @@ -472,8 +479,8 @@ command useful, especially their ["Examples" section][range-diff-example-docs]. ## No-Merge Policy -The rust-lang/rust repo uses what is known as a "rebase workflow". This means -that merge commits in PRs are not accepted. +The rust-lang/rust repo uses what is known as a "rebase workflow". +This means that merge commits in PRs are not accepted. As a result, if you are running `git merge` locally, chances are good that you should be rebasing instead. Of course, this is not always true; if your merge will just be a fast-forward, diff --git a/src/doc/rustc-dev-guide/src/macro-expansion.md b/src/doc/rustc-dev-guide/src/macro-expansion.md index 3199e9950d7e..60067c4f85c3 100644 --- a/src/doc/rustc-dev-guide/src/macro-expansion.md +++ b/src/doc/rustc-dev-guide/src/macro-expansion.md @@ -1,6 +1,7 @@ # Macro expansion -Rust has a very powerful macro system. In the previous chapter, we saw how +Rust has a very powerful macro system. +In the previous chapter, we saw how the parser sets aside macros to be expanded (using temporary [placeholders]). This chapter is about the process of expanding those macros iteratively until we have a complete [*Abstract Syntax Tree* (AST)][ast] for our crate with no @@ -9,9 +10,9 @@ unexpanded macros (or a compile error). [ast]: ./ast-validation.md [placeholders]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/placeholders/index.html -First, we discuss the algorithm that expands and integrates macro output into -ASTs. Next, we take a look at how hygiene data is collected. Finally, we look -at the specifics of expanding different types of macros. +First, we discuss the algorithm that expands and integrates macro output into ASTs. +Next, we take a look at how hygiene data is collected. +Finally, we look at the specifics of expanding different types of macros. Many of the algorithms and data structures described below are in [`rustc_expand`], with fundamental data structures in [`rustc_expand::base`][base]. @@ -25,21 +26,25 @@ handled in [`rustc_expand::config`][cfg]. ## Expansion and AST Integration -Firstly, expansion happens at the crate level. Given a raw source code for +Firstly, expansion happens at the crate level. +Given a raw source code for a crate, the compiler will produce a massive AST with all macros expanded, all modules inlined, etc. The primary entry point for this process is the -[`MacroExpander::fully_expand_fragment`][fef] method. With few exceptions, we +[`MacroExpander::fully_expand_fragment`][fef] method. +With few exceptions, we use this method on the whole crate (see ["Eager Expansion"](#eager-expansion) below for more detailed discussion of edge case expansion issues). [`rustc_builtin_macros`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_builtin_macros/index.html [reb]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/build/index.html -At a high level, [`fully_expand_fragment`][fef] works in iterations. We keep a +At a high level, [`fully_expand_fragment`][fef] works in iterations. +We keep a queue of unresolved macro invocations (i.e. macros we haven't found the -definition of yet). We repeatedly try to pick a macro from the queue, resolve -it, expand it, and integrate it back. If we can't make progress in an -iteration, this represents a compile error. Here is the [algorithm][original]: +definition of yet). +We repeatedly try to pick a macro from the queue, resolve it, expand it, and integrate it back. +If we can't make progress in an iteration, this represents a compile error. + Here is the [algorithm][original]: [fef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/expand/struct.MacroExpander.html#method.fully_expand_fragment [original]: https://github.com/rust-lang/rust/pull/53778#issuecomment-419224049 @@ -49,13 +54,13 @@ iteration, this represents a compile error. Here is the [algorithm][original]: 1. [Resolve](./name-resolution.md) imports in our partially built crate as much as possible. 2. Collect as many macro [`Invocation`s][inv] as possible from our - partially built crate (`fn`-like, attributes, derives) and add them to the - queue. + partially built crate (`fn`-like, attributes, derives) and add them to the queue. 3. Dequeue the first element and attempt to resolve it. 4. If it's resolved: 1. Run the macro's expander function that consumes a [`TokenStream`] or AST and produces a [`TokenStream`] or [`AstFragment`] (depending on - the macro kind). (A [`TokenStream`] is a collection of [`TokenTree`s][tt], + the macro kind). + (A [`TokenStream`] is a collection of [`TokenTree`s][tt], each of which are a token (punctuation, identifier, or literal) or a delimited group (anything inside `()`/`[]`/`{}`)). - At this point, we know everything about the macro itself and can @@ -63,17 +68,18 @@ iteration, this represents a compile error. Here is the [algorithm][original]: data; that is the [hygiene] data associated with [`ExpnId`] (see [Hygiene][hybelow] below). 2. Integrate that piece of AST into the currently-existing though - partially-built AST. This is essentially where the "token-like mass" - becomes a proper set-in-stone AST with side-tables. It happens as - follows: + partially-built AST. + This is essentially where the "token-like mass" + becomes a proper set-in-stone AST with side-tables. + It happens as follows: - If the macro produces tokens (e.g. a proc macro), we parse into an AST, which may produce parse errors. - During expansion, we create [`SyntaxContext`]s (hierarchy 2) (see [Hygiene][hybelow] below). - These three passes happen one after another on every AST fragment freshly expanded from a macro: - - [`NodeId`]s are assigned by [`InvocationCollector`]. This - also collects new macro calls from this new AST piece and + - [`NodeId`]s are assigned by [`InvocationCollector`]. + This also collects new macro calls from this new AST piece and adds them to the queue. - ["Def paths"][defpath] are created and [`DefId`]s are assigned to them by [`DefCollector`]. @@ -115,22 +121,23 @@ so that `rustc` can report more errors than just the original failure. ### Name Resolution Notice that name resolution is involved here: we need to resolve imports and -macro names in the above algorithm. This is done in -[`rustc_resolve::macros`][mresolve], which resolves macro paths, validates +macro names in the above algorithm. +This is done in [`rustc_resolve::macros`][mresolve], which resolves macro paths, validates those resolutions, and reports various errors (e.g. "not found", "found, but -it's unstable", "expected x, found y"). However, we don't try to resolve -other names yet. This happens later, as we will see in the chapter: [Name -Resolution](./name-resolution.md). +it's unstable", "expected x, found y"). +However, we don't try to resolve other names yet. +This happens later, as we will see in the chapter: [Name Resolution](./name-resolution.md). [mresolve]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/macros/index.html ### Eager Expansion _Eager expansion_ means we expand the arguments of a macro invocation before -the macro invocation itself. This is implemented only for a few special +the macro invocation itself. +This is implemented only for a few special built-in macros that expect literals; expanding arguments first for some of -these macro results in a smoother user experience. As an example, consider -the following: +these macro results in a smoother user experience. +As an example, consider the following: ```rust,ignore macro bar($i: ident) { $i } @@ -139,29 +146,27 @@ macro foo($i: ident) { $i } foo!(bar!(baz)); ``` -A lazy-expansion would expand `foo!` first. An eager-expansion would expand -`bar!` first. +A lazy-expansion would expand `foo!` first. +An eager-expansion would expand `bar!` first. -Eager-expansion is not a generally available feature of Rust. Implementing -eager-expansion more generally would be challenging, so we implement it for a -few special built-in macros for the sake of user-experience. The built-in -macros are implemented in [`rustc_builtin_macros`], along with some other +Eager-expansion is not a generally available feature of Rust. +Implementing eager-expansion more generally would be challenging, so we implement it for a +few special built-in macros for the sake of user-experience. +The built-in macros are implemented in [`rustc_builtin_macros`], along with some other early code generation facilities like injection of standard library imports or -generation of test harness. There are some additional helpers for building -AST fragments in [`rustc_expand::build`][reb]. Eager-expansion generally -performs a subset of the things that lazy (normal) expansion does. It is done -by invoking [`fully_expand_fragment`][fef] on only part of a crate (as opposed +generation of test harness. +There are some additional helpers for building AST fragments in [`rustc_expand::build`][reb]. +Eager-expansion generally performs a subset of the things that lazy (normal) expansion does. +It is done by invoking [`fully_expand_fragment`][fef] on only part of a crate (as opposed to the whole crate, like we normally do). ### Other Data Structures -Here are some other notable data structures involved in expansion and -integration: -- [`ResolverExpand`] - a `trait` used to break crate dependencies. This allows the - resolver services to be used in [`rustc_ast`], despite [`rustc_resolve`] and +Here are some other notable data structures involved in expansion and integration: +- [`ResolverExpand`] - a `trait` used to break crate dependencies. + This allows the resolver services to be used in [`rustc_ast`], despite [`rustc_resolve`] and pretty much everything else depending on [`rustc_ast`]. -- [`ExtCtxt`]/[`ExpansionData`] - holds various intermediate expansion - infrastructure data. +- [`ExtCtxt`]/[`ExpansionData`] - holds various intermediate expansion infrastructure data. - [`Annotatable`] - a piece of AST that can be an attribute target, almost the same thing as [`AstFragment`] except for types and patterns that can be produced by macros but cannot be annotated with attributes. @@ -182,7 +187,8 @@ integration: ## Hygiene and Hierarchies If you have ever used the C/C++ preprocessor macros, you know that there are some -annoying and hard-to-debug gotchas! For example, consider the following C code: +annoying and hard-to-debug gotchas! +For example, consider the following C code: ```c #define DEFINE_FOO struct Bar {int x;}; struct Foo {Bar bar;}; @@ -195,9 +201,9 @@ struct Bar { DEFINE_FOO ``` -Most people avoid writing C like this – and for good reason: it doesn't -compile. The `struct Bar` defined by the macro clashes names with the `struct -Bar` defined in the code. Consider also the following example: +Most people avoid writing C like this – and for good reason: it doesn't compile. +The `struct Bar` defined by the macro clashes names with the `struct Bar` defined in the code. +Consider also the following example: ```c #define DO_FOO(x) {\ @@ -210,20 +216,23 @@ int y = 22; DO_FOO(y); ``` -Do you see the problem? We wanted to generate a call `foo(22, 0)`, but instead +Do you see the problem? +We wanted to generate a call `foo(22, 0)`, but instead we got `foo(0, 0)` because the macro defined its own `y`! -These are both examples of _macro hygiene_ issues. _Hygiene_ relates to how to -handle names defined _within a macro_. In particular, a hygienic macro system -prevents errors due to names introduced within a macro. Rust macros are hygienic -in that they do not allow one to write the sorts of bugs above. +These are both examples of _macro hygiene_ issues. +_Hygiene_ relates to how to handle names defined _within a macro_. +In particular, a hygienic macro system prevents errors due to names introduced within a macro. +Rust macros are hygienic in that they do not allow one to write the sorts of bugs above. At a high level, hygiene within the Rust compiler is accomplished by keeping -track of the context where a name is introduced and used. We can then -disambiguate names based on that context. Future iterations of the macro system -will allow greater control to the macro author to use that context. For example, -a macro author may want to introduce a new name to the context where the macro -was called. Alternately, the macro author may be defining a variable for use +track of the context where a name is introduced and used. +We can then disambiguate names based on that context. +Future iterations of the macro system +will allow greater control to the macro author to use that context. +For example, +a macro author may want to introduce a new name to the context where the macro was called. +Alternately, the macro author may be defining a variable for use only within the macro (i.e. it should not be visible outside the macro). [code_dir]: https://github.com/rust-lang/rust/tree/HEAD/compiler/rustc_expand/src/mbe @@ -232,8 +241,9 @@ only within the macro (i.e. it should not be visible outside the macro). [code_parse_int]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/mbe/macro_parser/struct.TtParser.html#method.parse_tt [parsing]: ./the-parser.html -The context is attached to AST nodes. All AST nodes generated by macros have -context attached. Additionally, there may be other nodes that have context +The context is attached to AST nodes. +All AST nodes generated by macros have context attached. +Additionally, there may be other nodes that have context attached, such as some desugared syntax (non-macro-expanded nodes are considered to just have the "root" context, as described below). Throughout the compiler, we use [`rustc_span::Span`s][span] to refer to code locations. @@ -242,27 +252,29 @@ This struct also has hygiene information attached to it, as we will see later. [span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html Because macros invocations and definitions can be nested, the syntax context of -a node must be a hierarchy. For example, if we expand a macro and there is +a node must be a hierarchy. +For example, if we expand a macro and there is another macro invocation or definition in the generated output, then the syntax context should reflect the nesting. However, it turns out that there are actually a few types of context we may -want to track for different purposes. Thus, there are not just one but _three_ -expansion hierarchies that together comprise the hygiene information for a -crate. +want to track for different purposes. +Thus, there are not just one but _three_ +expansion hierarchies that together comprise the hygiene information for a crate. All of these hierarchies need some sort of "macro ID" to identify individual -elements in the chain of expansions. This ID is [`ExpnId`]. All macros receive -an integer ID, assigned continuously starting from 0 as we discover new macro -calls. All hierarchies start at [`ExpnId::root`][rootid], which is its own -parent. +elements in the chain of expansions. +This ID is [`ExpnId`]. +All macros receive an integer ID, assigned continuously starting from 0 as we discover new macro +calls. +All hierarchies start at [`ExpnId::root`][rootid], which is its own parent. The [`rustc_span::hygiene`][hy] crate contains all of the hygiene-related algorithms (with the exception of some hacks in [`Resolver::resolve_crate_root`][hacks]) and structures related to hygiene and expansion that are kept in global data. -The actual hierarchies are stored in [`HygieneData`][hd]. This is a global -piece of data containing hygiene and expansion info that can be accessed from +The actual hierarchies are stored in [`HygieneData`][hd]. +This is a global piece of data containing hygiene and expansion info that can be accessed from any [`Ident`] without any context. @@ -278,8 +290,8 @@ any [`Ident`] without any context. The first hierarchy tracks the order of expansions, i.e., when a macro invocation is in the output of another macro. -Here, the children in the hierarchy will be the "innermost" tokens. The -[`ExpnData`] struct itself contains a subset of properties from both macro +Here, the children in the hierarchy will be the "innermost" tokens. +The [`ExpnData`] struct itself contains a subset of properties from both macro definition and macro call available through global data. [`ExpnData::parent`][edp] tracks the child-to-parent link in this hierarchy. @@ -300,19 +312,19 @@ In this code, the AST nodes that are finally generated would have hierarchy ### The Macro Definition Hierarchy The second hierarchy tracks the order of macro definitions, i.e., when we are -expanding one macro another macro definition is revealed in its output. This -one is a bit tricky and more complex than the other two hierarchies. +expanding one macro another macro definition is revealed in its output. +This one is a bit tricky and more complex than the other two hierarchies. [`SyntaxContext`][sc] represents a whole chain in this hierarchy via an ID. [`SyntaxContextData`][scd] contains data associated with the given -[`SyntaxContext`][sc]; mostly it is a cache for results of filtering that chain in -different ways. [`SyntaxContextData::parent`][scdp] is the child-to-parent -link here, and [`SyntaxContextData::outer_expns`][scdoe] are individual -elements in the chain. The "chaining-operator" is -[`SyntaxContext::apply_mark`][am] in compiler code. +[`SyntaxContext`][sc]; mostly it is a cache for results of filtering that chain in different ways. + [`SyntaxContextData::parent`][scdp] is the child-to-parent +link here, and [`SyntaxContextData::outer_expns`][scdoe] are individual elements in the chain. +The "chaining-operator" is [`SyntaxContext::apply_mark`][am] in compiler code. A [`Span`][span], mentioned above, is actually just a compact representation of -a code location and [`SyntaxContext`][sc]. Likewise, an [`Ident`] is just an interned +a code location and [`SyntaxContext`][sc]. +Likewise, an [`Ident`] is just an interned [`Symbol`] + `Span` (i.e. an interned string + hygiene data). [`Symbol`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html @@ -324,13 +336,14 @@ a code location and [`SyntaxContext`][sc]. Likewise, an [`Ident`] is just an int For built-in macros, we use the context: [`SyntaxContext::empty().apply_mark(expn_id)`], and such macros are -considered to be defined at the hierarchy root. We do the same for `proc -macro`s because we haven't implemented cross-crate hygiene yet. +considered to be defined at the hierarchy root. +We do the same for `proc macro`s because we haven't implemented cross-crate hygiene yet. [`SyntaxContext::empty().apply_mark(expn_id)`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html#method.apply_mark If the token had context `X` before being produced by a macro then after being -produced by the macro it has context `X -> macro_id`. Here are some examples: +produced by the macro it has context `X -> macro_id`. +Here are some examples: Example 0: @@ -374,8 +387,8 @@ After all expansions, `foo` has context `ROOT -> id(n)` and `bar` has context Currently this hierarchy for tracking macro definitions is subject to the so-called ["context transplantation hack"][hack]. Modern (i.e. experimental) macros have stronger hygiene than the legacy "Macros By Example" (MBE) -system which can result in weird interactions between the two. The hack is -intended to make things "just work" for now. +system which can result in weird interactions between the two. +The hack is intended to make things "just work" for now. [`ExpnId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.ExpnId.html [hack]: https://github.com/rust-lang/rust/pull/51762#issuecomment-401400732 @@ -384,8 +397,7 @@ intended to make things "just work" for now. The third and final hierarchy tracks the location of macro invocations. -In this hierarchy [`ExpnData::call_site`][callsite] is the `child -> parent` -link. +In this hierarchy [`ExpnData::call_site`][callsite] is the `child -> parent` link. [callsite]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.ExpnData.html#structfield.call_site @@ -399,8 +411,7 @@ foo!(bar!(baz)); ``` For the `baz` AST node in the final output, the expansion-order hierarchy is -`ROOT -> id(foo) -> id(bar) -> baz`, while the call-site hierarchy is `ROOT -> -baz`. +`ROOT -> id(foo) -> id(bar) -> baz`, while the call-site hierarchy is `ROOT -> baz`. ### Macro Backtraces @@ -412,16 +423,18 @@ in [`rustc_span::hygiene`][hy]. ## Producing Macro Output Above, we saw how the output of a macro is integrated into the AST for a crate, -and we also saw how the hygiene data for a crate is generated. But how do we -actually produce the output of a macro? It depends on the type of macro. +and we also saw how the hygiene data for a crate is generated. +But how do we actually produce the output of a macro? +It depends on the type of macro. + +There are two types of macros in Rust: + 1. `macro_rules!` macros (a.k.a. + "Macros By Example" (MBE)), and, + 2. procedural macros (proc macros); including custom derives. -There are two types of macros in Rust: - 1. `macro_rules!` macros (a.k.a. "Macros By Example" (MBE)), and, - 2. procedural macros (proc macros); including custom derives. - During the parsing phase, the normal Rust parser will set aside the contents of -macros and their invocations. Later, macros are expanded using these -portions of the code. +macros and their invocations. +Later, macros are expanded using these portions of the code. Some important data structures/interfaces here: - [`SyntaxExtension`] - a lowered macro representation, contains its expander @@ -429,8 +442,8 @@ Some important data structures/interfaces here: [`TokenStream`] or AST + some additional data like stability, or a list of unstable features allowed inside the macro. - [`SyntaxExtensionKind`] - expander functions may have several different - signatures (take one token stream, or two, or a piece of AST, etc). This is - an `enum` that lists them. + signatures (take one token stream, or two, or a piece of AST, etc). + This is an `enum` that lists them. - [`BangProcMacro`]/[`TTMacroExpander`]/[`AttrProcMacro`]/[`MultiItemModifier`] - `trait`s representing the expander function signatures. @@ -443,12 +456,12 @@ Some important data structures/interfaces here: ## Macros By Example -MBEs have their own parser distinct from the Rust parser. When macros are -expanded, we may invoke the MBE parser to parse and expand a macro. The -MBE parser, in turn, may call the Rust parser when it needs to bind a +MBEs have their own parser distinct from the Rust parser. +When macros are expanded, we may invoke the MBE parser to parse and expand a macro. + The MBE parser, in turn, may call the Rust parser when it needs to bind a metavariable (e.g. `$my_expr`) while parsing the contents of a macro -invocation. The code for macro expansion is in -[`compiler/rustc_expand/src/mbe/`][code_dir]. +invocation. +The code for macro expansion is in [`compiler/rustc_expand/src/mbe/`][code_dir]. ### Example @@ -464,21 +477,22 @@ macro_rules! printer { } ``` -Here `$mvar` is called a _metavariable_. Unlike normal variables, rather than -binding to a value _at runtime_, a metavariable binds _at compile time_ to a -tree of _tokens_. A _token_ is a single "unit" of the grammar, such as an +Here `$mvar` is called a _metavariable_. +Unlike normal variables, rather than +binding to a value _at runtime_, a metavariable binds _at compile time_ to a tree of _tokens_. +A _token_ is a single "unit" of the grammar, such as an identifier (e.g. `foo`) or punctuation (e.g. `=>`). There are also other -special tokens, such as `EOF`, which itself indicates that there are no more -tokens. There are token trees resulting from the paired parentheses-like +special tokens, such as `EOF`, which itself indicates that there are no more tokens. +There are token trees resulting from the paired parentheses-like characters (`(`...`)`, `[`...`]`, and `{`...`}`) – they include the open and -close and all the tokens in between (Rust requires that parentheses-like -characters be balanced). Having macro expansion operate on token streams +close and all the tokens in between (Rust requires that parentheses-like characters be balanced). +Having macro expansion operate on token streams rather than the raw bytes of a source-file abstracts away a lot of complexity. The macro expander (and much of the rest of the compiler) doesn't consider the exact line and column of some syntactic construct in the code; it considers -which constructs are used in the code. Using tokens allows us to care about -_what_ without worrying about _where_. For more information about tokens, see -the [Parsing][parsing] chapter of this book. +which constructs are used in the code. +Using tokens allows us to care about _what_ without worrying about _where_. +For more information about tokens, see the [Parsing][parsing] chapter of this book. ```rust,ignore printer!(print foo); // `foo` is a variable @@ -490,15 +504,14 @@ The process of expanding the macro invocation into the syntax tree ### The MBE parser -There are two parts to MBE expansion done by the macro parser: +There are two parts to MBE expansion done by the macro parser: 1. parsing the definition, and, - 2. parsing the invocations. + 2. parsing the invocations. We think of the MBE parser as a nondeterministic finite automaton (NFA) based regex parser since it uses an algorithm similar in spirit to the [Earley -parsing algorithm](https://en.wikipedia.org/wiki/Earley_parser). The macro -parser is defined in -[`compiler/rustc_expand/src/mbe/macro_parser.rs`][code_mp]. +parsing algorithm](https://en.wikipedia.org/wiki/Earley_parser). +The macro parser is defined in [`compiler/rustc_expand/src/mbe/macro_parser.rs`][code_mp]. The interface of the macro parser is as follows (this is slightly simplified): @@ -513,31 +526,31 @@ fn parse_tt( We use these items in macro parser: - a `parser` variable is a reference to the state of a normal Rust parser, - including the token stream and parsing session. The token stream is what we - are about to ask the MBE parser to parse. We will consume the raw stream of + including the token stream and parsing session. + The token stream is what we are about to ask the MBE parser to parse. + We will consume the raw stream of tokens and output a binding of metavariables to corresponding token trees. The parsing session can be used to report parser errors. - a `matcher` variable is a sequence of [`MatcherLoc`]s that we want to match the token stream - against. They're converted from the original token trees in the macro's definition before - matching. + against. + They're converted from the original token trees in the macro's definition before matching. [`MatcherLoc`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/mbe/macro_parser/enum.MatcherLoc.html In the analogy of a regex parser, the token stream is the input and we are -matching it against the pattern defined by matcher. Using our examples, the +matching it against the pattern defined by matcher. +Using our examples, the token stream could be the stream of tokens containing the inside of the example -invocation `print foo`, while matcher might be the sequence of token (trees) -`print $mvar:ident`. +invocation `print foo`, while matcher might be the sequence of token (trees) `print $mvar:ident`. -The output of the parser is a [`ParseResult`], which indicates which of -three cases has occurred: +The output of the parser is a [`ParseResult`], which indicates which of three cases has occurred: - **Success**: the token stream matches the given matcher and we have produced a binding from metavariables to the corresponding token trees. - **Failure**: the token stream does not match matcher and results in an error message such as "No rule expected token ...". -- **Error**: some fatal error has occurred _in the parser_. For example, this - happens if there is more than one pattern match, since that indicates the +- **Error**: some fatal error has occurred _in the parser_. + For example, this happens if there is more than one pattern match, since that indicates the macro is ambiguous. The full interface is defined [here][code_parse_int]. @@ -553,11 +566,13 @@ For more information about the macro parser's implementation, see the comments i Using our example, we would try to match the token stream `print foo` from the invocation against the matchers `print $mvar:ident` and `print twice $mvar:ident` that we previously extracted from the -rules in the macro definition. When the macro parser comes to a place in the current matcher where +rules in the macro definition. +When the macro parser comes to a place in the current matcher where it needs to match a _non-terminal_ (e.g. `$mvar:ident`), it calls back to the normal Rust parser to -get the contents of that non-terminal. In this case, the Rust parser would look for an `ident` -token, which it finds (`foo`) and returns to the macro parser. Then, the macro parser continues -parsing. +get the contents of that non-terminal. +In this case, the Rust parser would look for an `ident` +token, which it finds (`foo`) and returns to the macro parser. +Then, the macro parser continues parsing. Note that exactly one of the matchers from the various rules should match the invocation; if there is more than one match, the parse is ambiguous, while if there are no matches at all, there is a syntax @@ -568,17 +583,19 @@ rule, substituting the values of any matches it captured when matching against t ## Procedural Macros -Procedural macros are also expanded during parsing. However, rather than -having a parser in the compiler, proc macros are implemented as custom, -third-party crates. The compiler will compile the proc macro crate and +Procedural macros are also expanded during parsing. +However, rather than having a parser in the compiler, proc macros are implemented as custom, +third-party crates. +The compiler will compile the proc macro crate and specially annotated functions in them (i.e. the proc macro itself), passing -them a stream of tokens. A proc macro can then transform the token stream and +them a stream of tokens. +A proc macro can then transform the token stream and output a new token stream, which is synthesized into the AST. -The token stream type used by proc macros is _stable_, so `rustc` does not -use it internally. The compiler's (unstable) token stream is defined in -[`rustc_ast::tokenstream::TokenStream`][rustcts]. This is converted into the -stable [`proc_macro::TokenStream`][stablets] and back in +The token stream type used by proc macros is _stable_, so `rustc` does not use it internally. +The compiler's (unstable) token stream is defined in +[`rustc_ast::tokenstream::TokenStream`][rustcts]. +This is converted into the stable [`proc_macro::TokenStream`][stablets] and back in [`rustc_expand::proc_macro`][pm] and [`rustc_expand::proc_macro_server`][pms]. Since the Rust ABI is currently unstable, we use the C ABI for this conversion. diff --git a/src/doc/rustc-dev-guide/src/notification-groups/about.md b/src/doc/rustc-dev-guide/src/notification-groups/about.md index 2c2c98860a9b..86797b1e0bb5 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/about.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/about.md @@ -7,11 +7,11 @@ and joining does not entail any particular commitment. Once you [join a notification group](#join), you will be added to a list that receives pings on github whenever a new issue is found -that fits the notification group's criteria. If you are interested, you -can then [claim the issue] and start working on it. +that fits the notification group's criteria. +If you are interested, you can then [claim the issue] and start working on it. -Of course, you don't have to wait for new issues to be tagged! If you -prefer, you can use the GitHub label for a notification group to +Of course, you don't have to wait for new issues to be tagged! +If you prefer, you can use the GitHub label for a notification group to search for existing issues that haven't been claimed yet. [claim the issue]: https://forge.rust-lang.org/triagebot/issue-assignment.html @@ -37,8 +37,8 @@ particularly those of **middle priority**: - By **isolated**, we mean that we do not expect large-scale refactoring to be required to fix the bug. - By **middle priority**, we mean that we'd like to see the bug fixed, - but it's not such a burning problem that we are dropping everything - else to fix it. The danger with such bugs, of course, is that they + but it's not such a burning problem that we are dropping everything else to fix it. + The danger with such bugs, of course, is that they can accumulate over time, and the role of the notification group is to try and stop that from happening! @@ -48,8 +48,7 @@ particularly those of **middle priority**: To join a notification group, you just have to open a PR adding your GitHub username to the appropriate file in the Rust team repository. -See the "example PRs" below to get a precise idea and to identify the -file to edit. +See the "example PRs" below to get a precise idea and to identify the file to edit. Also, if you are not already a member of a Rust team then -- in addition to adding your name to the file -- you have to checkout the repository and @@ -73,8 +72,8 @@ Example PRs: ## Tagging an issue for a notification group To tag an issue as appropriate for a notification group, you give -[rustbot] a [`ping`] command with the name of the notification -group. For example: +[rustbot] a [`ping`] command with the name of the notification group. +For example: ```text @rustbot ping apple @@ -87,8 +86,8 @@ group. For example: ``` To make some commands shorter and easier to remember, there are aliases, -defined in the [`triagebot.toml`] file. For example, all of these commands -are equivalent and will ping the Apple group: +defined in the [`triagebot.toml`] file. +For example, all of these commands are equivalent and will ping the Apple group: ```text @rustbot ping apple @@ -97,12 +96,12 @@ are equivalent and will ping the Apple group: ``` Keep in mind that these aliases are meant to make humans' life easier. -They might be subject to change. If you need to ensure that a command +They might be subject to change. +If you need to ensure that a command will always be valid, prefer the full invocations over the aliases. **Note though that this should only be done by compiler team members -or contributors, and is typically done as part of compiler team -triage.** +or contributors, and is typically done as part of compiler team triage.** [rustbot]: https://github.com/rust-lang/triagebot/ [`ping`]: https://forge.rust-lang.org/triagebot/pinging.html diff --git a/src/doc/rustc-dev-guide/src/notification-groups/arm.md b/src/doc/rustc-dev-guide/src/notification-groups/arm.md index bffcc6c04571..b71c3d606751 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/arm.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/arm.md @@ -10,13 +10,11 @@ ARM-related issues as well as suggestions on how to resolve interesting questions regarding our ARM support. The group also has an associated Zulip channel ([`#t-compiler/arm`]) -where people can go to pose questions and discuss ARM-specific -topics. +where people can go to pose questions and discuss ARM-specific topics. -So, if you are interested in participating, please sign up for the -ARM group! To do so, open a PR against the [rust-lang/team] -repository. Just [follow this example][eg], but change the username to -your own! +So, if you are interested in participating, please sign up for the ARM group! +To do so, open a PR against the [rust-lang/team] repository. +Just [follow this example][eg], but change the username to your own! [`#t-compiler/arm`]: https://rust-lang.zulipchat.com/#narrow/stream/242906-t-compiler.2Farm [rust-lang/team]: https://github.com/rust-lang/team diff --git a/src/doc/rustc-dev-guide/src/notification-groups/emscripten.md b/src/doc/rustc-dev-guide/src/notification-groups/emscripten.md index 4996ed62e46a..685517c8d16f 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/emscripten.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/emscripten.md @@ -10,13 +10,11 @@ Emscripten-related issues as well as suggestions on how to resolve interesting questions regarding our Emscripten support. The group also has an associated Zulip channel ([`#t-compiler/wasm`]) -where people can go to pose questions and discuss Emscripten-specific -topics. +where people can go to pose questions and discuss Emscripten-specific topics. -So, if you are interested in participating, please sign up for the -Emscripten group! To do so, open a PR against the [rust-lang/team] -repository. Just [follow this example][eg], but change the username to -your own! +So, if you are interested in participating, please sign up for the Emscripten group! +To do so, open a PR against the [rust-lang/team] repository. +Just [follow this example][eg], but change the username to your own! [`#t-compiler/wasm`]: https://rust-lang.zulipchat.com/#narrow/stream/463513-t-compiler.2Fwasm [rust-lang/team]: https://github.com/rust-lang/team diff --git a/src/doc/rustc-dev-guide/src/notification-groups/fuchsia.md b/src/doc/rustc-dev-guide/src/notification-groups/fuchsia.md index fd9c5d236f5c..3c07caaa5ef8 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/fuchsia.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/fuchsia.md @@ -6,7 +6,6 @@ [O-fuchsia]: https://github.com/rust-lang/rust/labels/O-fuchsia This list will be used to notify [Fuchsia][fuchsia] maintainers -when the compiler or the standard library changes in a way that would -break the Fuchsia integration. +when the compiler or the standard library changes in a way that would break the Fuchsia integration. [fuchsia]: ../tests/ecosystem-test-jobs/fuchsia.md diff --git a/src/doc/rustc-dev-guide/src/notification-groups/loongarch.md b/src/doc/rustc-dev-guide/src/notification-groups/loongarch.md index 09620a6a5ce8..8b1c33370f73 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/loongarch.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/loongarch.md @@ -12,10 +12,9 @@ interesting questions regarding our LoongArch support. The group also has an associated Zulip channel ([`#t-compiler/loong-arch`]) where people can go to pose questions and discuss LoongArch-specific topics. -So, if you are interested in participating, please sign up for the -LoongArch group! To do so, open a PR against the [rust-lang/team] -repository. Just [follow this example][eg], but change the username to -your own! +So, if you are interested in participating, please sign up for the LoongArch group! +To do so, open a PR against the [rust-lang/team] repository. +Just [follow this example][eg], but change the username to your own! [`#t-compiler/loong-arch`]: https://rust-lang.zulipchat.com/#narrow/channel/551512-t-compiler.2Floong-arch [rust-lang/team]: https://github.com/rust-lang/team diff --git a/src/doc/rustc-dev-guide/src/notification-groups/risc-v.md b/src/doc/rustc-dev-guide/src/notification-groups/risc-v.md index 250a512fbaac..d0b51572d52a 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/risc-v.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/risc-v.md @@ -10,13 +10,11 @@ RISC-V-related issues as well as suggestions on how to resolve interesting questions regarding our RISC-V support. The group also has an associated Zulip channel ([`#t-compiler/risc-v`]) -where people can go to pose questions and discuss RISC-V-specific -topics. +where people can go to pose questions and discuss RISC-V-specific topics. -So, if you are interested in participating, please sign up for the -RISC-V group! To do so, open a PR against the [rust-lang/team] -repository. Just [follow this example][eg], but change the username to -your own! +So, if you are interested in participating, please sign up for the RISC-V group! +To do so, open a PR against the [rust-lang/team] repository. +Just [follow this example][eg], but change the username to your own! [`#t-compiler/risc-v`]: https://rust-lang.zulipchat.com/#narrow/stream/250483-t-compiler.2Frisc-v [rust-lang/team]: https://github.com/rust-lang/team diff --git a/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md b/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md index c08cf9deecec..cb2a477cea1d 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md @@ -7,14 +7,13 @@ This list will be used to notify [Rust for Linux (RfL)][rfl] maintainers when the compiler or the standard library changes in a way that would -break Rust for Linux, since it depends on several unstable flags -and features. The RfL maintainers should then ideally provide support +break Rust for Linux, since it depends on several unstable flags and features. +The RfL maintainers should then ideally provide support for resolving the breakage or decide to temporarily accept the breakage and unblock CI by temporarily removing the RfL CI jobs. The group also has an associated Zulip channel ([`#rust-for-linux`]) -where people can go to ask questions and discuss topics related to Rust -for Linux. +where people can go to ask questions and discuss topics related to Rust for Linux. If you are interested in participating, please sign up for the Rust for Linux group on [Zulip][`#rust-for-linux`]! diff --git a/src/doc/rustc-dev-guide/src/notification-groups/wasi.md b/src/doc/rustc-dev-guide/src/notification-groups/wasi.md index 3d7fd01af28d..93962a54fdfb 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/wasi.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/wasi.md @@ -10,13 +10,11 @@ WASI-related issues as well as suggestions on how to resolve interesting questions regarding our WASI support. The group also has an associated Zulip channel ([`#t-compiler/wasm`]) -where people can go to pose questions and discuss WASI-specific -topics. +where people can go to pose questions and discuss WASI-specific topics. -So, if you are interested in participating, please sign up for the -WASI group! To do so, open a PR against the [rust-lang/team] -repository. Just [follow this example][eg], but change the username to -your own! +So, if you are interested in participating, please sign up for the WASI group! +To do so, open a PR against the [rust-lang/team] repository. +Just [follow this example][eg], but change the username to your own! [`#t-compiler/wasm`]: https://rust-lang.zulipchat.com/#narrow/stream/463513-t-compiler.2Fwasm [rust-lang/team]: https://github.com/rust-lang/team diff --git a/src/doc/rustc-dev-guide/src/notification-groups/windows.md b/src/doc/rustc-dev-guide/src/notification-groups/windows.md index 4b3970a9d63f..797eb69fd1cf 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/windows.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/windows.md @@ -10,8 +10,7 @@ Windows-related issues as well as suggestions on how to resolve interesting questions regarding our Windows support. The group also has an associated Zulip channel ([`#t-compiler/windows`]) -where people can go to pose questions and discuss Windows-specific -topics. +where people can go to pose questions and discuss Windows-specific topics. To get a better idea for what the group will do, here are some examples of the kinds of questions where we would have reached out to @@ -19,12 +18,12 @@ the group for advice in determining the best course of action: * Which versions of MinGW should we support? * Should we remove the legacy InnoSetup GUI installer? [#72569] -* What names should we use for static libraries on Windows? [#29520] +* What names should we use for static libraries on Windows? + [#29520] -So, if you are interested in participating, please sign up for the -Windows group! To do so, open a PR against the [rust-lang/team] -repository. Just [follow this example][eg], but change the username to -your own! +So, if you are interested in participating, please sign up for the Windows group! +To do so, open a PR against the [rust-lang/team] repository. +Just [follow this example][eg], but change the username to your own! [`#t-compiler/windows`]: https://rust-lang.zulipchat.com/#streams/242869/t-compiler.2Fwindows [rust-lang/team]: https://github.com/rust-lang/team diff --git a/src/doc/rustc-dev-guide/src/overview.md b/src/doc/rustc-dev-guide/src/overview.md index b90bb173c475..374a3c5bfa40 100644 --- a/src/doc/rustc-dev-guide/src/overview.md +++ b/src/doc/rustc-dev-guide/src/overview.md @@ -1,75 +1,77 @@ # Overview of the compiler -This chapter is about the overall process of compiling a program -- how -everything fits together. +This chapter is about the overall process of compiling a program -- how everything fits together. The Rust compiler is special in two ways: it does things to your code that other compilers don't do (e.g. borrow-checking) and it has a lot of -unconventional implementation choices (e.g. queries). We will talk about these -in turn in this chapter, and in the rest of the guide, we will look at the +unconventional implementation choices (e.g. queries). +We will talk about these in turn in this chapter, and in the rest of the guide, we will look at the individual pieces in more detail. ## What the compiler does to your code -So first, let's look at what the compiler does to your code. For now, we will -avoid mentioning how the compiler implements these steps except as needed. +So first, let's look at what the compiler does to your code. +For now, we will avoid mentioning how the compiler implements these steps except as needed. ### Invocation Compilation begins when a user writes a Rust source program in text and invokes -the `rustc` compiler on it. The work that the compiler needs to perform is -defined by command-line options. For example, it is possible to enable nightly +the `rustc` compiler on it. +The work that the compiler needs to perform is defined by command-line options. +For example, it is possible to enable nightly features (`-Z` flags), perform `check`-only builds, or emit the LLVM Intermediate Representation (`LLVM-IR`) rather than executable machine code. The `rustc` executable call may be indirect through the use of `cargo`. -Command line argument parsing occurs in the [`rustc_driver`]. This crate -defines the compile configuration that is requested by the user and passes it +Command line argument parsing occurs in the [`rustc_driver`]. +This crate defines the compile configuration that is requested by the user and passes it to the rest of the compilation process as a [`rustc_interface::Config`]. ### Lexing and parsing -The raw Rust source text is analyzed by a low-level *lexer* located in -[`rustc_lexer`]. At this stage, the source text is turned into a stream of -atomic source code units known as _tokens_. The `lexer` supports the -Unicode character encoding. +The raw Rust source text is analyzed by a low-level *lexer* located in [`rustc_lexer`]. +At this stage, the source text is turned into a stream of +atomic source code units known as _tokens_. + The `lexer` supports the Unicode character encoding. The token stream passes through a higher-level lexer located in -[`rustc_parse`] to prepare for the next stage of the compile process. The -[`Lexer`] `struct` is used at this stage to perform a set of validations +[`rustc_parse`] to prepare for the next stage of the compile process. +The [`Lexer`] `struct` is used at this stage to perform a set of validations and turn strings into interned symbols (_interning_ is discussed later). -[String interning] is a way of storing only one immutable -copy of each distinct string value. +[String interning] is a way of storing only one immutable copy of each distinct string value. The lexer has a small interface and doesn't depend directly on the diagnostic -infrastructure in `rustc`. Instead it provides diagnostics as plain data which -are emitted in [`rustc_parse::lexer`] as real diagnostics. The `lexer` -preserves full fidelity information for both IDEs and procedural macros +infrastructure in `rustc`. +Instead it provides diagnostics as plain data which +are emitted in [`rustc_parse::lexer`] as real diagnostics. +The `lexer` preserves full fidelity information for both IDEs and procedural macros (sometimes referred to as "proc-macros"). The *parser* [translates the token stream from the `lexer` into an Abstract Syntax -Tree (AST)][parser]. It uses a recursive descent (top-down) approach to syntax -analysis. The crate entry points for the `parser` are the +Tree (AST)][parser]. +It uses a recursive descent (top-down) approach to syntax analysis. +The crate entry points for the `parser` are the [`Parser::parse_crate_mod`][parse_crate_mod] and [`Parser::parse_mod`][parse_mod] -methods found in [`rustc_parse::parser::Parser`]. The external module parsing +methods found in [`rustc_parse::parser::Parser`]. +The external module parsing entry point is [`rustc_expand::module::parse_external_mod`][parse_external_mod]. And the macro-`parser` entry point is [`Parser::parse_nonterminal`][parse_nonterminal]. Parsing is performed with a set of [`parser`] utility methods including [`bump`], [`check`], [`eat`], [`expect`], [`look_ahead`]. -Parsing is organized by semantic construct. Separate -`parse_*` methods can be found in the [`rustc_parse`][rustc_parse_parser_dir] -directory. The source file name follows the construct name. For example, the -following files are found in the `parser`: +Parsing is organized by semantic construct. +Separate `parse_*` methods can be found in the [`rustc_parse`][rustc_parse_parser_dir] directory. +The source file name follows the construct name. +For example, the following files are found in the `parser`: - [`expr.rs`](https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_parse/src/parser/expr.rs) - [`pat.rs`](https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_parse/src/parser/pat.rs) - [`ty.rs`](https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_parse/src/parser/ty.rs) - [`stmt.rs`](https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_parse/src/parser/stmt.rs) -This naming scheme is used across many compiler stages. You will find either a -file or directory with the same name across the parsing, lowering, type +This naming scheme is used across many compiler stages. +You will find either a file or directory with the same name across the parsing, lowering, type checking, [Typed High-level Intermediate Representation (`THIR`)][thir] lowering, and [Mid-level Intermediate Representation (`MIR`)][mir] building sources. @@ -77,26 +79,25 @@ Macro-expansion, `AST`-validation, name-resolution, and early linting also take place during the lexing and parsing stage. The [`rustc_ast::ast`]::{[`Crate`], [`Expr`], [`Pat`], ...} `AST` nodes are -returned from the parser while the standard [`Diag`] API is used -for error handling. Generally Rust's compiler will try to recover from errors +returned from the parser while the standard [`Diag`] API is used for error handling. +Generally Rust's compiler will try to recover from errors by parsing a superset of Rust's grammar, while also emitting an error type. ### `AST` lowering Next the `AST` is converted into [High-Level Intermediate Representation -(`HIR`)][hir], a more compiler-friendly representation of the `AST`. This process -is called "lowering" and involves a lot of desugaring (the expansion and -formalizing of shortened or abbreviated syntax constructs) of things like loops -and `async fn`. +(`HIR`)][hir], a more compiler-friendly representation of the `AST`. +This process is called "lowering" and involves a lot of desugaring (the expansion and +formalizing of shortened or abbreviated syntax constructs) of things like loops and `async fn`. We then use the `HIR` to do [*type inference*] (the process of automatic detection of the type of an expression), [*trait solving*] (the process of -pairing up an impl with each reference to a `trait`), and [*type checking*]. Type -checking is the process of converting the types found in the `HIR` ([`hir::Ty`]), +pairing up an impl with each reference to a `trait`), and [*type checking*]. +Type checking is the process of converting the types found in the `HIR` ([`hir::Ty`]), which represent what the user wrote, into the internal representation used by -the compiler ([`Ty<'tcx>`]). It's called type checking because the information -is used to verify the type safety, correctness and coherence of the types used -in the program. +the compiler ([`Ty<'tcx>`]). +It's called type checking because the information +is used to verify the type safety, correctness and coherence of the types used in the program. ### `MIR` lowering @@ -105,29 +106,31 @@ The `HIR` is further lowered to `MIR` pattern and exhaustiveness checking) to convert into `MIR`. We do [many optimizations on the MIR][mir-opt] because it is generic and that -improves later code generation and compilation speed. It is easier to do some -optimizations at `MIR` level than at `LLVM-IR` level. For example LLVM doesn't seem +improves later code generation and compilation speed. +It is easier to do some optimizations at `MIR` level than at `LLVM-IR` level. +For example LLVM doesn't seem to be able to optimize the pattern the [`simplify_try`] `MIR`-opt looks for. Rust code is also [_monomorphized_] during code generation, which means making -copies of all the generic code with the type parameters replaced by concrete -types. To do this, we need to collect a list of what concrete types to generate -code for. This is called _monomorphization collection_ and it happens at the -`MIR` level. +copies of all the generic code with the type parameters replaced by concrete types. +To do this, we need to collect a list of what concrete types to generate code for. +This is called _monomorphization collection_ and it happens at the `MIR` level. [_monomorphized_]: https://en.wikipedia.org/wiki/Monomorphization ### Code generation -We then begin what is simply called _code generation_ or _codegen_. The [code -generation stage][codegen] is when higher-level representations of source are -turned into an executable binary. Since `rustc` uses LLVM for code generation, -the first step is to convert the `MIR` to `LLVM-IR`. This is where the `MIR` is -actually monomorphized. The `LLVM-IR` is passed to LLVM, which does a lot more +We then begin what is simply called _code generation_ or _codegen_. +The [code generation stage][codegen] is when higher-level representations of source are +turned into an executable binary. +Since `rustc` uses LLVM for code generation, +the first step is to convert the `MIR` to `LLVM-IR`. +This is where the `MIR` is actually monomorphized. +The `LLVM-IR` is passed to LLVM, which does a lot more optimizations on it, emitting machine code which is basically assembly code with additional low-level types and annotations added (e.g. an ELF object or -`WASM`). The different libraries/binaries are then linked together to produce -the final binary. +`WASM`). +The different libraries/binaries are then linked together to produce the final binary. [*trait solving*]: traits/resolution.md [*type checking*]: hir-typeck/summary.md @@ -171,24 +174,24 @@ the final binary. ## How it does it Now that we have a high-level view of what the compiler does to your code, -let's take a high-level view of _how_ it does all that stuff. There are a lot -of constraints and conflicting goals that the compiler needs to -satisfy/optimize for. For example, +let's take a high-level view of _how_ it does all that stuff. +There are a lot of constraints and conflicting goals that the compiler needs to +satisfy/optimize for. +For example, -- Compilation speed: how fast is it to compile a program? More/better - compile-time analyses often means compilation is slower. - - Also, we want to support incremental compilation, so we need to take that - into account. How can we keep track of what work needs to be redone and +- Compilation speed: how fast is it to compile a program? + More/better compile-time analyses often means compilation is slower. + - Also, we want to support incremental compilation, so we need to take that into account. + How can we keep track of what work needs to be redone and what can be reused if the user modifies their program? - Also we can't store too much stuff in the incremental cache because it would take a long time to load from disk and it could take a lot of space on the user's system... -- Compiler memory usage: while compiling a program, we don't want to use more - memory than we need. -- Program speed: how fast is your compiled program? More/better compile-time - analyses often means the compiler can do better optimizations. -- Program size: how large is the compiled binary? Similar to the previous - point. +- Compiler memory usage: while compiling a program, we don't want to use more memory than we need. +- Program speed: how fast is your compiled program? + More/better compile-time analyses often means the compiler can do better optimizations. +- Program size: how large is the compiled binary? + Similar to the previous point. - Compiler compilation speed: how long does it take to compile the compiler? This impacts contributors and compiler maintenance. - Implementation complexity: building a compiler is one of the hardest @@ -199,123 +202,131 @@ satisfy/optimize for. For example, tremendous amount of change constantly going on. - Integration: a number of other tools need to use the compiler in various ways (e.g. `cargo`, `clippy`, `Miri`) that must be supported. -- Compiler stability: the compiler should not crash or fail ungracefully on the - stable channel. +- Compiler stability: the compiler should not crash or fail ungracefully on the stable channel. - Rust stability: the compiler must respect Rust's stability guarantees by not breaking programs that previously compiled despite the many changes that are always going on to its implementation. - Limitations of other tools: `rustc` uses LLVM in its backend, and LLVM has some strengths we leverage and some aspects we need to work around. -So, as you continue your journey through the rest of the guide, keep these -things in mind. They will often inform decisions that we make. +So, as you continue your journey through the rest of the guide, keep these things in mind. +They will often inform decisions that we make. ### Intermediate representations As with most compilers, `rustc` uses some intermediate representations (IRs) to -facilitate computations. In general, working directly with the source code is -extremely inconvenient and error-prone. Source code is designed to be human-friendly while at +facilitate computations. +In general, working directly with the source code is extremely inconvenient and error-prone. +Source code is designed to be human-friendly while at the same time being unambiguous, but it's less convenient for doing something like, say, type checking. Instead most compilers, including `rustc`, build some sort of IR out of the -source code which is easier to analyze. `rustc` has a few IRs, each optimized -for different purposes: +source code which is easier to analyze. +`rustc` has a few IRs, each optimized for different purposes: -- Token stream: the lexer produces a stream of tokens directly from the source - code. This stream of tokens is easier for the parser to deal with than raw - text. +- Token stream: the lexer produces a stream of tokens directly from the source code. + This stream of tokens is easier for the parser to deal with than raw text. - Abstract Syntax Tree (`AST`): the abstract syntax tree is built from the stream - of tokens produced by the lexer. It represents - pretty much exactly what the user wrote. It helps to do some syntactic sanity + of tokens produced by the lexer. + It represents pretty much exactly what the user wrote. + It helps to do some syntactic sanity checking (e.g. checking that a type is expected where the user wrote one). -- High-level IR (HIR): This is a sort of desugared `AST`. It's still close - to what the user wrote syntactically, but it includes some implicit things +- High-level IR (HIR): This is a sort of desugared `AST`. + It's still close to what the user wrote syntactically, but it includes some implicit things such as some elided lifetimes, etc. This IR is amenable to type checking. - Typed `HIR` (THIR) _formerly High-level Abstract IR (HAIR)_: This is an - intermediate between `HIR` and MIR. It is like the `HIR` but it is fully typed + intermediate between `HIR` and MIR. + It is like the `HIR` but it is fully typed and a bit more desugared (e.g. method calls and implicit dereferences are - made fully explicit). As a result, it is easier to lower to `MIR` from `THIR` than - from HIR. -- Middle-level IR (`MIR`): This IR is basically a Control-Flow Graph (CFG). A CFG - is a type of diagram that shows the basic blocks of a program and how control - flow can go between them. Likewise, `MIR` also has a bunch of basic blocks with + made fully explicit). + As a result, it is easier to lower to `MIR` from `THIR` than from HIR. +- Middle-level IR (`MIR`): This IR is basically a Control-Flow Graph (CFG). + A CFG is a type of diagram that shows the basic blocks of a program and how control + flow can go between them. + Likewise, `MIR` also has a bunch of basic blocks with simple typed statements inside them (e.g. assignment, simple computations, etc) and control flow edges to other basic blocks (e.g., calls, dropping - values). `MIR` is used for borrow checking and other + values). + `MIR` is used for borrow checking and other important dataflow-based checks, such as checking for uninitialized values. - It is also used for a series of optimizations and for constant evaluation (via - `Miri`). Because `MIR` is still generic, we can do a lot of analyses here more + It is also used for a series of optimizations and for constant evaluation (via `Miri`). + Because `MIR` is still generic, we can do a lot of analyses here more efficiently than after monomorphization. -- `LLVM-IR`: This is the standard form of all input to the LLVM compiler. `LLVM-IR` - is a sort of typed assembly language with lots of annotations. It's +- `LLVM-IR`: This is the standard form of all input to the LLVM compiler. + `LLVM-IR` is a sort of typed assembly language with lots of annotations. + It's a standard format that is used by all compilers that use LLVM (e.g. the clang - C compiler also outputs `LLVM-IR`). `LLVM-IR` is designed to be easy for other - compilers to emit and also rich enough for LLVM to run a bunch of - optimizations on it. + C compiler also outputs `LLVM-IR`). + `LLVM-IR` is designed to be easy for other + compilers to emit and also rich enough for LLVM to run a bunch of optimizations on it. One other thing to note is that many values in the compiler are _interned_. This is a performance and memory optimization in which we allocate the values in -a special allocator called an -_[arena]_. Then, we pass -around references to the values allocated in the arena. This allows us to make +a special allocator called an _[arena]_. +Then, we pass around references to the values allocated in the arena. +This allows us to make sure that identical values (e.g. types in your program) are only allocated once -and can be compared cheaply by comparing pointers. Many of the intermediate -representations are interned. +and can be compared cheaply by comparing pointers. +Many of the intermediate representations are interned. [arena]: https://en.wikipedia.org/wiki/Region-based_memory_management ### Queries -The first big implementation choice is Rust's use of the _query_ system in its -compiler. The Rust compiler _is not_ organized as a series of passes over the -code which execute sequentially. The Rust compiler does this to make +The first big implementation choice is Rust's use of the _query_ system in its compiler. +The Rust compiler _is not_ organized as a series of passes over the code which execute sequentially. +The Rust compiler does this to make incremental compilation possible -- that is, if the user makes a change to their program and recompiles, we want to do as little redundant work as possible to output the new binary. -In `rustc`, all the major steps above are organized as a bunch of queries that -call each other. For example, there is a query to ask for the type of something -and another to ask for the optimized `MIR` of a function. These queries can call -each other and are all tracked through the query system. The results of the -queries are cached on disk so that the compiler can tell which queries' results -changed from the last compilation and only redo those. This is how incremental -compilation works. +In `rustc`, all the major steps above are organized as a bunch of queries that call each other. +For example, there is a query to ask for the type of something +and another to ask for the optimized `MIR` of a function. +These queries can call each other and are all tracked through the query system. +The results of the queries are cached on disk so that the compiler can tell which queries' results +changed from the last compilation and only redo those. +This is how incremental compilation works. -In principle, for the query-fied steps, we do each of the above for each item -individually. For example, we will take the `HIR` for a function and use queries -to ask for the `LLVM-IR` for that HIR. This drives the generation of optimized -`MIR`, which drives the borrow checker, which drives the generation of `MIR`, and -so on. +In principle, for the query-fied steps, we do each of the above for each item individually. +For example, we will take the `HIR` for a function and use queries +to ask for the `LLVM-IR` for that HIR. +This drives the generation of optimized +`MIR`, which drives the borrow checker, which drives the generation of `MIR`, and so on. -... except that this is very over-simplified. In fact, some queries are not +... except that this is very over-simplified. +In fact, some queries are not cached on disk, and some parts of the compiler have to run for all code anyway for correctness even if the code is dead code (e.g. the borrow checker). For example, [currently the `mir_borrowck` query is first executed on all functions of a crate.][passes] Then the codegen backend invokes the `collect_and_partition_mono_items` query, which first recursively requests the `optimized_mir` for all reachable functions, which in turn runs `mir_borrowck` -for that function and then creates codegen units. This kind of split will need +for that function and then creates codegen units. +This kind of split will need to remain to ensure that unreachable functions still have their errors emitted. [passes]: https://github.com/rust-lang/rust/blob/e69c7306e2be08939d95f14229e3f96566fb206c/compiler/rustc_interface/src/passes.rs#L791 Moreover, the compiler wasn't originally built to use a query system; the query -system has been retrofitted into the compiler, so parts of it are not query-fied -yet. Also, LLVM isn't our code, so that isn't querified either. The plan is to -eventually query-fy all of the steps listed in the previous section, +system has been retrofitted into the compiler, so parts of it are not query-fied yet. +Also, LLVM isn't our code, so that isn't querified either. +The plan is to eventually query-fy all of the steps listed in the previous section, but as of November 2022, only the steps between `HIR` and -`LLVM-IR` are query-fied. That is, lexing, parsing, name resolution, and macro +`LLVM-IR` are query-fied. +That is, lexing, parsing, name resolution, and macro expansion are done all at once for the whole program. One other thing to mention here is the all-important "typing context", [`TyCtxt`], which is a giant struct that is at the center of all things. -(Note that the name is mostly historic. This is _not_ a "typing context" in the -sense of `Γ` or `Δ` from type theory. The name is retained because that's what -the name of the struct is in the source code.) All +(Note that the name is mostly historic. +This is _not_ a "typing context" in the sense of `Γ` or `Δ` from type theory. +The name is retained because that's what the name of the struct is in the source code.) All queries are defined as methods on the [`TyCtxt`] type, and the in-memory query -cache is stored there too. In the code, there is usually a variable called -`tcx` which is a handle on the typing context. You will also see lifetimes with +cache is stored there too. +In the code, there is usually a variable called `tcx` which is a handle on the typing context. +You will also see lifetimes with the name `'tcx`, which means that something is tied to the lifetime of the [`TyCtxt`] (usually it is stored or interned there). @@ -327,9 +338,10 @@ For more information about queries in the compiler, see [the queries chapter][qu ### `ty::Ty` -Types are really important in Rust, and they form the core of a lot of compiler -analyses. The main type (in the compiler) that represents types (in the user's -program) is [`rustc_middle::ty::Ty`][ty]. This is so important that we have a whole chapter +Types are really important in Rust, and they form the core of a lot of compiler analyses. +The main type (in the compiler) that represents types (in the user's +program) is [`rustc_middle::ty::Ty`][ty]. +This is so important that we have a whole chapter on [`ty::Ty`][ty], but for now, we just want to mention that it exists and is the way `rustc` represents types! @@ -340,20 +352,21 @@ Also note that the [`rustc_middle::ty`] module defines the [`TyCtxt`] struct we ### Parallelism -Compiler performance is a problem that we would like to improve on -(and are always working on). One aspect of that is parallelizing -`rustc` itself. +Compiler performance is a problem that we would like to improve on (and are always working on). +One aspect of that is parallelizing `rustc` itself. Currently, there is only one part of rustc that is parallel by default: [code generation](./parallel-rustc.md#Codegen). -However, the rest of the compiler is still not yet parallel. There have been -lots of efforts spent on this, but it is generally a hard problem. The current -approach is to turn [`RefCell`]s into [`Mutex`]s -- that is, we -switch to thread-safe internal mutability. However, there are ongoing +However, the rest of the compiler is still not yet parallel. +There have been lots of efforts spent on this, but it is generally a hard problem. +The current approach is to turn [`RefCell`]s into [`Mutex`]s -- that is, we +switch to thread-safe internal mutability. +However, there are ongoing challenges with lock contention, maintaining query-system invariants under -concurrency, and the complexity of the code base. One can try out the current -work by enabling parallel compilation in `bootstrap.toml`. It's still early days, +concurrency, and the complexity of the code base. +One can try out the current work by enabling parallel compilation in `bootstrap.toml`. +It's still early days, but there are already some promising performance improvements. [`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html @@ -361,15 +374,15 @@ but there are already some promising performance improvements. ### Bootstrapping -`rustc` itself is written in Rust. So how do we compile the compiler? We use an -older compiler to compile the newer compiler. This is called [_bootstrapping_]. +`rustc` itself is written in Rust. +So how do we compile the compiler? We use an older compiler to compile the newer compiler. +This is called [_bootstrapping_]. -Bootstrapping has a lot of interesting implications. For example, it means -that one of the major users of Rust is the Rust compiler, so we are +Bootstrapping has a lot of interesting implications. +For example, it means that one of the major users of Rust is the Rust compiler, so we are constantly testing our own software ("eating our own dogfood"). -For more details on bootstrapping, see -[the bootstrapping section of the guide][rustc-bootstrap]. +For more details on bootstrapping, see [the bootstrapping section of the guide][rustc-bootstrap]. [_bootstrapping_]: https://en.wikipedia.org/wiki/Bootstrapping_(compilers) [rustc-bootstrap]: building/bootstrapping/intro.md @@ -382,8 +395,7 @@ For more details on bootstrapping, see parser, HIR, etc)? - e.g., `cargo rustc -- -Z unpretty=hir-tree` allows you to view `HIR` representation - What is the main source entry point for `X`? -- Where do phases diverge for cross-compilation to machine code across - different platforms? +- Where do phases diverge for cross-compilation to machine code across different platforms? --> # References @@ -406,7 +418,7 @@ For more details on bootstrapping, see - [Entry point for outline module parsing](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/module/fn.parse_external_mod.html) - [Entry point for macro fragments][parse_nonterminal] - `AST` definition: [`rustc_ast`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html) - - Feature gating: **TODO** + - Feature gating: [Feature Gate Checking](feature-gate-check.md) - Early linting: **TODO** - The High Level Intermediate Representation (HIR) - Guide: [The HIR](hir.md) @@ -439,6 +451,6 @@ For more details on bootstrapping, see - Guide: [Code Generation](backend/codegen.md) - Generating Machine Code from `LLVM-IR` with LLVM - **TODO: reference?** - Main entry point: [`rustc_codegen_ssa::base::codegen_crate`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/base/fn.codegen_crate.html) - - This monomorphizes and produces `LLVM-IR` for one codegen unit. It then - starts a background thread to run LLVM, which must be joined later. + - This monomorphizes and produces `LLVM-IR` for one codegen unit. + It then starts a background thread to run LLVM, which must be joined later. - Monomorphization happens lazily via [`FunctionCx::monomorphize`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/struct.FunctionCx.html#method.monomorphize) and [`rustc_codegen_ssa::base::codegen_instance `](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/base/fn.codegen_instance.html) diff --git a/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md b/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md index 46e38832e64d..988284620d5c 100644 --- a/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md +++ b/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md @@ -1,7 +1,8 @@ # Incremental compilation in detail The incremental compilation scheme is, in essence, a surprisingly -simple extension to the overall query system. It relies on the fact that: +simple extension to the overall query system. +It relies on the fact that: 1. queries are pure functions -- given the same inputs, a query will always yield the same result, and @@ -14,8 +15,8 @@ incremental and then goes on to discuss version implementation issues. ## A Basic Algorithm For Incremental Query Evaluation As explained in the [query evaluation model primer][query-model], query -invocations form a directed-acyclic graph. Here's the example from the -previous chapter again: +invocations form a directed-acyclic graph. +Here's the example from the previous chapter again: ```ignore list_of_all_hir_items <----------------------------- type_check_crate() @@ -30,38 +31,38 @@ previous chapter again: ``` Since every access from one query to another has to go through the query -context, we can record these accesses and thus actually build this dependency -graph in memory. With dependency tracking enabled, when compilation is done, +context, we can record these accesses and thus actually build this dependency graph in memory. +With dependency tracking enabled, when compilation is done, we know which queries were invoked (the nodes of the graph) and for each invocation, which other queries or input has gone into computing the query's result (the edges of the graph). Now suppose we change the source code of our program so that -HIR of `bar` looks different than before. Our goal is to only recompute -those queries that are actually affected by the change while re-using -the cached results of all the other queries. Given the dependency graph we can -do exactly that. For a given query invocation, the graph tells us exactly +HIR of `bar` looks different than before. +Our goal is to only recompute those queries that are actually affected by the change while re-using +the cached results of all the other queries. +Given the dependency graph we can do exactly that. +For a given query invocation, the graph tells us exactly what data has gone into computing its results, we just have to follow the -edges until we reach something that has changed. If we don't encounter -anything that has changed, we know that the query still would evaluate to +edges until we reach something that has changed. +If we don't encounter anything that has changed, we know that the query still would evaluate to the same result we already have in our cache. Taking the `type_of(foo)` invocation from above as an example, we can check -whether the cached result is still valid by following the edges to its -inputs. The only edge leads to `Hir(foo)`, an input that has not been affected -by the change. So we know that the cached result for `type_of(foo)` is still -valid. +whether the cached result is still valid by following the edges to its inputs. +The only edge leads to `Hir(foo)`, an input that has not been affected by the change. +So we know that the cached result for `type_of(foo)` is still valid. The story is a bit different for `type_check_item(foo)`: We again walk the -edges and already know that `type_of(foo)` is fine. Then we get to -`type_of(bar)` which we have not checked yet, so we walk the edges of -`type_of(bar)` and encounter `Hir(bar)` which *has* changed. Consequently -the result of `type_of(bar)` might yield a different result than what we -have in the cache and, transitively, the result of `type_check_item(foo)` -might have changed too. We thus re-run `type_check_item(foo)`, which in +edges and already know that `type_of(foo)` is fine. +Then we get to `type_of(bar)` which we have not checked yet, so we walk the edges of +`type_of(bar)` and encounter `Hir(bar)` which *has* changed. +Consequently the result of `type_of(bar)` might yield a different result than what we +have in the cache and, transitively, the result of `type_check_item(foo)` might have changed too. +We thus re-run `type_check_item(foo)`, which in turn will re-run `type_of(bar)`, which will yield an up-to-date result -because it reads the up-to-date version of `Hir(bar)`. Also, we re-run -`type_check_item(bar)` because result of `type_of(bar)` might have changed. +because it reads the up-to-date version of `Hir(bar)`. +Also, we re-run `type_check_item(bar)` because result of `type_of(bar)` might have changed. ## The problem with the basic algorithm: false positives @@ -69,8 +70,8 @@ because it reads the up-to-date version of `Hir(bar)`. Also, we re-run If you read the previous paragraph carefully you'll notice that it says that `type_of(bar)` *might* have changed because one of its inputs has changed. There's also the possibility that it might still yield exactly the same -result *even though* its input has changed. Consider an example with a -simple query that just computes the sign of an integer: +result *even though* its input has changed. +Consider an example with a simple query that just computes the sign of an integer: ```ignore IntValue(x) <---- sign_of(x) <--- some_other_query(x) @@ -81,23 +82,22 @@ Even though `IntValue(x)` is different in the two cases, `sign_of(x)` yields the result `+` in both cases. If we follow the basic algorithm, however, `some_other_query(x)` would have to -(unnecessarily) be re-evaluated because it transitively depends on a changed -input. Change detection yields a "false positive" in this case because it has -to conservatively assume that `some_other_query(x)` might be affected by that -changed input. +(unnecessarily) be re-evaluated because it transitively depends on a changed input. +Change detection yields a "false positive" in this case because it has +to conservatively assume that `some_other_query(x)` might be affected by that changed input. Unfortunately it turns out that the actual queries in the compiler are full of examples like this and small changes to the input often potentially affect -very large parts of the output binaries. As a consequence, we had to make the -change detection system smarter and more accurate. +very large parts of the output binaries. +As a consequence, we had to make the change detection system smarter and more accurate. ## Improving accuracy: the red-green algorithm The "false positives" problem can be solved by interleaving change detection -and query re-evaluation. Instead of walking the graph all the way to the +and query re-evaluation. +Instead of walking the graph all the way to the inputs when trying to find out if some cached result is still valid, we can -check if a result has *actually* changed after we were forced to re-evaluate -it. +check if a result has *actually* changed after we were forced to re-evaluate it. We call this algorithm the red-green algorithm because nodes in the dependency graph are assigned the color green if we were able to prove @@ -178,13 +178,15 @@ fn try_mark_green(tcx, current_node) -> bool { > NOTE: > The actual implementation can be found in -> [`compiler/rustc_query_system/src/dep_graph/graph.rs`][try_mark_green] +> [`compiler/rustc_middle/src/dep_graph/graph.rs`][try_mark_green] By using red-green marking we can avoid the devastating cumulative effect of -having false positives during change detection. Whenever a query is executed -in incremental mode, we first check if its already green. If not, we run -`try_mark_green()` on it. If it still isn't green after that, then we actually -invoke the query provider to re-compute the result. Re-computing the query might +having false positives during change detection. +Whenever a query is executed in incremental mode, we first check if its already green. +If not, we run `try_mark_green()` on it. +If it still isn't green after that, then we actually +invoke the query provider to re-compute the result. +Re-computing the query might then itself involve recursively invoking more queries, which can mean we come back to the `try_mark_green()` algorithm for the dependencies recursively. @@ -200,7 +202,8 @@ This comes with a whole new set of implementation challenges: - The query result cache is stored to disk, so they are not readily available for change comparison. - A subsequent compilation session will start off with new version of the code - that has arbitrary changes applied to it. All kinds of IDs and indices that + that has arbitrary changes applied to it. + All kinds of IDs and indices that are generated from a global, sequential counter (e.g. `NodeId`, `DefId`, etc) might have shifted, making the persisted results on disk not immediately usable anymore because the same numeric IDs and indices might refer to @@ -215,41 +218,46 @@ The following sections describe how the compiler solves these issues. ### A Question Of Stability: Bridging The Gap Between Compilation Sessions As noted before, various IDs (like `DefId`) are generated by the compiler in a -way that depends on the contents of the source code being compiled. ID assignment -is usually deterministic, that is, if the exact same code is compiled twice, -the same things will end up with the same IDs. However, if something +way that depends on the contents of the source code being compiled. +ID assignment is usually deterministic. +That is, if the exact same code is compiled twice, +the same things will end up with the same IDs. +However, if something changes, e.g. a function is added in the middle of a file, there is no guarantee that anything will have the same ID as it had before. As a consequence we cannot represent the data in our on-disk cache the same -way it is represented in memory. For example, if we just stored a piece +way it is represented in memory. +For example, if we just stored a piece of type information like `TyKind::FnDef(DefId, &'tcx Substs<'tcx>)` (as we do in memory) and then the contained `DefId` points to a different function in a new compilation session we'd be in trouble. The solution to this problem is to find "stable" forms for IDs which remain -valid in between compilation sessions. For the most important case, `DefId`s, -these are the so-called `DefPath`s. Each `DefId` has a -corresponding `DefPath` but in place of a numeric ID, a `DefPath` is based on +valid in between compilation sessions. +For the most important case, `DefId`s, +these are the so-called `DefPath`s. +Each `DefId` has a corresponding `DefPath`, but in place of a numeric ID, a `DefPath` is based on the path to the identified item, e.g. `std::collections::HashMap`. The advantage of an ID like this is that it is not affected by unrelated changes. For example, one can add a new function to `std::collections` but -`std::collections::HashMap` would still be `std::collections::HashMap`. A -`DefPath` is "stable" across changes made to the source code while a `DefId` -isn't. +`std::collections::HashMap` would still be `std::collections::HashMap`. +A `DefPath` is "stable" across changes made to the source code while a `DefId` isn't. -There is also the `DefPathHash` which is just a 128-bit hash value of the -`DefPath`. The two contain the same information and we mostly use the +There is also the `DefPathHash` which is just a 128-bit hash value of the `DefPath`. +The two contain the same information and we mostly use the `DefPathHash` because it simpler to handle, being `Copy` and self-contained. This principle of stable identifiers is used to make the data in the on-disk -cache resilient to source code changes. Instead of storing a `DefId`, we store +cache resilient to source code changes. +Instead of storing a `DefId`, we store the `DefPathHash` and when we deserialize something from the cache, we map the `DefPathHash` to the corresponding `DefId` in the *current* compilation session (which is just a simple hash table lookup). The `HirId`, used for identifying HIR components that don't have their own -`DefId`, is another such stable ID. It is (conceptually) a pair of a `DefPath` +`DefId`, is another such stable ID. +It is (conceptually) a pair of a `DefPath` and a `LocalId`, where the `LocalId` identifies something (e.g. a `hir::Expr`) locally within its "owner" (e.g. a `hir::Item`). If the owner is moved around, the `LocalId`s within it are still the same. @@ -259,30 +267,31 @@ the `LocalId`s within it are still the same. ### Checking query results for changes: `HashStable` and `Fingerprint`s In order to do red-green-marking we often need to check if the result of a -query has changed compared to the result it had during the previous -compilation session. There are two performance problems with this though: +query has changed compared to the result it had during the previous compilation session. +There are two performance problems with this though: -- We'd like to avoid having to load the previous result from disk just for - doing the comparison. We already computed the new result and will use that. +- We'd like to avoid having to load the previous result from disk just for doing the comparison. + We already computed the new result and will use that. Also loading a result from disk will "pollute" the interners with data that is unlikely to ever be used. -- We don't want to store each and every result in the on-disk cache. For - example, it would be wasted effort to persist things to disk that are +- We don't want to store each and every result in the on-disk cache. + For example, it would be wasted effort to persist things to disk that are already available in upstream crates. -The compiler avoids these problems by using so-called `Fingerprint`s. Each time -a new query result is computed, the query engine will compute a 128 bit hash -value of the result. We call this hash value "the `Fingerprint` of the query -result". The hashing is (and has to be) done "in a stable way". This means -that whenever something is hashed that might change in between compilation +The compiler avoids these problems by using so-called `Fingerprint`s. +Each time a new query result is computed, the query engine will compute a 128 bit hash +value of the result. +We call this hash value "the `Fingerprint` of the query result". +The hashing is (and has to be) done "in a stable way". +This means that whenever something is hashed that might change in between compilation sessions (e.g. a `DefId`), we instead hash its stable equivalent (e.g. the corresponding `DefPath`). That's what the whole `HashStable` -infrastructure is for. This way `Fingerprint`s computed in two -different compilation sessions are still comparable. +infrastructure is for. +This way `Fingerprint`s computed in two different compilation sessions are still comparable. The next step is to store these fingerprints along with the dependency graph. -This is cheap since fingerprints are just bytes to be copied. It's also cheap to -load the entire set of fingerprints together with the dependency graph. +This is cheap since fingerprints are just bytes to be copied. +It's also cheap to load the entire set of fingerprints together with the dependency graph. Now, when red-green-marking reaches the point where it needs to check if a result has changed, it can just compare the (already loaded) previous @@ -290,17 +299,17 @@ fingerprint to the fingerprint of the new result. This approach works rather well but it's not without flaws: -- There is a small possibility of hash collisions. That is, two different - results could have the same fingerprint and the system would erroneously +- There is a small possibility of hash collisions. + That is, two different results could have the same fingerprint and the system would erroneously assume that the result hasn't changed, leading to a missed update. - We mitigate this risk by using a high-quality hash function and a 128 bit - wide hash value. Due to these measures the practical risk of a hash - collision is negligible. + We mitigate this risk by using a high-quality hash function and a 128 bit wide hash value. + Due to these measures the practical risk of a hash collision is negligible. -- Computing fingerprints is quite costly. It is the main reason why incremental - compilation can be slower than non-incremental compilation. We are forced to - use a good and thus expensive hash function, and we have to map things to +- Computing fingerprints is quite costly. + It is the main reason why incremental + compilation can be slower than non-incremental compilation. + We are forced to use a good and thus expensive hash function, and we have to map things to their stable equivalents while doing the hashing. @@ -313,32 +322,36 @@ dependency graphs: The one we built during the previous compilation session and the one that we are building for the current compilation session. When a compilation session starts, the compiler loads the previous dependency -graph into memory as an immutable piece of data. Then, when a query is invoked, -it will first try to mark the corresponding node in the graph as green. This -means really that we are trying to mark the node in the *previous* dep-graph -as green that corresponds to the query key in the *current* session. How do we -do this mapping between current query key and previous `DepNode`? The answer -is again `Fingerprint`s: Nodes in the dependency graph are identified by a -fingerprint of the query key. Since fingerprints are stable across compilation +graph into memory as an immutable piece of data. +Then, when a query is invoked, +it will first try to mark the corresponding node in the graph as green. +This means really that we are trying to mark the node in the *previous* dep-graph +as green that corresponds to the query key in the *current* session. +How do we do this mapping between current query key and previous `DepNode`? +The answer is again `Fingerprint`s: Nodes in the dependency graph are identified by a +fingerprint of the query key. +Since fingerprints are stable across compilation sessions, computing one in the current session allows us to find a node -in the dependency graph from the previous session. If we don't find a node with +in the dependency graph from the previous session. +If we don't find a node with the given fingerprint, it means that the query key refers to something that did not yet exist in the previous session. So, having found the dep-node in the previous dependency graph, we can look up its dependencies (i.e. also dep-nodes in the previous graph) and continue with -the rest of the try-mark-green algorithm. The next interesting thing happens -when we successfully marked the node as green. At that point we copy the node -and the edges to its dependencies from the old graph into the new graph. We -have to do this because the new dep-graph cannot acquire the -node and edges via the regular dependency tracking. The tracking system can -only record edges while actually running a query -- but running the query, +the rest of the try-mark-green algorithm. +The next interesting thing happens when we successfully marked the node as green. +At that point we copy the node +and the edges to its dependencies from the old graph into the new graph. +We have to do this because the new dep-graph cannot acquire the +node and edges via the regular dependency tracking. +The tracking system can only record edges while actually running a query -- but running the query, although we have the result already cached, is exactly what we want to avoid. Once the compilation session has finished, all the unchanged parts have been copied over from the old into the new dependency graph, while the changed parts -have been added to the new graph by the tracking system. At this point, the -new graph is serialized out to disk, alongside the query result cache, and can +have been added to the new graph by the tracking system. +At this point, the new graph is serialized out to disk, alongside the query result cache, and can act as the previous dep-graph in a subsequent compilation session. @@ -346,18 +359,18 @@ act as the previous dep-graph in a subsequent compilation session. The system described so far has a somewhat subtle property: If all inputs of a dep-node are green then the dep-node itself can be marked as green without -computing or loading the corresponding query result. Applying this property -transitively often leads to the situation that some intermediate results are +computing or loading the corresponding query result. +Applying this property transitively often leads to the situation that some intermediate results are never actually loaded from disk, as in the following example: ```ignore input(A) <-- intermediate_query(B) <-- leaf_query(C) ``` -The compiler might need the value of `leaf_query(C)` in order to generate some -output artifact. If it can mark `leaf_query(C)` as green, it will load the -result from the on-disk cache. The result of `intermediate_query(B)` is never -loaded though. As a consequence, when the compiler persists the *new* result +The compiler might need the value of `leaf_query(C)` in order to generate some output artifact. +If it can mark `leaf_query(C)` as green, it will load the result from the on-disk cache. +The result of `intermediate_query(B)` is never loaded though. +As a consequence, when the compiler persists the *new* result cache by writing all in-memory query results to disk, `intermediate_query(B)` will not be in memory and thus will be missing from the new result cache. @@ -367,25 +380,25 @@ had a perfectly valid result for it in the cache just before. In order to prevent this from happening, the compiler does something called "cache promotion": Before emitting the new result cache it will walk all green -dep-nodes and make sure that their query result is loaded into memory. That way -the result cache doesn't unnecessarily shrink again. +dep-nodes and make sure that their query result is loaded into memory. +That way the result cache doesn't unnecessarily shrink again. # Incremental compilation and the compiler backend The compiler backend, the part involving LLVM, is using the query system but -it is not implemented in terms of queries itself. As a consequence it does not -automatically partake in dependency tracking. However, the manual integration -with the tracking system is pretty straight-forward. The compiler simply tracks -what queries get invoked when generating the initial LLVM version of each -codegen unit (CGU), which results in a dep-node for each CGU. In subsequent -compilation sessions it then tries to mark the dep-node for a CGU as green. If -it succeeds, it knows that the corresponding object and bitcode files on disk -are still valid. If it doesn't succeed, the entire CGU has to be recompiled. +it is not implemented in terms of queries itself. +As a consequence it does not automatically partake in dependency tracking. +However, the manual integration with the tracking system is pretty straight-forward. +The compiler simply tracks what queries get invoked when generating the initial LLVM version of each +codegen unit (CGU), which results in a dep-node for each CGU. +In subsequent compilation sessions it then tries to mark the dep-node for a CGU as green. +If it succeeds, it knows that the corresponding object and bitcode files on disk are still valid. +If it doesn't succeed, the entire CGU has to be recompiled. -This is the same approach that is used for regular queries. The main differences -are: +This is the same approach that is used for regular queries. +The main differences are: - that we cannot easily compute a fingerprint for LLVM modules (because they are opaque C++ objects), @@ -399,39 +412,41 @@ are: executed when and what stays in memory for how long. The query system could probably be extended with general purpose mechanisms to -deal with all of the above but so far that seemed like more trouble than it -would save. +deal with all of the above but so far that seemed like more trouble than it would save. ## Query modifiers -The query system allows for applying [modifiers][mod] to queries. These -modifiers affect certain aspects of how the system treats the query with +The query system allows for applying [modifiers][mod] to queries. +These modifiers affect certain aspects of how the system treats the query with respect to incremental compilation: - `eval_always` - A query with the `eval_always` attribute is re-executed - unconditionally during incremental compilation. I.e. the system will not - even try to mark the query's dep-node as green. This attribute has two use - cases: + unconditionally during incremental compilation. + I.e. + the system will not even try to mark the query's dep-node as green. + This attribute has two use cases: - `eval_always` queries can read inputs (from files, global state, etc). They can also produce side effects like writing to files and changing global state. - Some queries are very likely to be re-evaluated because their result - depends on the entire source code. In this case `eval_always` can be used + depends on the entire source code. + In this case `eval_always` can be used as an optimization because the system can skip recording dependencies in the first place. - `no_hash` - Applying `no_hash` to a query tells the system to not compute - the fingerprint of the query's result. This has two consequences: + the fingerprint of the query's result. + This has two consequences: - Not computing the fingerprint can save quite a bit of time because fingerprinting is expensive, especially for large, complex values. - Without the fingerprint, the system has to unconditionally assume that - the result of the query has changed. As a consequence anything depending - on a `no_hash` query will always be re-executed. + the result of the query has changed. + As a consequence, anything depending on a `no_hash` query will always be re-executed. Using `no_hash` for a query can make sense in two circumstances: @@ -446,24 +461,24 @@ respect to incremental compilation: and there are "projection queries" reading from that collection (e.g. `hir_owner`). In such a case the big collection will likely fulfill the condition above (any changed input means recomputing the whole collection) - and the results of the projection queries will be hashed anyway. If we also - hashed the collection query it would mean that we effectively hash the same + and the results of the projection queries will be hashed anyway. + If we also hashed the collection query, it would mean that we effectively hash the same data twice: once when hashing the collection and another time when hashing all - the projection query results. `no_hash` allows us to avoid that redundancy + the projection query results. + `no_hash` allows us to avoid that redundancy and the projection queries act as a "firewall", shielding their dependents from the unconditionally red `no_hash` node. - `cache_on_disk_if` - This attribute is what determines which query results - are persisted in the incremental compilation query result cache. The - attribute takes an expression that allows per query invocation - decisions. For example, it makes no sense to store values from upstream - crates in the cache because they are already available in the upstream - crate's metadata. + are persisted in the incremental compilation query result cache. + The attribute takes an expression that allows per query invocation decisions. + For example, it makes no sense to store values from upstream + crates in the cache because they are already available in the upstream crate's metadata. - - `anon` - This attribute makes the system use "anonymous" dep-nodes for the - given query. An anonymous dep-node is not identified by the corresponding - query key, instead its ID is computed from the IDs of its dependencies. This - allows the red-green system to do its change detection even if there is no + - `anon` - This attribute makes the system use "anonymous" dep-nodes for the given query. + An anonymous dep-node is not identified by the corresponding query key. + Instead, its ID is computed from the IDs of its dependencies. + This allows the red-green system to do its change detection even if there is no query key available for a given dep-node -- something which is needed for handling trait selection because it is not based on queries. @@ -473,7 +488,8 @@ respect to incremental compilation: ## The projection query pattern It's interesting to note that `eval_always` and `no_hash` can be used together -in the so-called "projection query" pattern. It is often the case that there is +in the so-called "projection query" pattern. +It is often the case that there is one query that depends on the entirety of the compiler's input (e.g. the indexed HIR) and another query that projects individual values out of this monolithic value (e.g. a HIR item with a certain `DefId`). These projection queries allow for @@ -500,18 +516,18 @@ can still mostly be marked as green. Let's assume that the result `monolithic_query` changes so that also the result of `projection(x)` has changed, i.e. both their dep-nodes are being marked as -red. As a consequence `foo(a)` needs to be re-executed; but `bar(b)` and -`baz(c)` can be marked as green. However, if `foo`, `bar`, and `baz` would have -directly depended on `monolithic_query` then all of them would have had to be -re-evaluated. +red. +As a consequence, `foo(a)` needs to be re-executed; but `bar(b)` and `baz(c)` can be marked as green. +However, if `foo`, `bar`, and `baz` would have +directly depended on `monolithic_query` then all of them would have had to be re-evaluated. This pattern works even without `eval_always` and `no_hash` but the two -modifiers can be used to avoid unnecessary overhead. If the monolithic query +modifiers can be used to avoid unnecessary overhead. +If the monolithic query is likely to change at any minor modification of the compiler's input it makes -sense to mark it as `eval_always`, thus getting rid of its dependency tracking -cost. And it always makes sense to mark the monolithic query as `no_hash` -because we have the projections to take care of keeping things green as much -as possible. +sense to mark it as `eval_always`, thus getting rid of its dependency tracking cost. +And it always makes sense to mark the monolithic query as `no_hash` +because we have the projections to take care of keeping things green as much as possible. # Shortcomings of the current system @@ -520,18 +536,18 @@ There are many things that still can be improved. ## Incrementality of on-disk data structures -The current system is not able to update on-disk caches and the dependency graph -in-place. Instead it has to rewrite each file entirely in each compilation -session. The overhead of doing so is a few percent of total compilation time. +The current system is not able to update on-disk caches and the dependency graph in-place. +Instead, it has to rewrite each file entirely in each compilation session. +The overhead of doing so is a few percent of total compilation time. ## Unnecessary data dependencies Data structures used as query results could be factored in a way that removes -edges from the dependency graph. Especially "span" information is very volatile, -so including it in query result will increase the chance that the result won't -be reusable. See for more -information. +edges from the dependency graph. +Especially "span" information is very volatile, +so including it in query result will increase the chance that the result won't be reusable. +See for more information. [query-model]: ./query-evaluation-model-in-detail.html -[try_mark_green]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_query_system/dep_graph/graph.rs.html +[try_mark_green]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_middle/dep_graph/graph.rs.html diff --git a/src/doc/rustc-dev-guide/src/rustdoc.md b/src/doc/rustc-dev-guide/src/rustdoc.md index 47b18f4e7e52..1259ed10c09b 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc.md +++ b/src/doc/rustc-dev-guide/src/rustdoc.md @@ -37,28 +37,25 @@ Note that literally all that does is call the `main()` that's in this crate's `l ## Cheat sheet -* Run `./x setup tools` before getting started. This will configure `x` - with nice settings for developing rustdoc and other tools, including +* Run `./x setup tools` before getting started. + This will configure `x` with nice settings for developing rustdoc and other tools, including downloading a copy of rustc rather than building it. * Use `./x check rustdoc` to quickly check for compile errors. -* Use `./x build library rustdoc` to make a usable - rustdoc you can run on other projects. +* Use `./x build library rustdoc` to make a usable rustdoc you can run on other projects. * Add `library/test` to be able to use `rustdoc --test`. * Run `rustup toolchain link stage2 build/host/stage2` to add a custom toolchain called `stage2` to your rustup environment. After running that, `cargo +stage2 doc` in any directory will build with your locally-compiled rustdoc. -* Use `./x doc library` to use this rustdoc to generate the - standard library docs. +* Use `./x doc library` to use this rustdoc to generate the standard library docs. * The completed docs will be available in `build/host/doc` (under `core`, `alloc`, and `std`). * If you want to copy those docs to a webserver, copy all of `build/host/doc`, since that's where the CSS, JS, fonts, and landing page are. * For frontend debugging, disable the `rust.docs-minification` option in [`bootstrap.toml`]. -* Use `./x test tests/rustdoc*` to run the tests using a stage1 - rustdoc. +* Use `./x test tests/rustdoc*` to run the tests using a stage1 rustdoc. * See [Rustdoc internals] for more information about tests. -* Use `./x.py test tidy --extra-checks=js` to run rustdoc’s JavaScript checks (`eslint`, `es-check`, and `tsc`). -> **Note:** `./x.py test tidy` already runs these checks automatically when JS/TS sources changed; `--extra-checks=js` forces them explicitly. +* Use `./x test tidy --extra-checks=js` to run rustdoc’s JavaScript checks (`eslint`, `es-check`, and `tsc`). +> **Note:** `./x test tidy` already runs these checks automatically when JS/TS sources changed; `--extra-checks=js` forces them explicitly. ### JavaScript CI checks @@ -66,7 +63,7 @@ Rustdoc’s JavaScript and TypeScript are checked during CI by `eslint`, `es-che These run as part of the `tidy` job. ```console -./x.py test tidy --extra-checks=js +./x test tidy --extra-checks=js ``` The `--extra-checks=js` flag enables the frontend linting that runs in CI. @@ -82,12 +79,12 @@ All paths in this section are relative to `src/librustdoc/` in the rust-lang/rus * The data types that get rendered by the functions mentioned above are defined in `clean/types.rs`. The functions responsible for creating them from the `HIR` and the `rustc_middle::ty` IR live in `clean/mod.rs`. -* The bits specific to using rustdoc as a test harness are in - `doctest.rs`. +* The bits specific to using rustdoc as a test harness are in `doctest.rs`. * The Markdown renderer is loaded up in `html/markdown.rs`, including functions for extracting doctests from a given block of Markdown. * Frontend CSS and JavaScript are stored in `html/static/`. - * Re. JavaScript, type annotations are written using [TypeScript-flavored JSDoc] + * Re. + JavaScript, type annotations are written using [TypeScript-flavored JSDoc] comments and an external `.d.ts` file. This way, the code itself remains plain, valid JavaScript. We only use `tsc` as a linter. diff --git a/src/doc/rustc-dev-guide/src/syntax-intro.md b/src/doc/rustc-dev-guide/src/syntax-intro.md index a5a8bab14971..7290e9b4f90d 100644 --- a/src/doc/rustc-dev-guide/src/syntax-intro.md +++ b/src/doc/rustc-dev-guide/src/syntax-intro.md @@ -2,7 +2,8 @@ Working directly with source code is very inconvenient and error-prone. Thus, before we do anything else, we convert raw source code into an -[Abstract Syntax Tree (AST)][AST]. It turns out that doing this involves a lot of work, +[Abstract Syntax Tree (AST)][AST]. +It turns out that doing this involves a lot of work, including [lexing, parsing], [macro expansion], [name resolution], conditional compilation, [feature-gate checking], and [validation] of the [AST]. In this chapter, we take a look at all of these steps. diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 959a73a5a9ad..6ca9653c1854 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -785,16 +785,12 @@ only apply to the test as a whole, not to particular revisions. The only directives that are intended to really work when customized to a revision are error patterns and compiler flags. - -The following test suites support revisions: - -- ui -- assembly -- codegen -- coverage -- debuginfo -- rustdoc UI tests -- incremental (these are special in that they inherently cannot be run in parallel) + +> Note that these test suites do not support revisions: +> - `codegen-units` +> - `run-make` +> - `rustdoc-html` +> - `rustdoc-json` ### Ignoring unused revision names diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md index 2ad035bbafbc..151670d08997 100644 --- a/src/doc/rustc-dev-guide/src/tracing.md +++ b/src/doc/rustc-dev-guide/src/tracing.md @@ -12,6 +12,25 @@ To see the logs, you need to set the `RUSTC_LOG` environment variable to your lo The full syntax of the log filters can be found in the [rustdoc of `tracing-subscriber`](https://docs.rs/tracing-subscriber/0.2.24/tracing_subscriber/filter/struct.EnvFilter.html#directives). +## Environment variables + +This is an overview of the environment variables rustc accepts to customize +its tracing output. The definition of these can mostly be found in `compiler/rustc_log/src/lib.rs`. + +| Name | Usage | +| ------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| `RUSTC_LOG` | Tracing filters (see the rest of this page) | +| `RUSTC_LOG_COLOR` | `always`, `never` or `auto`. | +| `RUSTC_LOG_ENTRY_EXIT` | if set and not '0', log span entry/exit. | +| `RUSTC_LOG_THREAD_IDS` | if set and equal to '1', also log thread ids. | +| `RUSTC_LOG_BACKTRACE` | Capture and print a backtrace if a trace is emitted with a target that matches the provided string value. | +| `RUSTC_LOG_LINES` | If `1`, indents log lines based on depth. | +| `RUSTC_LOG_FORMAT_JSON` | If `1`, outputs logs as JSON. The exact parameters can be found in `rustc_log/src/lib.rs` but the format is *UNSTABLE*. | +| `RUSTC_LOG_OUTPUT_TARGET` | If provided, logs go to the provided file name instead of stderr. | + + + + ## Function level filters Lots of functions in rustc are annotated with diff --git a/src/doc/rustc-dev-guide/src/ty-module/binders.md b/src/doc/rustc-dev-guide/src/ty-module/binders.md index 21cf80abc6e3..6bb7085a2e0d 100644 --- a/src/doc/rustc-dev-guide/src/ty-module/binders.md +++ b/src/doc/rustc-dev-guide/src/ty-module/binders.md @@ -1,8 +1,12 @@ # `Binder` and Higher ranked regions -Sometimes we define generic parameters not on an item but as part of a type or a where clause. As an example the type `for<'a> fn(&'a u32)` or the where clause `for<'a> T: Trait<'a>` both introduce a generic lifetime named `'a`. Currently there is no stable syntax for `for` or `for` but on nightly `feature(non_lifetime_binders)` can be used to write where clauses (but not types) using `for`/`for`. +Sometimes, we define generic parameters not on an item but as part of a type or a where clause. +As an example, the type `for<'a> fn(&'a u32)` or the where clause `for<'a> T: Trait<'a>` both introduce a generic lifetime named `'a`. +Currently, there is no stable syntax for `for` or `for`, +but on nightly, `feature(non_lifetime_binders)` can be used to write where clauses (but not types) using `for`/`for`. -The `for` is referred to as a "binder" because it brings new names into scope. In rustc we use the `Binder` type to track where these parameters are introduced and what the parameters are (i.e. how many and whether the parameter is a type/const/region). A type such as `for<'a> fn(&'a u32)` would be +The `for` is referred to as a "binder" because it brings new names into scope. +In rustc we use the `Binder` type to track where these parameters are introduced and what the parameters are (i.e. how many and whether the parameter is a type/const/region). A type such as `for<'a> fn(&'a u32)` would be represented in rustc as: ``` Binder( @@ -11,13 +15,19 @@ Binder( ) ``` -Usages of these parameters is represented by the `RegionKind::Bound` (or `TyKind::Bound`/`ConstKind::Bound` variants). These bound regions/types/consts are composed of two main pieces of data: +Usages of these parameters is represented by the `RegionKind::Bound` (or `TyKind::Bound`/`ConstKind::Bound` variants). +These bound regions/types/consts are composed of two main pieces of data: - A [DebruijnIndex](../appendix/background.md#what-is-a-de-bruijn-index) to specify which binder we are referring to. - A [`BoundVar`] which specifies which of the parameters that the `Binder` introduces we are referring to. -We also sometimes store some extra information for diagnostics reasons via the [`BoundTyKind`]/[`BoundRegionKind`] but this is not important for type equality or more generally the semantics of `Ty`. (omitted from the above example) +We also sometimes store some extra information for diagnostics reasons via the [`BoundTyKind`]/[`BoundRegionKind`], +but this is not important for type equality, or, more generally, the semantics of `Ty`. +(omitted from the above example) -In debug output (and also informally when talking to each other) we tend to write these bound variables in the format of `^DebruijnIndex_BoundVar`. The above example would instead be written as `Binder(fn(&'^0_0), &[BoundVariableKind::Region])`. Sometimes when the `DebruijnIndex` is `0` we just omit it and would write `^0`. +In debug output (and also informally when talking to each other), +we tend to write these bound variables in the format of `^DebruijnIndex_BoundVar`. +The above example would instead be written as `Binder(fn(&'^0_0), &[BoundVariableKind::Region])`. +Sometimes, when the `DebruijnIndex` is `0`, we just omit it and would write `^0`. Another concrete example, this time a mixture of `for<'a>` in a where clause and a type: ``` @@ -35,16 +45,24 @@ Binder( ) ``` -Note how the `'^1_0` refers to the `'a` parameter. We use a `DebruijnIndex` of `1` to refer to the binder one level up from the innermost one, and a var of `0` to refer to the first parameter bound which is `'a`. We also use `'^0` to refer to the `'b` parameter, the `DebruijnIndex` is `0` (referring to the innermost binder) so we omit it, leaving only the boundvar of `0` referring to the first parameter bound which is `'b`. +Note how the `'^1_0` refers to the `'a` parameter. +We use a `DebruijnIndex` of `1` to refer to the binder one level up from the innermost one, and a var of `0` to refer to the first parameter bound which is `'a`. +We also use `'^0` to refer to the `'b` parameter, the `DebruijnIndex` is `0` (referring to the innermost binder) so we omit it, leaving only the boundvar of `0` referring to the first parameter bound which is `'b`. -We did not always explicitly track the set of bound vars introduced by each `Binder`, this caused a number of bugs (read: ICEs [#81193](https://github.com/rust-lang/rust/issues/81193), [#79949](https://github.com/rust-lang/rust/issues/79949), [#83017](https://github.com/rust-lang/rust/issues/83017)). By tracking these explicitly we can assert when constructing higher ranked where clauses/types that there are no escaping bound variables or variables from a different binder. See the following example of an invalid type inside of a binder: +We did not always explicitly track the set of bound vars introduced by each `Binder`, +and this caused a number of bugs (read: ICEs [#81193](https://github.com/rust-lang/rust/issues/81193), [#79949](https://github.com/rust-lang/rust/issues/79949), [#83017](https://github.com/rust-lang/rust/issues/83017)). +By tracking these explicitly, we can assert when constructing higher ranked where clauses/types that there are no escaping bound variables or variables from a different binder. +See the following example of an invalid type inside of a binder: ``` Binder( fn(&'^1_0 &'^1 T/#0), &[BoundVariableKind::Region(...)], ) ``` -This would cause all kinds of issues as the region `'^1_0` refers to a binder at a higher level than the outermost binder i.e. it is an escaping bound var. The `'^1` region (also writeable as `'^0_1`) is also ill formed as the binder it refers to does not introduce a second parameter. Modern day rustc will ICE when constructing this binder due to both of those reasons, in the past we would have simply allowed this to work and then ran into issues in other parts of the codebase. +This would cause all kinds of issues as the region `'^1_0` refers to a binder at a higher level than the outermost binder i.e. it is an escaping bound var. +The `'^1` region (also writeable as `'^0_1`) is also ill formed as the binder it refers to does not introduce a second parameter. +Modern day rustc will ICE when constructing this binder due to both of those reasons. +In the past, we would have simply allowed this to work and then ran into issues in other parts of the codebase. [`Binder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Binder.html [`BoundVar`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.BoundVar.html diff --git a/src/doc/rustc-dev-guide/src/ty-module/instantiating-binders.md b/src/doc/rustc-dev-guide/src/ty-module/instantiating-binders.md index 82c340e87a81..f6fc574a3ba2 100644 --- a/src/doc/rustc-dev-guide/src/ty-module/instantiating-binders.md +++ b/src/doc/rustc-dev-guide/src/ty-module/instantiating-binders.md @@ -1,6 +1,8 @@ # Instantiating `Binder`s -Much like [`EarlyBinder`], when accessing the inside of a [`Binder`] we must first discharge it by replacing the bound vars with some other value. This is for much the same reason as with `EarlyBinder`, types referencing parameters introduced by the `Binder` do not make any sense outside of that binder. See the following erroring example: +Much like [`EarlyBinder`], when accessing the inside of a [`Binder`], we must first discharge it by replacing the bound vars with some other value. +This is for much the same reason as with `EarlyBinder`, types referencing parameters introduced by the `Binder` do not make any sense outside of that binder. +See the following erroring example: ```rust,ignore fn foo<'a>(a: &'a u32) -> &'a u32 { a @@ -15,7 +17,9 @@ fn main() { let references_bound_vars = bar(higher_ranked_fn_ptr); } ``` -In this example we are providing an argument of type `for<'a> fn(&'^0 u32) -> &'^0 u32` to `bar`, we do not want to allow `T` to be inferred to the type `&'^0 u32` as it would be rather nonsensical (and likely unsound if we did not happen to ICE). `main` doesn't know about `'a` so the borrow checker would not be able to handle a borrow with lifetime `'a`. +In this example, we are providing an argument of type `for<'a> fn(&'^0 u32) -> &'^0 u32` to `bar`. +We do not want to allow `T` to be inferred to the type `&'^0 u32` as it would be rather nonsensical (and likely unsound if we did not happen to ICE). +`main` doesn't know about `'a` so the borrow checker would not be able to handle a borrow with lifetime `'a`. Unlike `EarlyBinder` we typically do not instantiate `Binder` with some concrete set of arguments from the user, i.e. `['b, 'static]` as arguments to a `for<'a1, 'a2> fn(&'a1 u32, &'a2 u32)`. Instead we usually instantiate the binder with inference variables or placeholders. @@ -31,27 +35,39 @@ As another example of instantiating with infer vars, given some `for<'a> T: Trai - Equate the goal of `T: Trait<'static>` with the instantiated where clause, inferring `'?0 = 'static` - The goal holds because we were successfully able to unify `T: Trait<'static>` with `T: Trait<'?0>` -Instantiating binders with inference variables can be accomplished by using the [`instantiate_binder_with_fresh_vars`] method on [`InferCtxt`]. Binders should be instantiated with infer vars when we only care about one specific instantiation of the binder, if instead we wish to reason about all possible instantiations of the binder then placeholders should be used instead. +Instantiating binders with inference variables can be accomplished by using the [`instantiate_binder_with_fresh_vars`] method on [`InferCtxt`]. +Binders should be instantiated with infer vars when we only care about one specific instantiation of the binder, if instead we wish to reason about all possible instantiations of the binder then placeholders should be used instead. ## Instantiating with placeholders -Placeholders are very similar to `Ty/ConstKind::Param`/`ReEarlyParam`, they represent some unknown type that is only equal to itself. `Ty`/`Const` and `Region` all have a [`Placeholder`] variant that is comprised of a [`Universe`] and a [`BoundVar`]. +Placeholders are very similar to `Ty/ConstKind::Param`/`ReEarlyParam`, they represent some unknown type that is only equal to itself. +`Ty`/`Const` and `Region` all have a [`Placeholder`] variant that is comprised of a [`Universe`] and a [`BoundVar`]. -The `Universe` tracks which binder the placeholder originated from, and the `BoundVar` tracks which parameter on said binder that this placeholder corresponds to. Equality of placeholders is determined solely by whether the universes are equal and the `BoundVar`s are equal. See the [chapter on Placeholders and Universes][ch_placeholders_universes] for more information. +The `Universe` tracks which binder the placeholder originated from, and the `BoundVar` tracks which parameter on said binder that this placeholder corresponds to. +Equality of placeholders is determined solely by whether the universes are equal and the `BoundVar`s are equal. +See the [chapter on Placeholders and Universes][ch_placeholders_universes] for more information. -When talking with other rustc devs or seeing `Debug` formatted `Ty`/`Const`/`Region`s, `Placeholder` will often be written as `'!UNIVERSE_BOUNDVARS`. For example given some type `for<'a> fn(&'a u32, for<'b> fn(&'b &'a u32))`, after instantiating both binders (assuming the `Universe` in the current `InferCtxt` was `U0` beforehand), the type of `&'b &'a u32` would be represented as `&'!2_0 &!1_0 u32`. +When talking with other rustc devs or seeing `Debug` formatted `Ty`/`Const`/`Region`s, `Placeholder` will often be written as `'!UNIVERSE_BOUNDVARS`. +For example, given some type `for<'a> fn(&'a u32, for<'b> fn(&'b &'a u32))`, +after instantiating both binders (assuming the `Universe` in the current `InferCtxt` was `U0` beforehand), +the type of `&'b &'a u32` would be represented as `&'!2_0 &!1_0 u32`. -When the universe of the placeholder is `0`, it will be entirely omitted from the debug output, i.e. `!0_2` would be printed as `!2`. This rarely happens in practice though as we increase the universe in the `InferCtxt` when instantiating a binder with placeholders so usually the lowest universe placeholders encounterable are ones in `U1`. +When the universe of the placeholder is `0`, it will be entirely omitted from the debug output, i.e. `!0_2` would be printed as `!2`. +This rarely happens in practice though as we increase the universe in the `InferCtxt` when instantiating a binder with placeholders, +so usually the lowest universe placeholders encounterable are ones in `U1`. -`Binder`s can be instantiated with placeholders via the [`enter_forall`] method on `InferCtxt`. It should be used whenever the compiler should care about any possible instantiation of the binder instead of one concrete instantiation. +`Binder`s can be instantiated with placeholders via the [`enter_forall`] method on `InferCtxt`. +It should be used whenever the compiler should care about any possible instantiation of the binder instead of one concrete instantiation. -Note: in the original example of this chapter it was mentioned that we should not infer a local variable to have type `&'^0 u32`. This code is prevented from compiling via universes (as explained in the linked chapter) +Note: in the original example of this chapter it was mentioned that we should not infer a local variable to have type `&'^0 u32`. +This code is prevented from compiling via universes (as explained in the linked chapter) ### Why have both `RePlaceholder` and `ReBound`? You may be wondering why we have both of these variants, afterall the data stored in `Placeholder` is effectively equivalent to that of `ReBound`: something to track which binder, and an index to track which parameter the `Binder` introduced. -The main reason for this is that `Bound` is a more syntactic representation of bound variables whereas `Placeholder` is a more semantic representation. As a concrete example: +The main reason for this is that `Bound` is a more syntactic representation of bound variables whereas `Placeholder` is a more semantic representation. +As a concrete example: ```rust impl<'a> Other<'a> for &'a u32 { } @@ -66,7 +82,10 @@ where { ... } ``` -Given these trait implementations `u32: Bar` should _not_ hold. `&'a u32` only implements `Other<'a>` when the lifetime of the borrow and the lifetime on the trait are equal. However if we only used `ReBound` and did not have placeholders it may be easy to accidentally believe that trait bound does hold. To explain this let's walk through an example of trying to prove `u32: Bar` in a world where rustc did not have placeholders: +Given these trait implementations, `u32: Bar` should _not_ hold. +`&'a u32` only implements `Other<'a>` when the lifetime of the borrow and the lifetime on the trait are equal. +However, if we only used `ReBound` and did not have placeholders, it may be easy to accidentally believe that trait bound does hold. +To explain this, let's walk through an example of trying to prove `u32: Bar` in a world where rustc did not have placeholders: - We start by trying to prove `u32: Bar` - We find the `impl Bar for T` impl, we would wind up instantiating the `EarlyBinder` with `u32` (note: this is not _quite_ accurate as we first instantiate the binder with an inference variable that we then infer to be `u32` but that distinction is not super important here) - There is a where clause `for<'a> &'^0 T: Trait` on the impl, as we instantiated the early binder with `u32` we actually have to prove `for<'a> &'^0 u32: Trait` @@ -83,22 +102,25 @@ While in theory we could make this work it would be quite involved and more comp - When resolving inference variables rewrite any bound variables according to the current binder depth of the infcx - Maybe more (while writing this list items kept getting added so it seems naive to think this is exhaustive) -Fundamentally all of this complexity is because `Bound` ty/const/regions have a different representation for a given parameter on a `Binder` depending on how many other `Binder`s there are between the binder introducing the parameter, and its usage. For example given the following code: +Fundamentally, all of this complexity is because `Bound` ty/const/regions have a different representation for a given parameter on a `Binder` depending on how many other `Binder`s there are between the binder introducing the parameter, and its usage. +For example, given the following code: ```rust fn foo() where for<'a> T: Trait<'a, for<'b> fn(&'b T, &'a u32)> { ... } ``` -That where clause would be written as: -`for<'a> T: Trait<'^0, for<'b> fn(&'^0 T, &'^1_0 u32)>` -Despite there being two references to the `'a` parameter they are both represented differently: `^0` and `^1_0`, due to the fact that the latter usage is nested under a second `Binder` for the inner function pointer type. +That where clause would be written as `for<'a> T: Trait<'^0, for<'b> fn(&'^0 T, &'^1_0 u32)>`. +Despite there being two references to the `'a` parameter, +they are both represented differently, `^0` and `^1_0`, +due to the fact that the latter usage is nested under a second `Binder` for the inner function pointer type. This is in contrast to `Placeholder` ty/const/regions which do not have this limitation due to the fact that `Universe`s are specific to the current `InferCtxt` not the usage site of the parameter. -It is trivially possible to instantiate `EarlyBinder`s and unify inference variables with existing `Placeholder`s as no matter what context the `Placeholder` is in, it will have the same representation. As an example if we were to instantiate the binder on the higher ranked where clause from above, it would be represented like so: -`T: Trait<'!1_0, for<'b> fn(&'^0 T, &'!1_0 u32)>` -the `RePlaceholder` representation for both usages of `'a` are the same despite one being underneath another `Binder`. +It is trivially possible to instantiate `EarlyBinder`s and unify inference variables with existing `Placeholder`s as no matter what context the `Placeholder` is in, it will have the same representation. +As an example, if we were to instantiate the binder on the higher ranked where clause from above, it would be represented like +`T: Trait<'!1_0, for<'b> fn(&'^0 T, &'!1_0 u32)>`. +The `RePlaceholder` representation for both usages of `'a` are the same despite one being underneath another `Binder`. If we were to then instantiate the binder on the function pointer we would get a type such as: `fn(&'!2_0 T, ^'!1_0 u32)` @@ -107,9 +129,12 @@ the `RePlaceholder` for the `'b` parameter is in a higher universe to track the ## Instantiating with `ReLateParam` As discussed in [the chapter about representing types][representing-types], `RegionKind` has two variants for representing generic parameters, `ReLateParam` and `ReEarlyParam`. -`ReLateParam` is conceptually a `Placeholder` that is always in the root universe (`U0`). It is used when instantiating late bound parameters of functions/closures while inside of them. Its actual representation is relatively different from both `ReEarlyParam` and `RePlaceholder`: +`ReLateParam` is conceptually a `Placeholder` that is always in the root universe (`U0`). +It is used when instantiating late bound parameters of functions/closures while inside of them. +Its actual representation is relatively different from both `ReEarlyParam` and `RePlaceholder`: - A `DefId` for the item that introduced the late bound generic parameter -- A [`BoundRegionKind`] which either specifies the `DefId` of the generic parameter and its name (via a `Symbol`), or that this placeholder is representing the anonymous lifetime of a `Fn`/`FnMut` closure's self borrow. There is also a variant for `BrAnon` but this is not used for `ReLateParam`. +- A [`BoundRegionKind`] which either specifies the `DefId` of the generic parameter and its name (via a `Symbol`), or that this placeholder is representing the anonymous lifetime of a `Fn`/`FnMut` closure's self borrow. + There is also a variant for `BrAnon` but this is not used for `ReLateParam`. For example, given the following code: ```rust,ignore @@ -128,11 +153,18 @@ ReLateParam( ) ``` -In this specific case of referencing late bound generic parameters of a function from inside the body this is done implicitly during `hir_ty_lowering` rather than explicitly when instantiating a `Binder` somewhere. In some cases however, we do explicitly instantiate a `Binder` with `ReLateParam`s. +In this specific case of referencing late bound generic parameters of a function from inside the body, +this is done implicitly during `hir_ty_lowering`, +rather than explicitly when instantiating a `Binder` somewhere. +In some cases however, we do explicitly instantiate a `Binder` with `ReLateParam`s. -Generally whenever we have a `Binder` for late bound parameters on a function/closure and we are conceptually inside of the binder already, we use [`liberate_late_bound_regions`] to instantiate it with `ReLateParam`s. That makes this operation the `Binder` equivalent to `EarlyBinder`'s `instantiate_identity`. +Generally, whenever we have a `Binder` for late bound parameters on a function/closure, +and we are conceptually inside of the binder already, +we use [`liberate_late_bound_regions`] to instantiate it with `ReLateParam`s. +That makes this operation the `Binder` equivalent to `EarlyBinder`'s `instantiate_identity`. -As a concrete example, accessing the signature of a function we are type checking will be represented as `EarlyBinder>`. As we are already "inside" of these binders, we would call `instantiate_identity` followed by `liberate_late_bound_regions`. +As a concrete example, accessing the signature of a function we are type checking will be represented as `EarlyBinder>`. +As we are already "inside" of these binders, we would call `instantiate_identity` followed by `liberate_late_bound_regions`. [`liberate_late_bound_regions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.liberate_late_bound_regions [representing-types]: param-ty-const-regions.md diff --git a/src/doc/rustc-dev-guide/src/ty-module/param-ty-const-regions.md b/src/doc/rustc-dev-guide/src/ty-module/param-ty-const-regions.md index b0c793003041..f9963907897c 100644 --- a/src/doc/rustc-dev-guide/src/ty-module/param-ty-const-regions.md +++ b/src/doc/rustc-dev-guide/src/ty-module/param-ty-const-regions.md @@ -1,7 +1,7 @@ # Parameter `Ty`/`Const`/`Region`s -When inside of generic items, types can be written that use in scope generic parameters, for example `fn foo<'a, T>(_: &'a Vec)`. In this specific case -the `&'a Vec` type would be represented internally as: +When inside of generic items, types can be written that use in scope generic parameters, for example `fn foo<'a, T>(_: &'a Vec)`. +In this specific case, the `&'a Vec` type would be represented internally as: ``` TyKind::Ref( RegionKind::LateParam(DefId(foo), DefId(foo::'a), "'a"), @@ -29,8 +29,11 @@ struct Foo(Vec); ``` The `Vec` type is represented as `TyKind::Adt(Vec, &[GenericArgKind::Type(Param("T", 0))])`. -The name is somewhat self explanatory, it's the name of the type parameter. The index of the type parameter is an integer indicating -its order in the list of generic parameters in scope (note: this includes parameters defined on items on outer scopes than the item the parameter is defined on). Consider the following examples: +The name is somewhat self explanatory; it's the name of the type parameter. +The index of the type parameter is an integer indicating +its order in the list of generic parameters in scope. +Note that this includes parameters defined on items on outer scopes than the item the parameter is defined on. +Consider the following examples: ```rust,ignore struct Foo { @@ -49,15 +52,20 @@ impl Foo { } ``` -Concretely given the `ty::Generics` for the item the parameter is defined on, if the index is `2` then starting from the root `parent`, it will be the third parameter to be introduced. For example in the above example, `Z` has index `2` and is the third generic parameter to be introduced, starting from the `impl` block. +Concretely, given the `ty::Generics` for the item the parameter is defined on, +if the index is `2` and if we start from the root `parent`, it will be the third parameter to be introduced. +For example in the above example, `Z` has index `2` and is the third generic parameter to be introduced, starting from the `impl` block. The index fully defines the `Ty` and is the only part of `TyKind::Param` that matters for reasoning about the code we are compiling. -Generally we do not care what the name is and only use the index. The name is included for diagnostics and debug logs as otherwise it would be +Generally, we do not care what the name is, and we only use the index. +The name is included for diagnostics and debug logs as otherwise it would be incredibly difficult to understand the output, i.e. `Vec: Sized` vs `Vec: Sized`. In debug output, parameter types are often printed out as `{name}/#{index}`, for example in the function `foo` if we were to debug print `Vec` it would be written as `Vec`. -An alternative representation would be to only have the name, however using an index is more efficient as it means we can index into `GenericArgs` when instantiating generic parameters with some arguments. We would otherwise have to store `GenericArgs` as a `HashMap` and do a hashmap lookup everytime we used a generic item. +An alternative representation would be to only have the name. +However, using an index is more efficient as it means we can index into `GenericArgs` when instantiating generic parameters with some arguments. +We would otherwise have to store `GenericArgs` as a `HashMap` and do a hashmap lookup everytime we used a generic item. In theory an index would also allow for having multiple distinct parameters that use the same name, e.g. `impl
Foo { fn bar() { .. } }`. @@ -65,9 +73,15 @@ The rules against shadowing make this difficult but those language rules could c ### Lifetime parameters -In contrast to `Ty`/`Const`'s `Param` singular `Param` variant, lifetimes have two variants for representing region parameters: [`RegionKind::EarlyParam`] and [`RegionKind::LateParam`]. The reason for this is due to function's distinguishing between [early and late bound parameters][ch_early_late_bound] which is discussed in an earlier chapter (see link). +In contrast to `Ty`/`Const`'s `Param` singular `Param` variant, lifetimes have two variants for representing region parameters: [`RegionKind::EarlyParam`] and [`RegionKind::LateParam`]. +The reason for this is due to function's distinguishing between [early and late bound parameters][ch_early_late_bound] which is discussed in an earlier chapter (see link). -`RegionKind::EarlyParam` is structured identically to `Ty/Const`'s `Param` variant, it is simply a `u32` index and a `Symbol`. For lifetime parameters defined on non-function items we always use `ReEarlyParam`. For functions we use `ReEarlyParam` for any early bound parameters and `ReLateParam` for any late bound parameters. Note that just like `Ty` and `Const` params we often debug format them as `'SYMBOL/#INDEX`, see for example: +`RegionKind::EarlyParam` is structured identically to `Ty/Const`'s `Param` variant; it is simply a `u32` index and a `Symbol`. +For lifetime parameters defined on non-function items, we always use `ReEarlyParam`. +For functions, we use `ReEarlyParam` for any early bound parameters and `ReLateParam` for any late bound parameters. +Note that, just like `Ty` and `Const` params, we often debug format them as `'SYMBOL/#INDEX`. + +An example: ```rust,ignore // This function would have its signature represented as: diff --git a/src/doc/rustc-dev-guide/src/ty.md b/src/doc/rustc-dev-guide/src/ty.md index c84e82adf5c5..ef2ea37b7ca4 100644 --- a/src/doc/rustc-dev-guide/src/ty.md +++ b/src/doc/rustc-dev-guide/src/ty.md @@ -1,38 +1,40 @@ # The `ty` module: representing types -The `ty` module defines how the Rust compiler represents types internally. It also defines the +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. ## `ty::Ty` -When we talk about how rustc represents types, we usually refer to a type called `Ty`. There are -quite a few modules and types for `Ty` in the compiler ([Ty documentation][ty]). +When we talk about how rustc represents types, we usually refer to a type called `Ty`. +There are quite a few modules and types for `Ty` in the compiler ([Ty documentation][ty]). [ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/index.html The specific `Ty` we are referring to is [`rustc_middle::ty::Ty`][ty_ty] (and not -[`rustc_hir::Ty`][hir_ty]). The distinction is important, so we will discuss it first before going -into the details of `ty::Ty`. +[`rustc_hir::Ty`][hir_ty]). +The distinction is important, so we will discuss it first before going into the details of `ty::Ty`. [ty_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html [hir_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.Ty.html ## `rustc_hir::Ty` vs `ty::Ty` -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 +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. 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 +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. **Example: `fn foo(x: u32) → u32 { x }`** -In this function, we see that `u32` appears twice. We know -that that is the same type, +In this function, we see that `u32` appears twice. +We know that that is the same type, i.e. the function takes an argument and returns an argument of the same type, but from the point of view of the HIR, there would be two distinct type instances because these @@ -43,13 +45,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 +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 -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`. +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 +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 whole program, not a specific usage, unlike `rustc_hir::Ty`. @@ -67,12 +72,15 @@ Here is a summary: **Order** -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 +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 -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 -lowering a `rustc_hir::Ty` to a `ty::Ty` is located. The main routine used is `lower_ty`. +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 +lowering a `rustc_hir::Ty` to a `ty::Ty` is located. +The main routine used is `lower_ty`. This occurs during the type-checking phase, but also in other parts of the compiler that want to ask questions like "what argument types does this function expect?" @@ -80,20 +88,23 @@ questions like "what argument types does this function expect?" **How semantics drive the two instances of `Ty`** -You can think of HIR as the perspective -of the type information that assumes the least. We assume two things are distinct until they are -proven to be the same thing. In other words, we know less about them, so we should assume less about -them. +You can think of HIR as the perspective of the type information that assumes the least. +We assume two things are distinct until they are proven to be the same thing. +In other words, we know less about them, so we should assume less about them. They are syntactically two strings: `"u32"` at line N column 20 and `"u32"` at line N column 35. We -don’t know that they are the same yet. So, in the HIR we treat them as if they are different. Later, +don’t know that they are the same yet. +So, in the HIR we treat them as if they are different. +Later, we determine that they semantically are the same type and that’s the `ty::Ty` we use. -Consider another example: `fn foo(x: T) -> u32`. Suppose that someone invokes `foo::(0)`. +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`. Later, when doing code generation, +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`).) @@ -110,9 +121,11 @@ mod b { } ``` -Here the type `X` will vary depending on context, clearly. If you look at the `rustc_hir::Ty`, +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` +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). ## `ty::Ty` implementation @@ -121,15 +134,15 @@ or `fn(i32) -> i32` (with type aliases fully expanded). [`Interned>`][tykind]. You can ignore `Interned` in general; you will basically never access it explicitly. We always hide them within `Ty` and skip over it via `Deref` impls or methods. -`TyKind` is a big enum -with variants to represent many different Rust types +`TyKind` is a big enum with variants to represent many different Rust types (e.g. primitives, references, algebraic data types, generics, lifetimes, etc). -`WithCachedTypeInfo` has a few cached values like `flags` and `outer_exclusive_binder`. They +`WithCachedTypeInfo` has a few cached values like `flags` and `outer_exclusive_binder`. +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. This allows us to do cheap comparisons for equality, along with the other -benefits of interning. +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. +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 @@ -137,16 +150,16 @@ benefits of interning. To allocate a new type, you can use the various `new_*` methods defined on [`Ty`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html). -These have names -that correspond mostly to the various kinds of types. For example: +These have names that correspond mostly to the various kinds of types. +For example: ```rust,ignore let array_ty = Ty::new_array_with_const_len(tcx, ty, count); ``` These methods all return a `Ty<'tcx>` – note that the lifetime you get back is the lifetime of the -arena that this `tcx` has access to. Types are always canonicalized and interned (so we never -allocate exactly the same type twice). +arena that this `tcx` has access to. +Types are always canonicalized and interned (so we never allocate exactly the same type twice). You can also find various common types in the `tcx` itself by accessing its fields: `tcx.types.bool`, `tcx.types.char`, etc. (See [`CommonTypes`] for more.) @@ -158,13 +171,15 @@ You can also find various common types in the `tcx` itself by accessing its fiel 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, +for duplicates. +This is because often in Rust there are multiple ways to represent the same type, particularly once inference is involved. For example, the type `{integer}` (`ty::Infer(ty::IntVar(..))` an integer inference variable, the type of an integer literal like `0`) and `u8` (`ty::UInt(..)`) should often be treated as equal when testing whether they can be assigned to each other (which is a common operation in -diagnostics code). `==` on them will return `false` though, since they are different types. +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)` @@ -174,32 +189,36 @@ as whether two types can be assigned to each other, not whether they're represen 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. If you are in a function -that has access to an inference context already, this should be the case. Specifically, this is the -case during HIR type checking or MIR borrow checking. +variables inside the types actually belong to that inference context. +If you are in a function that has access to an inference context already, this should be the case. +Specifically, this is the case during HIR type checking or MIR borrow checking. -Another consideration is normalization. Two types may actually be the same, but one is behind an -associated type. 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 +Another consideration is normalization. +Two types may actually be the same, but one is behind an associated type. +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. After type checking, diagnostics code can use -`tcx.normalize_erasing_regions(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 +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 -are resolved and all regions have been erased. In these cases, if you know that 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. 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 +[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`?". +inference variables using [`fresh_args_for_item`]. +This can be used to answer questions like "can `Vec` for any `T` be unified with `Vec`?". [type-inference]: ./type-inference.md#creating-an-inference-context [`fresh_args_for_item`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html#method.fresh_substs_for_item @@ -229,25 +248,29 @@ There are a lot of related types, and we’ll cover them in time (e.g regions/li “substitutions”, etc). There are many variants on the `TyKind` enum, which you can see by looking at its -[documentation][tykind]. Here is a sampling: +[documentation][tykind]. +Here is a sampling: - [**Algebraic Data Types (ADTs)**][kindadt] An [*algebraic data type*][wikiadt] is a `struct`, - `enum` or `union`. Under the hood, `struct`, `enum` and `union` are actually implemented - the same way: they are all [`ty::TyKind::Adt`][kindadt]. It’s basically a user defined type. + `enum` or `union`. + Under the hood, `struct`, `enum` and `union` are actually implemented + the same way: they are all [`ty::TyKind::Adt`][kindadt]. + It’s basically a user defined type. We will talk more about these later. - [**Foreign**][kindforeign] Corresponds to `extern type T`. -- [**Str**][kindstr] Is the type str. When the user writes `&str`, `Str` is the how we represent the - `str` part of that type. +- [**Str**][kindstr] Is the type str. + When the user writes `&str`, `Str` is how we represent the `str` part of that type. - [**Slice**][kindslice] Corresponds to `[T]`. - [**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 +- [**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. `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`). -- [**Error**][kinderr] Represents a type error somewhere so that we can print better diagnostics. We - will discuss this more later. +- [**Error**][kinderr] Represents a type error somewhere so that we can print better diagnostics. + We will discuss this more later. - [**And many more**...][kindvars] [wikiadt]: https://en.wikipedia.org/wiki/Algebraic_data_type @@ -270,19 +293,22 @@ Although there is no hard and fast rule, the `ty` module tends to be used like s use ty::{self, Ty, TyCtxt}; ``` -In particular, since they are so common, the `Ty` and `TyCtxt` types are imported directly. Other +In particular, since they are so common, the `Ty` and `TyCtxt` types are imported directly. +Other types are often referenced with an explicit `ty::` prefix (e.g. `ty::TraitRef<'tcx>`). But some modules choose to import a larger or smaller set of names explicitly. ## Type errors -There is a `TyKind::Error` that is produced when the user makes a type error. The idea is that +There is a `TyKind::Error` that is produced when the user makes a type error. +The idea is that we would propagate this type and suppress other errors that come up due to it so as not to overwhelm the user with cascading compiler error messages. -There is an **important invariant** for `TyKind::Error`. The compiler should -**never** produce `Error` unless we **know** that an error has already been -reported to the user. This is usually +There is an **important invariant** for `TyKind::Error`. +The compiler should **never** produce `Error` unless we **know** that an error has already been +reported to the user. +This is usually because (a) you just reported it right there or (b) you are propagating an existing Error type (in which case the error should've been reported when that error type was produced). @@ -291,10 +317,12 @@ other errors -- i.e., we don't report them. If we were to produce an `Error` typ 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 +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 compiler bug report. [`delayed_bug`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.delayed_bug @@ -302,13 +330,13 @@ compiler bug report. For added safety, it's not actually possible to produce a `TyKind::Error` value outside of [`rustc_middle::ty`][ty]; there is a private member of -`TyKind::Error` that prevents it from being constructable elsewhere. Instead, -one should use the [`Ty::new_error`][terr] or -[`Ty::new_error_with_message`][terrmsg] methods. These methods either take an `ErrorGuaranteed` -or call `span_delayed_bug` before returning an interned `Ty` of kind `Error`. If you -were already planning to use [`span_delayed_bug`], then you can just pass the -span and message to [`ty_error_with_message`][terrmsg] instead to avoid -a redundant delayed bug. +`TyKind::Error` that prevents it from being constructable elsewhere. +Instead, +one should use the [`Ty::new_error`][terr] or [`Ty::new_error_with_message`][terrmsg] methods. +These methods either take an `ErrorGuaranteed` +or call `span_delayed_bug` before returning an interned `Ty` of kind `Error`. +If you were already planning to use [`span_delayed_bug`], then you can just pass the +span and message to [`ty_error_with_message`][terrmsg] instead to avoid a redundant delayed bug. [terr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.new_error [terrmsg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.new_error_with_message @@ -316,9 +344,12 @@ a redundant delayed bug. ## `TyKind` variant shorthand syntax -When looking at the debug output of `Ty` or simply talking about different types in the compiler, you may encounter syntax that is not valid rust but is used to concisely represent internal information about types. Below is a quick reference cheat sheet to tell what the various syntax actually means, these should be covered in more depth in later chapters. +When looking at the debug output of `Ty` or simply talking about different types in the compiler, you may encounter syntax that is not valid Rust but is used to concisely represent internal information about types. +Below is a quick reference cheat sheet to tell what the various syntax actually means: - Generic parameters: `{name}/#{index}` e.g. `T/#0`, where `index` corresponds to its position in the list of generic parameters - Inference variables: `?{id}` e.g. `?x`/`?0`, where `id` identifies the inference variable - Variables from binders: `^{binder}_{index}` e.g. `^0_x`/`^0_2`, where `binder` and `index` identify which variable from which binder is being referred to - Placeholders: `!{id}` or `!{id}_{universe}` e.g. `!x`/`!0`/`!x_2`/`!0_2`, representing some unique type in the specified universe. The universe is often elided when it is `0` + +These should be covered in more depth in later chapters. diff --git a/src/doc/rustc/src/check-cfg.md b/src/doc/rustc/src/check-cfg.md index 4caeaa106b49..dfe036bf1bb1 100644 --- a/src/doc/rustc/src/check-cfg.md +++ b/src/doc/rustc/src/check-cfg.md @@ -53,7 +53,7 @@ To check for the _none_ value (ie `#[cfg(foo)]`) one can use the `none()` predic `values()`: `values(none())`. It can be followed or preceded by any number of `"value"`. To enable checking of values, but to provide an *none*/empty set of expected values -(ie. expect `#[cfg(name)]`), use these forms: +(i.e. expect `#[cfg(name)]`), use these forms: ```bash rustc --check-cfg 'cfg(name)' diff --git a/src/doc/rustc/src/lints/levels.md b/src/doc/rustc/src/lints/levels.md index 09b55da741d6..5b23ac9e09c1 100644 --- a/src/doc/rustc/src/lints/levels.md +++ b/src/doc/rustc/src/lints/levels.md @@ -393,7 +393,7 @@ Here’s how these different lint controls interact: warning: 1 warning emitted ``` -3. [CLI level flags](#via-compiler-flag) take precedence over attributes. +3. [CLI level flags](#via-compiler-flag) override the default level of a lint. They essentially behave like crate-level attributes. Attributes within the source code take precedence over CLI flags, except for `-F`/`--forbid`, which cannot be overridden. The order of the flags matter; flags on the right take precedence over earlier flags. diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md index 2003a3cb9eaa..baa46135d534 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-gnu.md @@ -29,7 +29,7 @@ If cross-compiling, make sure your C compiler is included in `$PATH`, then add i `bootstrap.toml`: ```toml -[target.aarch64-unknown-linux-musl] +[target.aarch64-unknown-linux-gnu] cc = "aarch64-linux-gnu-gcc" cxx = "aarch64-linux-gnu-g++" ar = "aarch64-linux-gnu-ar" diff --git a/src/doc/rustc/src/platform-support/armv7a-vex-v5.md b/src/doc/rustc/src/platform-support/armv7a-vex-v5.md index 68fbec2ff4b4..611ec04b42c8 100644 --- a/src/doc/rustc/src/platform-support/armv7a-vex-v5.md +++ b/src/doc/rustc/src/platform-support/armv7a-vex-v5.md @@ -24,7 +24,7 @@ This target is cross-compiled. Dynamic linking is unsupported. `std` has only partial support due to platform limitations. Notably: - `std::process` and `std::net` are unimplemented. `std::thread` only supports sleeping and yielding, as this is a single-threaded environment. - `std::time` has full support for `Instant`, but no support for `SystemTime`. -- `std::io` has full support for `stdin`/`stdout`/`stderr`. `stdout` and `stderr` both write to to USB channel 1 on this platform and are not differentiated. +- `std::io` has full support for `stdin`/`stdout`/`stderr`. `stdout` and `stderr` both write to USB channel 1 on this platform and are not differentiated. - `std::fs` has limited support for reading or writing to files. Directory operations, file deletion, and some file opening features are unsupported and will return errors. - A global allocator implemented on top of `dlmalloc` is provided. - Modules that do not need to interact with the OS beyond allocation such as `std::collections`, `std::hash`, `std::future`, `std::sync`, etc are fully supported. diff --git a/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md b/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md index ec20672f6540..3dc608e70430 100644 --- a/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md +++ b/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md @@ -267,3 +267,18 @@ the meantime using `-Cpanic=unwind` will require using [`-Zbuild-std`] and passing the appropriate flags to rustc. [`-Zbuild-std`]: ../../cargo/reference/unstable.html#build-std + +### The exception tag for panics + +Rust panics are currently implemented as a specific class of C++ exceptions. +This is because llvm only supports throwing and catching the C++ exception tag +from `wasm_throw` intrinsic and the lowering for the catchpads emitted by the +Rust try intrinsic. + +In particular, llvm throw and catch blocks expect a `WebAssembly.Tag` symbol +called `__cpp_exception`. If it is not defined somewhere, llvm will generate an +Emscripten style import from `env.__cpp_exception`. We don't want this, so we +define the symbol in `libunwind` but only for wasm32-unknown-unknown. WASI +doesn't currently support unwinding at all, and the Emscripten linker provides +the tag in an appropriate manner depending on what sort of binary is being +linked. diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-reg.md b/src/doc/unstable-book/src/language-features/asm-experimental-reg.md index a251573d276c..5f695c90e4f7 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-reg.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-reg.md @@ -22,6 +22,9 @@ This tracks support for additional registers in architectures where inline assem | Architecture | Register class | Target feature | Allowed types | | ------------ | -------------- | -------------- | ------------- | | s390x | `vreg` | `vector` | `i32`, `f32`, `i64`, `f64`, `i128`, `f128`, `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | +| x86 | `xmm_reg` | `sse` | `i128` | +| x86 | `ymm_reg` | `avx` | `i128` | +| x86 | `zmm_reg` | `avx512f` | `i128` | ## Register aliases diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index b0b6682f5279..bd27998b3770 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -18,6 +18,12 @@ def unwrap_unique_or_non_null(unique_or_nonnull): return ptr if ptr.type.code == gdb.TYPE_CODE_PTR else ptr[ptr.type.fields()[0]] +def unwrap_scalar_wrappers(wrapper): + while not wrapper.type.is_scalar: + wrapper = wrapper[wrapper.type.fields()[0]] + return wrapper + + # GDB 14 has a tag class that indicates that extension methods are ok # to call. Use of this tag only requires that printers hide local # attributes and methods by prefixing them with "_". @@ -197,8 +203,8 @@ class StdRcProvider(printer_base): self._is_atomic = is_atomic self._ptr = unwrap_unique_or_non_null(valobj["ptr"]) self._value = self._ptr["data" if is_atomic else "value"] - self._strong = self._ptr["strong"]["v" if is_atomic else "value"]["value"] - self._weak = self._ptr["weak"]["v" if is_atomic else "value"]["value"] - 1 + self._strong = unwrap_scalar_wrappers(self._ptr["strong"]) + self._weak = unwrap_scalar_wrappers(self._ptr["weak"]) - 1 def to_string(self): if self._is_atomic: diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 582471622baa..88d210691d00 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -9,6 +9,7 @@ from lldb import ( eBasicTypeUnsignedLong, eBasicTypeUnsignedChar, eFormatChar, + eTypeIsInteger, ) from rust_types import is_tuple_fields @@ -90,6 +91,12 @@ 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() @@ -1246,12 +1253,9 @@ class StdRcSyntheticProvider: rust 1.33.0: struct NonNull { pointer: *const T } struct NonZero(T) struct RcInner { strong: Cell, weak: Cell, value: T } - struct Cell { value: UnsafeCell } - struct UnsafeCell { value: T } struct Arc { ptr: NonNull>, ... } - struct ArcInner { strong: atomic::AtomicUsize, weak: atomic::AtomicUsize, data: T } - struct AtomicUsize { v: UnsafeCell } + struct ArcInner { strong: atomic::Atomic, weak: atomic::Atomic, data: T } """ def __init__(self, valobj: SBValue, _dict: LLDBOpaque, is_atomic: bool = False): @@ -1261,16 +1265,8 @@ class StdRcSyntheticProvider: self.value = self.ptr.GetChildMemberWithName("data" if is_atomic else "value") - self.strong = ( - self.ptr.GetChildMemberWithName("strong") - .GetChildAtIndex(0) - .GetChildMemberWithName("value") - ) - self.weak = ( - self.ptr.GetChildMemberWithName("weak") - .GetChildAtIndex(0) - .GetChildMemberWithName("value") - ) + self.strong = unwrap_scalar_wrappers(self.ptr.GetChildMemberWithName("strong")) + self.weak = unwrap_scalar_wrappers(self.ptr.GetChildMemberWithName("weak")) self.value_builder = ValueBuilder(valobj) diff --git a/src/etc/natvis/libcore.natvis b/src/etc/natvis/libcore.natvis index d09f0d635692..4e2f09743a03 100644 --- a/src/etc/natvis/libcore.natvis +++ b/src/etc/natvis/libcore.natvis @@ -89,38 +89,38 @@ - - {(bool)v.value} + + {(bool)v.value.__0} - - {v.value} + + {v.value.__0} - - {v.value} + + {v.value.__0} - - {v.value} + + {v.value.__0} - - {v.value} + + {v.value.__0} - - {v.value} + + {v.value.__0} - - {v.value} + + {v.value.__0} - - {v.value} + + {v.value.__0} - - {v.value} + + {v.value.__0} - - {v.value} + + {v.value.__0} - - {v.value} + + {v.value.__0} diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 485af5ab1d01..3ec2bc3af8d4 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -429,7 +429,7 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { "freebsd" => "FreeBSD", "fuchsia" => "Fuchsia", "haiku" => "Haiku", - "hermit" => "HermitCore", + "hermit" => "Hermit", "illumos" => "illumos", "ios" => "iOS", "l4re" => "L4Re", diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 22737cda97e5..09b2bc5dcef1 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -5,10 +5,9 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; -use rustc_hir as hir; -use rustc_hir::Mutability; use rustc_hir::def::{DefKind, MacroKinds, Res}; use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId, LocalModDefId}; +use rustc_hir::{self as hir, Mutability, find_attr}; use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt}; @@ -130,7 +129,7 @@ pub(crate) fn try_inline( clean::StaticItem(build_static(cx, did, cx.tcx.is_mutable_static(did))) }) } - Res::Def(DefKind::Const, did) => { + Res::Def(DefKind::Const { .. }, did) => { record_extern_fqn(cx, did, ItemType::Constant); cx.with_param_env(did, |cx| { let ct = build_const_item(cx, did); @@ -192,7 +191,7 @@ pub(crate) fn try_inline_glob( .iter() .filter(|child| !child.reexport_chain.is_empty()) .filter_map(|child| child.res.opt_def_id()) - .filter(|def_id| !cx.tcx.is_doc_hidden(def_id)) + .filter(|&def_id| !cx.tcx.is_doc_hidden(def_id)) .collect(); let attrs = cx.tcx.hir_attrs(import.hir_id()); let mut items = build_module_items( @@ -220,6 +219,8 @@ pub(crate) fn try_inline_glob( } pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> &'hir [hir::Attribute] { + // FIXME: all uses should use `find_attr`! + #[allow(deprecated)] cx.tcx.get_all_attrs(did) } @@ -403,7 +404,7 @@ pub(crate) fn build_impls( // * https://github.com/rust-lang/rust/issues/103170 — where it didn't used to get documented // * https://github.com/rust-lang/rust/pull/99917 — where the feature got used // * https://github.com/rust-lang/rust/issues/53487 — overall tracking issue for Error - if tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { + if find_attr!(tcx, did, RustcHasIncoherentInherentImpls) { let type_ = if tcx.is_trait(did) { SimplifiedType::Trait(did) } else { SimplifiedType::Adt(did) }; for &did in tcx.incoherent_impls(type_).iter() { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c09e17d3787e..13a9c789e895 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -197,7 +197,7 @@ fn generate_item_with_correct_attrs( // itself matter), for non-inlined re-exports see #85043. let import_is_inline = find_attr!( inline::load_attrs(cx, import_id.to_def_id()), - AttributeKind::Doc(d) + Doc(d) if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) ) || (is_glob_import(cx.tcx, import_id) && (cx.document_hidden() || !cx.tcx.is_doc_hidden(def_id))); @@ -1007,7 +1007,7 @@ fn clean_proc_macro<'tcx>( return ProcMacroItem(ProcMacro { kind, helpers: vec![] }); } let attrs = cx.tcx.hir_attrs(item.hir_id()); - let Some((trait_name, helper_attrs)) = find_attr!(attrs, AttributeKind::ProcMacroDerive { trait_name, helper_attrs, ..} => (*trait_name, helper_attrs)) + let Some((trait_name, helper_attrs)) = find_attr!(attrs, ProcMacroDerive { trait_name, helper_attrs, ..} => (*trait_name, helper_attrs)) else { return ProcMacroItem(ProcMacro { kind, helpers: vec![] }); }; @@ -1026,11 +1026,11 @@ fn clean_fn_or_proc_macro<'tcx>( cx: &mut DocContext<'tcx>, ) -> ItemKind { let attrs = cx.tcx.hir_attrs(item.hir_id()); - let macro_kind = if find_attr!(attrs, AttributeKind::ProcMacro(..)) { + let macro_kind = if find_attr!(attrs, ProcMacro(..)) { Some(MacroKind::Bang) - } else if find_attr!(attrs, AttributeKind::ProcMacroDerive { .. }) { + } else if find_attr!(attrs, ProcMacroDerive { .. }) { Some(MacroKind::Derive) - } else if find_attr!(attrs, AttributeKind::ProcMacroAttribute(..)) { + } else if find_attr!(attrs, ProcMacroAttribute(..)) { Some(MacroKind::Attr) } else { None @@ -1050,8 +1050,7 @@ fn clean_fn_or_proc_macro<'tcx>( /// `rustc_legacy_const_generics`. More information in /// . fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[hir::Attribute]) { - let Some(indexes) = - find_attr!(attrs, AttributeKind::RustcLegacyConstGenerics{fn_indexes,..} => fn_indexes) + let Some(indexes) = find_attr!(attrs, RustcLegacyConstGenerics{fn_indexes,..} => fn_indexes) else { return; }; @@ -1222,11 +1221,11 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Body(body)); - MethodItem(m, None) + MethodItem(m, Defaultness::from_trait_item(trait_item.defaultness)) } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(idents)) => { let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Idents(idents)); - RequiredMethodItem(m) + RequiredMethodItem(m, Defaultness::from_trait_item(trait_item.defaultness)) } hir::TraitItemKind::Type(bounds, Some(default)) => { let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); @@ -1271,7 +1270,7 @@ pub(crate) fn clean_impl_item<'tcx>( hir::ImplItemImplKind::Inherent { .. } => hir::Defaultness::Final, hir::ImplItemImplKind::Trait { defaultness, .. } => defaultness, }; - MethodItem(m, Some(defaultness)) + MethodItem(m, Defaultness::from_impl_item(defaultness)) } hir::ImplItemKind::Type(hir_ty) => { let type_ = clean_ty(hir_ty, cx); @@ -1353,18 +1352,20 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } } - let provided = match assoc_item.container { - ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => true, - ty::AssocContainer::Trait => assoc_item.defaultness(tcx).has_value(), + let defaultness = assoc_item.defaultness(tcx); + let (provided, defaultness) = match assoc_item.container { + ty::AssocContainer::Trait => { + (defaultness.has_value(), Defaultness::from_trait_item(defaultness)) + } + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { + (true, Defaultness::from_impl_item(defaultness)) + } }; + if provided { - let defaultness = match assoc_item.container { - ty::AssocContainer::TraitImpl(_) => Some(assoc_item.defaultness(tcx)), - ty::AssocContainer::InherentImpl | ty::AssocContainer::Trait => None, - }; MethodItem(item, defaultness) } else { - RequiredMethodItem(item) + RequiredMethodItem(item, defaultness) } } ty::AssocKind::Type { .. } => { @@ -1808,6 +1809,14 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T } TyKind::Slice(ty) => Slice(Box::new(clean_ty(ty, cx))), TyKind::Pat(ty, pat) => Type::Pat(Box::new(clean_ty(ty, cx)), format!("{pat:?}").into()), + TyKind::FieldOf(ty, hir::TyFieldPath { variant, field }) => { + let field_str = if let Some(variant) = variant { + format!("{variant}.{field}") + } else { + format!("{field}") + }; + Type::FieldOf(Box::new(clean_ty(ty, cx)), field_str.into()) + } TyKind::Array(ty, const_arg) => { // NOTE(min_const_generics): We can't use `const_eval_poly` for constants // as we currently do not supply the parent generics to anonymous constants @@ -2700,7 +2709,29 @@ fn add_without_unwanted_attributes<'hir>( } hir::Attribute::Parsed(AttributeKind::Doc(box d)) => { // Remove attributes from `normal` that should not be inherited by `use` re-export. - let DocAttribute { hidden, inline, cfg, .. } = d; + let DocAttribute { + aliases, + hidden, + inline, + cfg, + auto_cfg: _, + auto_cfg_change: _, + fake_variadic: _, + keyword: _, + attribute: _, + masked: _, + notable_trait: _, + search_unbox: _, + html_favicon_url: _, + html_logo_url: _, + html_playground_url: _, + html_root_url: _, + html_no_source: _, + issue_tracker_base_url: _, + rust_logo: _, + test_attrs: _, + no_crate_inject: _, + } = d; let mut attr = DocAttribute::default(); if is_inline { attr.cfg = cfg.clone(); @@ -2708,6 +2739,7 @@ fn add_without_unwanted_attributes<'hir>( attr.inline = inline.clone(); attr.hidden = hidden.clone(); } + attr.aliases = aliases.clone(); attrs.push(( Cow::Owned(hir::Attribute::Parsed(AttributeKind::Doc(Box::new(attr)))), import_parent, @@ -3020,7 +3052,7 @@ fn clean_use_statement_inner<'tcx>( let attrs = cx.tcx.hir_attrs(import.hir_id()); let inline_attr = find_attr!( attrs, - AttributeKind::Doc(d) if d.inline.first().is_some_and(|(i, _)| *i == DocInline::Inline) => d + Doc(d) if d.inline.first().is_some_and(|(i, _)| *i == DocInline::Inline) => d ) .and_then(|d| d.inline.first()); let pub_underscore = visibility.is_public() && name == Some(kw::Underscore); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 9687d014c872..c595a0ae2d48 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -7,9 +7,11 @@ use arrayvec::ArrayVec; use itertools::Either; use rustc_abi::{ExternAbi, VariantIdx}; +use rustc_ast as ast; use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::thin_vec::ThinVec; +use rustc_hir as hir; use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation, DocAttribute}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; @@ -28,7 +30,6 @@ use rustc_span::symbol::{Symbol, kw, sym}; use rustc_span::{DUMMY_SP, FileName, Ident, Loc, RemapPathScopeComponents}; use tracing::{debug, trace}; -use {rustc_ast as ast, rustc_hir as hir}; pub(crate) use self::ItemKind::*; pub(crate) use self::Type::{ @@ -61,6 +62,29 @@ pub(crate) enum ItemId { Blanket { impl_id: DefId, for_: DefId }, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub(crate) enum Defaultness { + Implicit, + Default, + Final, +} + +impl Defaultness { + pub(crate) fn from_trait_item(defaultness: hir::Defaultness) -> Self { + match defaultness { + hir::Defaultness::Default { .. } => Self::Implicit, + hir::Defaultness::Final => Self::Final, + } + } + + pub(crate) fn from_impl_item(defaultness: hir::Defaultness) -> Self { + match defaultness { + hir::Defaultness::Default { .. } => Self::Default, + hir::Defaultness::Final => Self::Implicit, + } + } +} + impl ItemId { #[inline] pub(crate) fn is_local(self) -> bool { @@ -180,7 +204,14 @@ fn to_remote(url: impl ToString) -> ExternalLocation { if !url.ends_with('/') { url.push('/'); } - Remote(url) + let is_absolute = url.starts_with('/') + || url.split_once(':').is_some_and(|(scheme, _)| { + scheme.bytes().next().is_some_and(|b| b.is_ascii_alphabetic()) + && scheme + .bytes() + .all(|b| b.is_ascii_alphanumeric() || matches!(b, b'+' | b'-' | b'.')) + }); + Remote { url, is_absolute } } // See if there's documentation generated into the local directory @@ -198,12 +229,8 @@ fn to_remote(url: impl ToString) -> ExternalLocation { // Failing that, see if there's an attribute specifying where to find this // external crate let did = self.crate_num.as_def_id(); - tcx.get_all_attrs(did) - .iter() - .find_map(|a| match a { - Attribute::Parsed(AttributeKind::Doc(d)) => d.html_root_url.map(|(url, _)| url), - _ => None, - }) + find_attr!(tcx, did, Doc(d) =>d.html_root_url.map(|(url, _)| url)) + .flatten() .map(to_remote) .or_else(|| extern_url.map(to_remote)) // NOTE: only matters if `extern_url_takes_precedence` is false .unwrap_or(Unknown) // Well, at least we tried. @@ -252,13 +279,7 @@ fn retrieve_keywords_or_documented_attributes Option impl Iterator { let as_target = move |did: DefId, tcx: TyCtxt<'_>| -> Option<(DefId, Symbol)> { - tcx.get_all_attrs(did) - .iter() - .find_map(|attr| match attr { - Attribute::Parsed(AttributeKind::Doc(d)) => callback(d), - _ => None, - }) - .map(|value| (did, value)) + find_attr!(tcx, did, Doc(d) => callback(d)).flatten().map(|value| (did, value)) }; self.mapped_root_modules(tcx, as_target) } @@ -285,17 +306,14 @@ pub(crate) fn primitives( // duplicately for the same primitive. This is handled later on when // rendering by delegating everything to a hash map. fn as_primitive(def_id: DefId, tcx: TyCtxt<'_>) -> Option<(DefId, PrimitiveType)> { - tcx.get_attrs(def_id, sym::rustc_doc_primitive).next().map(|attr| { - let attr_value = attr.value_str().expect("syntax should already be validated"); - let Some(prim) = PrimitiveType::from_symbol(attr_value) else { - span_bug!( - attr.span(), - "primitive `{attr_value}` is not a member of `PrimitiveType`" - ); - }; - - (def_id, prim) - }) + let (attr_span, prim_sym) = find_attr!( + tcx, def_id, + RustcDocPrimitive(span, prim) => (*span, *prim) + )?; + let Some(prim) = PrimitiveType::from_symbol(prim_sym) else { + span_bug!(attr_span, "primitive `{prim_sym}` is not a member of `PrimitiveType`"); + }; + Some((def_id, prim)) } self.mapped_root_modules(tcx, as_primitive) @@ -306,7 +324,7 @@ fn as_primitive(def_id: DefId, tcx: TyCtxt<'_>) -> Option<(DefId, PrimitiveType) #[derive(Debug)] pub(crate) enum ExternalLocation { /// Remote URL root of the external crate - Remote(String), + Remote { url: String, is_absolute: bool }, /// This external crate can be found in the local doc/ folder Local, /// The external crate could not be found. @@ -436,7 +454,15 @@ pub(crate) fn is_unstable(&self) -> bool { } pub(crate) fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool { - self.item_id.as_def_id().map(|did| inner_docs(tcx.get_all_attrs(did))).unwrap_or(false) + self.item_id + .as_def_id() + .map(|did| { + inner_docs( + #[allow(deprecated)] + tcx.get_all_attrs(did), + ) + }) + .unwrap_or(false) } pub(crate) fn span(&self, tcx: TyCtxt<'_>) -> Option { @@ -490,6 +516,7 @@ pub(crate) fn from_def_id_and_parts( kind: ItemKind, cx: &mut DocContext<'_>, ) -> Item { + #[allow(deprecated)] let hir_attrs = cx.tcx.get_all_attrs(def_id); Self::from_def_id_and_attrs_and_parts( @@ -699,7 +726,7 @@ pub(crate) fn stable_since(&self, tcx: TyCtxt<'_>) -> Option { } pub(crate) fn is_non_exhaustive(&self) -> bool { - find_attr!(&self.attrs.other_attrs, AttributeKind::NonExhaustive(..)) + find_attr!(&self.attrs.other_attrs, NonExhaustive(..)) } /// Returns a documentation-level item type from the item. @@ -707,12 +734,12 @@ pub(crate) fn type_(&self) -> ItemType { ItemType::from(self) } - pub(crate) fn is_default(&self) -> bool { + pub(crate) fn defaultness(&self) -> Option { match self.kind { - ItemKind::MethodItem(_, Some(defaultness)) => { - defaultness.has_value() && !defaultness.is_final() + ItemKind::MethodItem(_, defaultness) | ItemKind::RequiredMethodItem(_, defaultness) => { + Some(defaultness) } - _ => false, + _ => None, } } @@ -774,8 +801,8 @@ fn build_fn_header( } } ItemKind::FunctionItem(_) - | ItemKind::MethodItem(_, _) - | ItemKind::RequiredMethodItem(_) => { + | ItemKind::MethodItem(..) + | ItemKind::RequiredMethodItem(..) => { let def_id = self.def_id().unwrap(); build_fn_header(def_id, tcx, tcx.asyncness(def_id)) } @@ -859,11 +886,11 @@ pub(crate) enum ItemKind { TraitAliasItem(TraitAlias), ImplItem(Box), /// A required method in a trait declaration meaning it's only a function signature. - RequiredMethodItem(Box), + RequiredMethodItem(Box, Defaultness), /// A method in a trait impl or a provided method in a trait declaration. /// /// Compared to [RequiredMethodItem], it also contains a method body. - MethodItem(Box, Option), + MethodItem(Box, Defaultness), StructFieldItem(Type), VariantItem(Variant), /// `fn`s from an extern block @@ -921,8 +948,8 @@ pub(crate) fn inner_items(&self) -> impl Iterator { | StaticItem(_) | ConstantItem(_) | TraitAliasItem(_) - | RequiredMethodItem(_) - | MethodItem(_, _) + | RequiredMethodItem(..) + | MethodItem(..) | StructFieldItem(_) | ForeignFunctionItem(_, _) | ForeignStaticItem(_, _) @@ -991,13 +1018,11 @@ pub(crate) struct Attributes { impl Attributes { pub(crate) fn has_doc_flag bool>(&self, callback: F) -> bool { - self.other_attrs - .iter() - .any(|a| matches!(a, Attribute::Parsed(AttributeKind::Doc(d)) if callback(d))) + find_attr!(&self.other_attrs, Doc(d) if callback(d)) } pub(crate) fn is_doc_hidden(&self) -> bool { - find_attr!(&self.other_attrs, AttributeKind::Doc(d) if d.hidden.is_some()) + find_attr!(&self.other_attrs, Doc(d) if d.hidden.is_some()) } pub(crate) fn from_hir(attrs: &[hir::Attribute]) -> Attributes { @@ -1325,6 +1350,7 @@ pub(crate) enum Type { /// The `String` field is a stringified version of the array's length parameter. Array(Box, Box), Pat(Box, Box), + FieldOf(Box, Box), /// A raw pointer type: `*const i32`, `*mut i32` RawPointer(Mutability, Box), /// A reference type: `&i32`, `&'a mut Foo` @@ -1376,7 +1402,7 @@ fn is_type_alias(&self) -> bool { /// use rustdoc::format::cache::Cache; /// use rustdoc::clean::types::{Type, PrimitiveType}; /// let cache = Cache::new(false); - /// let generic = Type::Generic(rustc_span::symbol::sym::Any); + /// let generic = Type::Generic(Symbol::intern("T")); /// let unit = Type::Primitive(PrimitiveType::Unit); /// assert!(!generic.is_doc_subtype_of(&unit, &cache)); /// assert!(unit.is_doc_subtype_of(&generic, &cache)); @@ -1538,6 +1564,7 @@ pub(crate) fn def_id(&self, cache: &Cache) -> Option { Slice(..) => PrimitiveType::Slice, Array(..) => PrimitiveType::Array, Type::Pat(..) => PrimitiveType::Pat, + Type::FieldOf(..) => PrimitiveType::FieldOf, RawPointer(..) => PrimitiveType::RawPointer, QPath(box QPathData { self_type, .. }) => return self_type.def_id(cache), Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => return None, @@ -1585,6 +1612,7 @@ pub(crate) enum PrimitiveType { Slice, Array, Pat, + FieldOf, Tuple, Unit, RawPointer, @@ -1740,6 +1768,7 @@ pub(crate) fn as_sym(&self) -> Symbol { Char => sym::char, Array => sym::array, Pat => sym::pat, + FieldOf => sym::field_of, Slice => sym::slice, Tuple => sym::tuple, Unit => sym::unit, diff --git a/src/librustdoc/clean/types/tests.rs b/src/librustdoc/clean/types/tests.rs index 915b7d851a3e..a0fc623c20c4 100644 --- a/src/librustdoc/clean/types/tests.rs +++ b/src/librustdoc/clean/types/tests.rs @@ -73,9 +73,11 @@ fn should_not_trim() { fn is_same_generic() { use crate::clean::types::{PrimitiveType, Type}; use crate::formats::cache::Cache; - let cache = Cache::new(false, false); - let generic = Type::Generic(rustc_span::symbol::sym::Any); - let unit = Type::Primitive(PrimitiveType::Unit); - assert!(!generic.is_doc_subtype_of(&unit, &cache)); - assert!(unit.is_doc_subtype_of(&generic, &cache)); + create_default_session_globals_then(|| { + let cache = Cache::new(false, false); + let generic = Type::Generic(sym::character('T')); + let unit = Type::Primitive(PrimitiveType::Unit); + assert!(!generic.is_doc_subtype_of(&unit, &cache)); + assert!(unit.is_doc_subtype_of(&generic, &cache)); + }) } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index bd83c6ea6b83..834b73997c95 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -1,24 +1,22 @@ -#[cfg(bootstrap)] -pub use std::assert_matches::debug_assert_matches; -#[cfg(not(bootstrap))] pub use std::debug_assert_matches; use std::fmt::{self, Display, Write as _}; use std::sync::LazyLock as Lazy; use std::{ascii, mem}; +use rustc_ast as ast; use rustc_ast::join_path_idents; use rustc_ast::tokenstream::TokenTree; use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; -use rustc_hir::Attribute; -use rustc_hir::attrs::{AttributeKind, DocAttribute}; +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}; use rustc_span::symbol::{Symbol, kw, sym}; use tracing::{debug, warn}; -use {rustc_ast as ast, rustc_hir as hir}; use crate::clean::auto_trait::synthesize_auto_trait_impls; use crate::clean::blanket_impl::synthesize_blanket_impls; @@ -504,7 +502,7 @@ pub(crate) fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId { Res::Def( AssocTy | AssocFn - | AssocConst + | AssocConst { .. } | Variant | Fn | TyAlias @@ -514,7 +512,7 @@ pub(crate) fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId { | Union | Mod | ForeignTy - | Const + | Const { .. } | Static { .. } | Macro(..) | TraitAlias, @@ -574,10 +572,7 @@ pub(crate) fn has_doc_flag bool>( did: DefId, callback: F, ) -> bool { - tcx.get_all_attrs(did).iter().any(|attr| match attr { - Attribute::Parsed(AttributeKind::Doc(d)) => callback(d), - _ => false, - }) + find_attr!(tcx, did, Doc(d) if callback(d)) } /// A link to `doc.rust-lang.org` that includes the channel name. Use this instead of manual links diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 7629425ffb55..bb41758c7a2e 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -157,11 +157,10 @@ pub(crate) fn new_dcx( diagnostic_width: Option, unstable_opts: &UnstableOptions, ) -> rustc_errors::DiagCtxt { - let translator = rustc_driver::default_translator(); let emitter: Box = match error_format { ErrorOutputType::HumanReadable { kind, color_config } => match kind { HumanReadableErrorType { short, unicode } => Box::new( - AnnotateSnippetEmitter::new(stderr_destination(color_config), translator) + AnnotateSnippetEmitter::new(stderr_destination(color_config)) .sm(source_map.map(|sm| sm as _)) .short_message(short) .diagnostic_width(diagnostic_width) @@ -178,7 +177,6 @@ pub(crate) fn new_dcx( JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), Some(source_map), - translator, pretty, json_rendered, color_config, @@ -287,7 +285,11 @@ pub(crate) fn create_config( crate_check_cfg: check_cfgs, input, output_file: None, - output_dir: None, + output_dir: if render_options.output_to_stdout { + None + } else { + Some(render_options.output.clone()) + }, file_loader: None, lint_caps, psess_created: None, @@ -349,7 +351,7 @@ pub(crate) fn run_global_ctxt( // (see `override_queries` in the `config`) // NOTE: These are copy/pasted from typeck/lib.rs and should be kept in sync with those changes. - let _ = tcx.sess.time("wf_checking", || tcx.ensure_ok().check_type_wf(())); + tcx.sess.time("wf_checking", || tcx.ensure_ok().check_type_wf(())); tcx.dcx().abort_if_errors(); diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index d30d869cd4c1..a77efaaed8d5 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -467,7 +467,6 @@ fn parse_source( let filename = FileName::anon_source_code(&wrapped_source); let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); - let translator = rustc_driver::default_translator(); let supports_color = match get_stderr_color_choice(ColorConfig::Auto, &std::io::stderr()) { ColorChoice::Auto => unreachable!(), ColorChoice::AlwaysAnsi | ColorChoice::Always => true, @@ -476,7 +475,7 @@ fn parse_source( info.supports_color = supports_color; // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that the parser emits directly into a `Sink` instead of stderr. - let emitter = AnnotateSnippetEmitter::new(AutoStream::never(Box::new(io::sink())), translator); + let emitter = AnnotateSnippetEmitter::new(AutoStream::never(Box::new(io::sink()))); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index ee5f260615db..c970fdbbc93a 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -82,8 +82,8 @@ fn fold_inner_recur(&mut self, kind: ItemKind) -> ItemKind { | StaticItem(_) | ConstantItem(..) | TraitAliasItem(_) - | RequiredMethodItem(_) - | MethodItem(_, _) + | RequiredMethodItem(..) + | MethodItem(..) | StructFieldItem(_) | ForeignFunctionItem(..) | ForeignStaticItem(..) diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index d3923e3e3116..6830c1ec6560 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -156,7 +156,7 @@ pub(crate) fn from_def_id(def_id: DefId, tcx: TyCtxt<'_>) -> Self { DefKind::Enum => Self::Enum, DefKind::Fn => Self::Function, DefKind::Mod => Self::Module, - DefKind::Const => Self::Constant, + DefKind::Const { .. } => Self::Constant, DefKind::Static { .. } => Self::Static, DefKind::Struct => Self::Struct, DefKind::Union => Self::Union, @@ -180,7 +180,7 @@ pub(crate) fn from_def_id(def_id: DefId, tcx: TyCtxt<'_>) -> Self { } DefKind::Ctor(CtorOf::Struct, _) => Self::Struct, DefKind::Ctor(CtorOf::Variant, _) => Self::Variant, - DefKind::AssocConst => Self::AssocConst, + DefKind::AssocConst { .. } => Self::AssocConst, DefKind::TyParam | DefKind::ConstParam | DefKind::ExternCrate diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index f38a21bd1ff3..7eff0b5402b4 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -22,8 +22,8 @@ use rustc_hir::{ConstStability, StabilityLevel, StableSince}; use rustc_metadata::creader::CStore; use rustc_middle::ty::{self, TyCtxt, TypingMode}; +use rustc_span::Symbol; use rustc_span::symbol::kw; -use rustc_span::{Symbol, sym}; use tracing::{debug, trace}; use super::url_parts_builder::UrlPartsBuilder; @@ -386,32 +386,32 @@ fn generate_macro_def_id_path( } else { ItemType::Macro }; - let mut path = clean::inline::get_item_path(tcx, def_id, item_type); - if path.len() < 2 { - // The minimum we can have is the crate name followed by the macro name. If shorter, then - // it means that `relative` was empty, which is an error. - debug!("macro path cannot be empty!"); + let path = clean::inline::get_item_path(tcx, def_id, item_type); + // The minimum we can have is the crate name followed by the macro name. If shorter, then + // it means that `relative` was empty, which is an error. + let [module_path @ .., last] = path.as_slice() else { + debug!("macro path is empty!"); + return Err(HrefError::NotInExternalCache); + }; + if module_path.is_empty() { + debug!("macro path too short: missing crate prefix (got 1 element, need at least 2)"); return Err(HrefError::NotInExternalCache); } - // FIXME: Try to use `iter().chain().once()` instead. - let mut prev = None; - if let Some(last) = path.pop() { - path.push(Symbol::intern(&format!("{}.{last}.html", item_type.as_str()))); - prev = Some(last); - } - let url = match cache.extern_locations[&def_id.krate] { - ExternalLocation::Remote(ref s) => { - // `ExternalLocation::Remote` always end with a `/`. - format!("{s}{path}", path = fmt::from_fn(|f| path.iter().joined("/", f))) + ExternalLocation::Remote { ref url, is_absolute } => { + let mut prefix = remote_url_prefix(url, is_absolute, cx.current.len()); + 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}", + "{root_path}{path}/{item_type}.{last}.html", root_path = root_path.unwrap_or(""), - path = fmt::from_fn(|f| path.iter().joined("/", f)) + path = fmt::from_fn(|f| module_path.iter().joined("/", f)), + item_type = item_type.as_str(), ) } ExternalLocation::Unknown => { @@ -419,10 +419,6 @@ fn generate_macro_def_id_path( return Err(HrefError::NotInExternalCache); } }; - if let Some(prev) = prev { - path.pop(); - path.push(prev); - } Ok(HrefInfo { url, kind: item_type, rust_path: path }) } @@ -458,15 +454,15 @@ fn generate_item_def_id_path( let shortty = ItemType::from_def_id(def_id, tcx); let module_fqp = to_module_fqp(shortty, &fqp); - let mut is_remote = false; - let url_parts = url_parts(cx.cache(), def_id, module_fqp, &cx.current, &mut is_remote)?; - let mut url_parts = make_href(root_path, shortty, url_parts, &fqp, is_remote); + 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); + if def_id != original_def_id { let kind = ItemType::from_def_id(original_def_id, tcx); - url_parts = format!("{url_parts}#{kind}.{}", tcx.item_name(original_def_id)) + url = format!("{url}#{kind}.{}", tcx.item_name(original_def_id)) }; - Ok(HrefInfo { url: url_parts, kind: shortty, rust_path: fqp }) + Ok(HrefInfo { url, kind: shortty, rust_path: fqp }) } /// Checks if the given defid refers to an item that is unnamable, such as one defined in a const block. @@ -493,22 +489,31 @@ fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] { if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] } } +fn remote_url_prefix(url: &str, is_absolute: bool, depth: usize) -> UrlPartsBuilder { + let url = url.trim_end_matches('/'); + if is_absolute { + UrlPartsBuilder::singleton(url) + } else { + let extra = depth.saturating_sub(1); + let mut b: UrlPartsBuilder = iter::repeat_n("..", extra).collect(); + b.push(url); + b + } +} + fn url_parts( cache: &Cache, def_id: DefId, module_fqp: &[Symbol], relative_to: &[Symbol], - is_remote: &mut bool, -) -> Result { +) -> Result<(UrlPartsBuilder, bool), HrefError> { match cache.extern_locations[&def_id.krate] { - ExternalLocation::Remote(ref s) => { - *is_remote = true; - let s = s.trim_end_matches('/'); - let mut builder = UrlPartsBuilder::singleton(s); + ExternalLocation::Remote { ref url, is_absolute } => { + let mut builder = remote_url_prefix(url, is_absolute, relative_to.len()); builder.extend(module_fqp.iter().copied()); - Ok(builder) + Ok((builder, is_absolute)) } - ExternalLocation::Local => Ok(href_relative_parts(module_fqp, relative_to)), + ExternalLocation::Local => Ok((href_relative_parts(module_fqp, relative_to), false)), ExternalLocation::Unknown => Err(HrefError::DocumentationNotBuilt), } } @@ -518,9 +523,10 @@ fn make_href( shortty: ItemType, mut url_parts: UrlPartsBuilder, fqp: &[Symbol], - is_remote: bool, + is_absolute: bool, ) -> String { - if !is_remote && let Some(root_path) = root_path { + // 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); } @@ -545,7 +551,7 @@ pub(crate) fn href_with_root_path( let tcx = cx.tcx(); let def_kind = tcx.def_kind(original_did); let did = match def_kind { - DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant => { + DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::Variant => { // documented on their parent's page tcx.parent(original_did) } @@ -583,13 +589,17 @@ pub(crate) fn href_with_root_path( } } - let mut is_remote = false; - let (fqp, shortty, url_parts) = match cache.paths.get(&did) { - Some(&(ref fqp, shortty)) => (fqp, shortty, { - let module_fqp = to_module_fqp(shortty, fqp.as_slice()); - debug!(?fqp, ?shortty, ?module_fqp); - href_relative_parts(module_fqp, relative_to) - }), + let (fqp, shortty, url_parts, is_absolute) = match cache.paths.get(&did) { + Some(&(ref fqp, shortty)) => ( + fqp, + shortty, + { + let module_fqp = to_module_fqp(shortty, fqp.as_slice()); + debug!(?fqp, ?shortty, ?module_fqp); + href_relative_parts(module_fqp, relative_to) + }, + false, + ), None => { // 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 @@ -597,7 +607,8 @@ pub(crate) fn href_with_root_path( 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) { let module_fqp = to_module_fqp(shortty, fqp); - (fqp, shortty, url_parts(cache, did, module_fqp, relative_to, &mut is_remote)?) + let (parts, is_absolute) = url_parts(cache, did, module_fqp, relative_to)?; + (fqp, shortty, parts, is_absolute) } else if matches!(def_kind, DefKind::Macro(_)) { return generate_macro_def_id_path(did, cx, root_path); } else if did.is_local() { @@ -608,7 +619,7 @@ pub(crate) fn href_with_root_path( } }; Ok(HrefInfo { - url: make_href(root_path, shortty, url_parts, fqp, is_remote), + url: make_href(root_path, shortty, url_parts, fqp, is_absolute), kind: shortty, rust_path: fqp.clone(), }) @@ -627,8 +638,8 @@ pub(crate) fn href_relative_parts(fqp: &[Symbol], relative_to_fqp: &[Symbol]) -> if f != r { let dissimilar_part_count = relative_to_fqp.len() - i; let fqp_module = &fqp[i..]; - return iter::repeat_n(sym::dotdot, dissimilar_part_count) - .chain(fqp_module.iter().copied()) + return iter::repeat_n("..", dissimilar_part_count) + .chain(fqp_module.iter().map(|s| s.as_str())) .collect(); } } @@ -640,7 +651,7 @@ pub(crate) fn href_relative_parts(fqp: &[Symbol], relative_to_fqp: &[Symbol]) -> Ordering::Greater => { // e.g. linking to std::sync from std::sync::atomic let dissimilar_part_count = relative_to_fqp.len() - fqp.len(); - iter::repeat_n(sym::dotdot, dissimilar_part_count).collect() + iter::repeat_n("..", dissimilar_part_count).collect() } Ordering::Equal => { // linking to the same module @@ -762,21 +773,19 @@ fn primitive_link_fragment( } Some(&def_id) => { let loc = match m.extern_locations[&def_id.krate] { - ExternalLocation::Remote(ref s) => { + ExternalLocation::Remote { ref url, is_absolute } => { let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); - let builder: UrlPartsBuilder = - [s.as_str().trim_end_matches('/'), cname_sym.as_str()] - .into_iter() - .collect(); + let mut builder = remote_url_prefix(url, is_absolute, cx.current.len()); + builder.push(cname_sym.as_str()); Some(builder) } ExternalLocation::Local => { let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); Some(if cx.current.first() == Some(&cname_sym) { - iter::repeat_n(sym::dotdot, cx.current.len() - 1).collect() + iter::repeat_n("..", cx.current.len() - 1).collect() } else { - iter::repeat_n(sym::dotdot, cx.current.len()) - .chain(iter::once(cname_sym)) + iter::repeat_n("..", cx.current.len()) + .chain(iter::once(cname_sym.as_str())) .collect() }) } @@ -837,7 +846,7 @@ pub(crate) fn fragment(did: DefId, tcx: TyCtxt<'_>) -> impl Display { fmt::from_fn(move |f| { let def_kind = tcx.def_kind(did); match def_kind { - DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant => { + DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::Variant => { let item_type = ItemType::from_def_id(did, tcx); write!(f, "#{}.{}", item_type.as_str(), tcx.item_name(did)) } @@ -958,6 +967,11 @@ fn fmt_type( fmt::Display::fmt(&print_type(t, cx), f)?; write!(f, " is {pat}") } + clean::Type::FieldOf(t, field) => { + write!(f, "field_of!(")?; + fmt::Display::fmt(&print_type(t, cx), f)?; + write!(f, ", {field})") + } clean::Array(box clean::Generic(name), n) if !f.alternate() => primitive_link( f, PrimitiveType::Array, @@ -1566,10 +1580,6 @@ pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display { }) } -pub(crate) fn print_default_space(v: bool) -> &'static str { - if v { "default " } else { "" } -} - fn print_generic_arg(generic_arg: &clean::GenericArg, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match generic_arg { clean::GenericArg::Lifetime(lt) => f.write_str(print_lifetime(lt)), diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index b0ea8776425f..4a7b1d1d6c56 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -386,8 +386,9 @@ pub(crate) fn href_from_span(&self, span: clean::Span, with_lines: bool) -> Opti let e = ExternalCrate { crate_num: cnum }; (e.name(self.tcx()), e.src_root(self.tcx())) } - ExternalLocation::Remote(ref s) => { - root = s.to_string(); + ExternalLocation::Remote { ref url, .. } => { + // FIXME: relative extern URLs are not depth-adjusted for source pages + root = url.to_string(); let e = ExternalCrate { crate_num: cnum }; (e.name(self.tcx()), e.src_root(self.tcx())) } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 57428b6f481e..556d383a0e9f 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -59,14 +59,14 @@ use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, StableSince}; use rustc_middle::ty::print::PrintTraitRefExt; use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::DUMMY_SP; use rustc_span::symbol::{Symbol, sym}; -use rustc_span::{BytePos, DUMMY_SP, FileName}; use tracing::{debug, info}; pub(crate) use self::context::*; pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources}; pub(crate) use self::write_shared::*; -use crate::clean::{self, ItemId, RenderedLink}; +use crate::clean::{self, Defaultness, ItemId, RenderedLink}; use crate::display::{Joined as _, MaybeDisplay as _}; use crate::error::Error; use crate::formats::Impl; @@ -75,8 +75,8 @@ use crate::html::escape::Escape; use crate::html::format::{ Ending, HrefError, HrefInfo, PrintWithSpace, full_print_fn_decl, href, print_abi_with_space, - print_constness_with_space, print_default_space, print_generic_bounds, print_generics, - print_impl, print_path, print_type, print_where_clause, visibility_print_with_space, + print_constness_with_space, print_generic_bounds, print_generics, print_impl, print_path, + print_type, print_where_clause, visibility_print_with_space, }; use crate::html::markdown::{ HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, @@ -1110,7 +1110,11 @@ fn assoc_method( let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item"); let name = meth.name.as_ref().unwrap(); let vis = visibility_print_with_space(meth, cx).to_string(); - let defaultness = print_default_space(meth.is_default()); + let defaultness = match meth.defaultness().expect("Expected assoc method to have defaultness") { + Defaultness::Implicit => "", + Defaultness::Final => "final ", + Defaultness::Default => "default ", + }; // FIXME: Once https://github.com/rust-lang/rust/issues/143874 is implemented, we can remove // this condition. let constness = match render_mode { @@ -1261,7 +1265,7 @@ fn render_assoc_item( ) -> impl fmt::Display { fmt::from_fn(move |f| match &item.kind { clean::StrippedItem(..) => Ok(()), - clean::RequiredMethodItem(m) | clean::MethodItem(m, _) => { + clean::RequiredMethodItem(m, _) | clean::MethodItem(m, _) => { assoc_method(item, &m.generics, &m.decl, link, parent, cx, render_mode).fmt(f) } clean::RequiredAssocConstItem(generics, ty) => assoc_const( @@ -1586,7 +1590,7 @@ fn render_deref_methods( fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool { let self_type_opt = match item.kind { clean::MethodItem(ref method, _) => method.decl.receiver_type(), - clean::RequiredMethodItem(ref method) => method.decl.receiver_type(), + clean::RequiredMethodItem(ref method, _) => method.decl.receiver_type(), _ => None, }; @@ -1856,7 +1860,7 @@ fn doc_impl_item( deprecation_class = ""; } match &item.kind { - clean::MethodItem(..) | clean::RequiredMethodItem(_) => { + clean::MethodItem(..) | clean::RequiredMethodItem(..) => { // Only render when the method is not static or we allow static methods if render_method_item { let id = cx.derive_id(format!("{item_type}.{name}")); @@ -2034,7 +2038,9 @@ fn doc_impl_item( if !impl_.is_negative_trait_impl() { for impl_item in &impl_.items { match impl_item.kind { - clean::MethodItem(..) | clean::RequiredMethodItem(_) => methods.push(impl_item), + clean::MethodItem(..) | clean::RequiredMethodItem(..) => { + methods.push(impl_item) + } clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => { assoc_types.push(impl_item) } @@ -2337,9 +2343,10 @@ fn render_impl_summary( if let Some(doc) = doc { if impl_is_empty { w.write_str( - "

{ + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let mut diag = Diag::new( + dcx, + level, + match self.lint_kind { + AssertLintKind::ArithmeticOverflow => { + msg!("this arithmetic operation will overflow") + } + AssertLintKind::UnconditionalPanic => { + msg!("this operation will panic at runtime") + } + }, + ); let label = self.assert_kind.diagnostic_message(); self.assert_kind.add_args(&mut |name, value| { diag.arg(name, value); }); diag.span_label(self.span, label); + diag } } @@ -156,14 +163,14 @@ pub(crate) fn lint(&self) -> &'static Lint { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("call to inline assembly that may unwind")] pub(crate) struct AsmUnwindCall { #[label("call to inline assembly that may unwind")] pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "call to {$foreign -> [true] foreign function @@ -181,7 +188,7 @@ pub(crate) struct FfiUnwindCall { pub foreign: bool, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("taking a reference to a function item does not give a function pointer")] pub(crate) struct FnItemRef { #[suggestion( @@ -194,14 +201,14 @@ pub(crate) struct FnItemRef { pub ident: Ident, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("value captured by `{$name}` is never read")] #[help("did you mean to capture by reference instead?")] pub(crate) struct UnusedCaptureMaybeCaptureRef { pub name: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("variable `{$name}` is assigned to, but never used")] #[note("consider using `_{$name}` instead")] pub(crate) struct UnusedVarAssignedOnly { @@ -210,7 +217,7 @@ pub(crate) struct UnusedVarAssignedOnly { pub typo: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("value assigned to `{$name}` is never read")] pub(crate) struct UnusedAssign { pub name: Symbol, @@ -237,14 +244,14 @@ pub(crate) struct UnusedAssignSuggestion { pub rhs_borrow_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("value passed to `{$name}` is never read")] #[help("maybe it is overwritten before being read?")] pub(crate) struct UnusedAssignPassed { pub name: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused variable: `{$name}`")] pub(crate) struct UnusedVariable { pub name: Symbol, @@ -331,11 +338,13 @@ pub(crate) struct MustNotSupend<'a, 'tcx> { } // Needed for def_path_str -impl<'a> LintDiagnostic<'a, ()> for MustNotSupend<'_, '_> { - fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { - diag.primary_message(msg!( - "{$pre}`{$def_path}`{$post} held across a suspend point, but should not be" - )); +impl<'a> Diagnostic<'a, ()> for MustNotSupend<'_, '_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let mut diag = Diag::new( + dcx, + level, + msg!("{$pre}`{$def_path}`{$post} held across a suspend point, but should not be"), + ); diag.span_label(self.yield_sp, msg!("the value is held across this suspend point")); if let Some(reason) = self.reason { diag.subdiagnostic(reason); @@ -344,6 +353,7 @@ fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { diag.arg("pre", self.pre); diag.arg("def_path", self.tcx.def_path_str(self.def_id)); diag.arg("post", self.post); + diag } } @@ -379,4 +389,5 @@ pub(crate) struct ForceInlineFailure { #[note("`{$callee}` is required to be inlined to: {$sym}")] pub(crate) struct ForceInlineJustification { pub sym: Symbol, + pub callee: String, } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 6507a5194add..517bc61e5eb0 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -61,10 +61,8 @@ //! The evaluated form is inserted in `evaluated` as an `OpTy` or `None` if evaluation failed. //! //! The difficulty is non-deterministic evaluation of MIR constants. Some `Const` can have -//! different runtime values each time they are evaluated. This is the case with -//! `Const::Slice` which have a new pointer each time they are evaluated, and constants that -//! contain a fn pointer (`AllocId` pointing to a `GlobalAlloc::Function`) pointing to a different -//! symbol in each codegen unit. +//! different runtime values each time they are evaluated. This happens with valtrees that +//! generate a new allocation each time they are used. This is checked by `is_deterministic`. //! //! Meanwhile, we want to be able to read indirect constants. For instance: //! ``` @@ -81,8 +79,12 @@ //! may be non-deterministic. When that happens, we assign a disambiguator to ensure that we do not //! merge the constants. See `duplicate_slice` test in `gvn.rs`. //! -//! Second, when writing constants in MIR, we do not write `Const::Slice` or `Const` -//! that contain `AllocId`s. +//! Conversely, some constants cannot cross function boundaries, which could happen because of +//! inlining. For instance, constants that contain a fn pointer (`AllocId` pointing to a +//! `GlobalAlloc::Function`) point to a different symbol in each codegen unit. To avoid this, +//! when writing constants in MIR, we do not write `Const`s that contain `AllocId`s. This is +//! checked by `may_have_provenance`. See for +//! more information. use std::borrow::Cow; use std::hash::{Hash, Hasher}; @@ -103,7 +105,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexVec, newtype_index}; use rustc_middle::bug; -use rustc_middle::mir::interpret::GlobalAlloc; +use rustc_middle::mir::interpret::{AllocRange, GlobalAlloc}; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::layout::HasTypingEnv; @@ -300,7 +302,7 @@ fn insert_unique( /// Insert a `(Value, Ty)` pair to be deduplicated. /// Returns `true` as second tuple field if this value did not exist previously. - #[allow(rustc::pass_by_value)] // closures take `&VnIndex` + #[allow(rustc::disallowed_pass_by_ref)] // closures take `&VnIndex` fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> (VnIndex, bool) { debug_assert!(match value { Value::Opaque(_) | Value::Address { .. } => false, @@ -487,7 +489,7 @@ fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> Option) -> VnIndex { - if value.is_deterministic() { + if is_deterministic(value) { // The constant is deterministic, no need to disambiguate. let constant = Value::Constant { value, disambiguator: None }; self.insert(value.ty(), constant) @@ -522,14 +524,14 @@ fn assign(&mut self, local: Local, value: VnIndex) { fn insert_bool(&mut self, flag: bool) -> VnIndex { // Booleans are deterministic. let value = Const::from_bool(self.tcx, flag); - debug_assert!(value.is_deterministic()); + debug_assert!(is_deterministic(value)); self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: None }) } fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex { // Scalars are deterministic. let value = Const::from_scalar(self.tcx, scalar, ty); - debug_assert!(value.is_deterministic()); + debug_assert!(is_deterministic(value)); self.insert(ty, Value::Constant { value, disambiguator: None }) } @@ -1002,21 +1004,19 @@ fn simplify_operand( operand: &mut Operand<'tcx>, location: Location, ) -> Option { - match *operand { - Operand::RuntimeChecks(c) => { - Some(self.insert(self.tcx.types.bool, Value::RuntimeChecks(c))) - } - Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)), + let value = match *operand { + Operand::RuntimeChecks(c) => self.insert(self.tcx.types.bool, Value::RuntimeChecks(c)), + Operand::Constant(ref constant) => self.insert_constant(constant.const_), Operand::Copy(ref mut place) | Operand::Move(ref mut place) => { - let value = self.simplify_place_value(place, location)?; - if let Some(const_) = self.try_as_constant(value) { - *operand = Operand::Constant(Box::new(const_)); - } else if let Value::RuntimeChecks(c) = self.get(value) { - *operand = Operand::RuntimeChecks(c); - } - Some(value) + self.simplify_place_value(place, location)? } + }; + if let Some(const_) = self.try_as_constant(value) { + *operand = Operand::Constant(Box::new(const_)); + } else if let Value::RuntimeChecks(c) = self.get(value) { + *operand = Operand::RuntimeChecks(c); } + Some(value) } #[instrument(level = "trace", skip(self), ret)] @@ -1069,7 +1069,7 @@ fn simplify_rvalue( // Unsupported values. Rvalue::ThreadLocalRef(..) => return None, - Rvalue::CopyForDeref(_) | Rvalue::ShallowInitBox(..) => { + Rvalue::CopyForDeref(_) => { bug!("forbidden in runtime MIR: {rvalue:?}") } }; @@ -1731,6 +1731,49 @@ fn value_is_all_in_one_field( } } +/// Return true if any evaluation of this constant in the same MIR body +/// always returns the same value, taking into account even pointer identity tests. +/// +/// In other words, this answers: is "cloning" the `Const` ok? +/// +/// This returns `false` for constants that synthesize new `AllocId` when they are instantiated. +/// It is `true` for anything else, since a given `AllocId` *does* have a unique runtime value +/// within the scope of a single MIR body. +fn is_deterministic(c: Const<'_>) -> bool { + // Primitive types cannot contain provenance and always have the same value. + if c.ty().is_primitive() { + return true; + } + + match c { + // Some constants may generate fresh allocations for pointers they contain, + // so using the same constant twice can yield two different results. + // Notably, valtrees purposefully generate new allocations. + Const::Ty(..) => false, + // We do not know the contents, so don't attempt to do anything clever. + Const::Unevaluated(..) => false, + // When an evaluated constant contains provenance, it is encoded as an `AllocId`. + // Cloning the constant will reuse the same `AllocId`. If this is in the same MIR + // body, this same `AllocId` will result in the same pointer in codegen. + Const::Val(..) => true, + } +} + +/// Check if a constant may contain provenance information. +/// Can return `true` even if there is no provenance. +fn may_have_provenance(tcx: TyCtxt<'_>, value: ConstValue, size: Size) -> bool { + match value { + ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false, + ConstValue::Scalar(Scalar::Ptr(..)) | ConstValue::Slice { .. } => return true, + ConstValue::Indirect { alloc_id, offset } => !tcx + .global_alloc(alloc_id) + .unwrap_memory() + .inner() + .provenance() + .range_empty(AllocRange::from(offset..offset + size), &tcx), + } +} + fn op_to_prop_const<'tcx>( ecx: &mut InterpCx<'tcx, DummyMachine>, op: &OpTy<'tcx>, @@ -1762,7 +1805,7 @@ fn op_to_prop_const<'tcx>( if !scalar.try_to_scalar_int().is_ok() { // Check that we do not leak a pointer. // Those pointers may lose part of their identity in codegen. - // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. + // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/128775 is fixed. return None; } return Some(ConstValue::Scalar(scalar)); @@ -1774,7 +1817,7 @@ fn op_to_prop_const<'tcx>( let (size, _align) = ecx.size_and_align_of_val(&mplace).discard_err()??; // Do not try interning a value that contains provenance. - // Due to https://github.com/rust-lang/rust/issues/79738, doing so could lead to bugs. + // Due to https://github.com/rust-lang/rust/issues/128775, doing so could lead to bugs. // FIXME: remove this hack once that issue is fixed. let alloc_ref = ecx.get_ptr_alloc(mplace.ptr(), size).discard_err()??; if alloc_ref.has_provenance() { @@ -1801,16 +1844,7 @@ fn op_to_prop_const<'tcx>( // Everything failed: create a new allocation to hold the data. let alloc_id = ecx.intern_with_temp_alloc(op.layout, |ecx, dest| ecx.copy_op(op, dest)).discard_err()?; - let value = ConstValue::Indirect { alloc_id, offset: Size::ZERO }; - - // Check that we do not leak a pointer. - // Those pointers may lose part of their identity in codegen. - // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. - if ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty() { - return Some(value); - } - - None + Some(ConstValue::Indirect { alloc_id, offset: Size::ZERO }) } impl<'tcx> VnState<'_, '_, 'tcx> { @@ -1831,14 +1865,28 @@ fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option Option> { - // This was already constant in MIR, do not change it. If the constant is not - // deterministic, adding an additional mention of it in MIR will not give the same value as - // the former mention. - if let Value::Constant { value, disambiguator: None } = self.get(index) { - debug_assert!(value.is_deterministic()); + let value = self.get(index); + + // This was already an *evaluated* constant in MIR, do not change it. + if let Value::Constant { value, disambiguator: None } = value + && let Const::Val(..) = value + { return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } + if let Some(value) = self.try_as_evaluated_constant(index) { + return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); + } + + // We failed to provide an evaluated form, fallback to using the unevaluated constant. + if let Value::Constant { value, disambiguator: None } = value { + return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); + } + + None + } + + fn try_as_evaluated_constant(&mut self, index: VnIndex) -> Option> { let op = self.eval_to_const(index)?; if op.layout.is_unsized() { // Do not attempt to propagate unsized locals. @@ -1849,11 +1897,12 @@ fn try_as_constant(&mut self, index: VnIndex) -> Option> { // Check that we do not leak a pointer. // Those pointers may lose part of their identity in codegen. - // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. - assert!(!value.may_have_provenance(self.tcx, op.layout.size)); + // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/128775 is fixed. + if may_have_provenance(self.tcx, value, op.layout.size) { + return None; + } - let const_ = Const::Val(value, op.layout.ty); - Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ }) + Some(Const::Val(value, op.layout.ty)) } /// Construct a place which holds the same value as `index` and for which all locals strictly diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 179ada36be75..9069f279e981 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -1,10 +1,9 @@ //! Inlining pass for MIR functions. -use std::iter; use std::ops::{Range, RangeFrom}; +use std::{debug_assert_matches, iter}; use rustc_abi::{ExternAbi, FieldIdx}; -use rustc_data_structures::debug_assert_matches; use rustc_hir::attrs::{InlineAttr, OptimizeAttr}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -251,15 +250,17 @@ fn on_inline_failure(&self, callsite: &CallSite<'tcx>, reason: &'static str) { }; let call_span = callsite.source_info.span; + let callee = tcx.def_path_str(callsite.callee.def_id()); tcx.dcx().emit_err(crate::errors::ForceInlineFailure { call_span, attr_span, caller_span: tcx.def_span(self.def_id), caller: tcx.def_path_str(self.def_id), callee_span: tcx.def_span(callsite.callee.def_id()), - callee: tcx.def_path_str(callsite.callee.def_id()), + callee: callee.clone(), reason, - justification: justification.map(|sym| crate::errors::ForceInlineJustification { sym }), + justification: justification + .map(|sym| crate::errors::ForceInlineJustification { sym, callee }), }); } } @@ -607,7 +608,6 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>( let callee_attrs = callee_attrs.as_ref(); check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?; check_codegen_attributes(inliner, callsite, callee_attrs)?; - inliner.check_codegen_attributes_extra(callee_attrs)?; let terminator = caller_body[callsite.block].terminator.as_ref().unwrap(); let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() }; diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 84d642ea4ebc..b97901f075bc 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -1,7 +1,6 @@ //! Performs various peephole optimizations. use rustc_abi::ExternAbi; -use rustc_hir::attrs::AttributeKind; use rustc_hir::{LangItem, find_attr}; use rustc_middle::bug; use rustc_middle::mir::visit::MutVisitor; @@ -30,8 +29,7 @@ fn is_enabled(&self, sess: &rustc_session::Session) -> bool { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let preserve_ub_checks = - find_attr!(tcx.hir_krate_attrs(), AttributeKind::RustcPreserveUbChecks); + let preserve_ub_checks = find_attr!(tcx.hir_krate_attrs(), RustcPreserveUbChecks); if !preserve_ub_checks { SimplifyUbCheck { tcx }.visit_body(body); } @@ -56,7 +54,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let terminator = block.terminator.as_mut().unwrap(); ctx.simplify_primitive_clone(terminator, &mut block.statements); - ctx.simplify_align_of_slice_val(terminator, &mut block.statements); + ctx.simplify_size_or_align_of_val(terminator, &mut block.statements); ctx.simplify_intrinsic_assert(terminator); ctx.simplify_nounwind_call(terminator); simplify_duplicate_switch_targets(terminator); @@ -246,13 +244,18 @@ fn simplify_primitive_clone( terminator.kind = TerminatorKind::Goto { target: *destination_block }; } - // Convert `align_of_val::<[T]>(ptr)` to `align_of::()`, since the - // alignment of a slice doesn't actually depend on metadata at all - // and the element type is always `Sized`. - // - // This is here so it can run after inlining, where it's more useful. - // (LowerIntrinsics is done in cleanup, before the optimization passes.) - fn simplify_align_of_slice_val( + /// Simplify `size_of_val` and `align_of_val` if we don't actually need + /// to look at the value in order to calculate the result: + /// - For `Sized` types we can always do this for both, + /// - For `align_of_val::<[T]>` we can return `align_of::()`, since it + /// doesn't depend on the slice's length and the elements are sized. + /// + /// This is here so it can run after inlining, where it's more useful. + /// (LowerIntrinsics is done in cleanup, before the optimization passes.) + /// + /// Note that we intentionally just produce the lang item constants so this + /// works on generic types and avoids any risk of layout calculation cycles. + fn simplify_size_or_align_of_val( &self, terminator: &mut Terminator<'tcx>, statements: &mut Vec>, @@ -263,19 +266,35 @@ fn simplify_align_of_slice_val( } = &terminator.kind && args.len() == 1 && let Some((fn_def_id, generics)) = func.const_fn_def() - && self.tcx.is_intrinsic(fn_def_id, sym::align_of_val) - && let ty::Slice(elem_ty) = *generics.type_at(0).kind() { - let align_def_id = self.tcx.require_lang_item(LangItem::AlignOf, source_info.span); - let align_const = Operand::unevaluated_constant( + let lang_item = if self.tcx.is_intrinsic(fn_def_id, sym::size_of_val) { + LangItem::SizeOf + } else if self.tcx.is_intrinsic(fn_def_id, sym::align_of_val) { + LangItem::AlignOf + } else { + return; + }; + let generic_ty = generics.type_at(0); + let ty = if generic_ty.is_sized(self.tcx, self.typing_env) { + generic_ty + } else if let LangItem::AlignOf = lang_item + && let ty::Slice(elem_ty) = *generic_ty.kind() + { + elem_ty + } else { + return; + }; + + let const_def_id = self.tcx.require_lang_item(lang_item, source_info.span); + let const_op = Operand::unevaluated_constant( self.tcx, - align_def_id, - &[elem_ty.into()], + const_def_id, + &[ty.into()], source_info.span, ); statements.push(Statement::new( source_info, - StatementKind::Assign(Box::new((*destination, Rvalue::Use(align_const)))), + StatementKind::Assign(Box::new((*destination, Rvalue::Use(const_op)))), )); terminator.kind = TerminatorKind::Goto { target: *destination_block }; } diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index caaf300a88d6..c4994417ed7e 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -35,7 +35,7 @@ fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let def_id = body.source.def_id().expect_local(); let def_kind = tcx.def_kind(def_id); let is_fn_like = def_kind.is_fn_like(); - let is_assoc_const = def_kind == DefKind::AssocConst; + let is_assoc_const = matches!(def_kind, DefKind::AssocConst { .. }); // Only run const prop on functions, methods, closures and associated constants if !is_fn_like && !is_assoc_const { @@ -443,7 +443,6 @@ fn check_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) -> Option< | Rvalue::CopyForDeref(..) | Rvalue::Repeat(..) | Rvalue::Cast(..) - | Rvalue::ShallowInitBox(..) | Rvalue::Discriminant(..) | Rvalue::WrapUnsafeBinder(..) => {} } @@ -605,8 +604,6 @@ fn eval_rvalue(&mut self, rvalue: &Rvalue<'tcx>, dest: &Place<'tcx>) -> Option<( Ref(..) | RawPtr(..) => return None, - ShallowInitBox(..) => return None, - Cast(ref kind, ref value, to) => match kind { CastKind::IntToInt | CastKind::IntToFloat => { let value = self.eval_operand(value)?; diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index bc2c6bd81aca..6bf4221d9ab7 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,11 +1,10 @@ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] #![feature(const_type_name)] #![feature(cow_is_borrowed)] #![feature(file_buffered)] -#![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] +#![feature(iterator_try_collect)] #![feature(try_blocks)] #![feature(yeet_expr)] // tidy-alphabetical-end @@ -443,8 +442,8 @@ fn mir_promoted( { tcx.mir_const_qualif(def) } - DefKind::AssocConst - | DefKind::Const + DefKind::AssocConst { .. } + | DefKind::Const { .. } | DefKind::Static { .. } | DefKind::InlineConst | DefKind::AnonConst => tcx.mir_const_qualif(def), @@ -561,9 +560,9 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & DefKind::Fn | DefKind::AssocFn | DefKind::Static { .. } - | DefKind::Const - | DefKind::AssocConst => { - if let Err(guar) = tcx.ensure_ok().check_well_formed(root.expect_local()) { + | DefKind::Const { .. } + | DefKind::AssocConst { .. } => { + if let Err(guar) = tcx.ensure_result().check_well_formed(root.expect_local()) { body.tainted_by_errors = Some(guar); } } diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index 88297a4efef7..b8b1b930bc41 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -85,7 +85,6 @@ fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { | Rvalue::Repeat(..) | Rvalue::Aggregate(..) | Rvalue::Cast(..) - | Rvalue::ShallowInitBox(..) | Rvalue::WrapUnsafeBinder(..) => true, Rvalue::ThreadLocalRef(..) | Rvalue::UnaryOp(..) 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 2f733f54b26c..c354279b8ed3 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 @@ -5,12 +5,13 @@ use itertools::Itertools as _; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::unord::{UnordMap, UnordSet}; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{Subdiagnostic, msg}; use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::LocalDefId; use rustc_index::bit_set::MixedBitSet; use rustc_index::{IndexSlice, IndexVec}; -use rustc_macros::{LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::bug; use rustc_middle::mir::{ self, BasicBlock, Body, ClearCrossCrate, Local, Location, MirDumper, Place, StatementKind, @@ -23,8 +24,8 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; use rustc_mir_dataflow::{Analysis, MaybeReachable, ResultsCursor}; +use rustc_session::lint; use rustc_session::lint::builtin::TAIL_EXPR_DROP_ORDER; -use rustc_session::lint::{self}; use rustc_span::{DUMMY_SP, Span, Symbol}; use tracing::debug; @@ -498,7 +499,7 @@ fn assign_observables_names( names } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("relative drop order changing in Rust 2024")] struct TailExprDropOrderLint<'a> { #[subdiagnostic] @@ -524,31 +525,30 @@ 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>) { - // Because parent uses this field , we need to remove it delay before adding it. - diag.remove_arg("name"); - diag.arg("name", self.name); - diag.remove_arg("is_generated_name"); - diag.arg("is_generated_name", self.is_generated_name); - diag.remove_arg("is_dropped_first_edition_2024"); - diag.arg("is_dropped_first_edition_2024", self.is_dropped_first_edition_2024); - let msg = diag.eagerly_translate(msg!( - "{$is_generated_name -> - [true] this value will be stored in a temporary; let us call it `{$name}` - *[false] `{$name}` calls a custom destructor - }" - )); - diag.span_label(self.span, msg); + diag.span_label( + self.span, + msg!( + "{$is_generated_name -> + [true] this value will be stored in a temporary; let us call it `{$name}` + *[false] `{$name}` calls a custom destructor + }" + ) + .arg("name", self.name) + .arg("is_generated_name", self.is_generated_name) + .format(), + ); for dtor in self.destructors { dtor.add_to_diag(diag); } - let msg = - diag.eagerly_translate(msg!( - "{$is_dropped_first_edition_2024 -> - [true] up until Edition 2021 `{$name}` is dropped last but will be dropped earlier in Edition 2024 - *[false] `{$name}` will be dropped later as of Edition 2024 - }" - )); - diag.span_label(self.span, msg); + diag.span_label(self.span, msg!( + "{$is_dropped_first_edition_2024 -> + [true] up until Edition 2021 `{$name}` is dropped last but will be dropped earlier in Edition 2024 + *[false] `{$name}` will be dropped later as of Edition 2024 + }" + ) + .arg("is_dropped_first_edition_2024", self.is_dropped_first_edition_2024) + .arg("name", self.name) + .format()); } } diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index 9bafee9f31c3..af246735b95a 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1,6 +1,5 @@ use rustc_abi::FieldIdx; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::find_attr; @@ -63,14 +62,14 @@ pub(crate) fn check_liveness<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Den } // Don't run unused pass for #[naked] - if find_attr!(tcx.get_all_attrs(def_id.to_def_id()), AttributeKind::Naked(..)) { + if find_attr!(tcx, def_id.to_def_id(), Naked(..)) { return DenseBitSet::new_empty(0); } // Don't run unused pass for #[derive] let parent = tcx.parent(tcx.typeck_root_def_id(def_id.to_def_id())); if let DefKind::Impl { of_trait: true } = tcx.def_kind(parent) - && find_attr!(tcx.get_all_attrs(parent), AttributeKind::AutomaticallyDerived(..)) + && find_attr!(tcx, parent, AutomaticallyDerived(..)) { return DenseBitSet::new_empty(0); } @@ -202,7 +201,7 @@ fn maybe_suggest_unit_pattern_typo<'tcx>( let constants = tcx .hir_body_owners() .filter(|&def_id| { - matches!(tcx.def_kind(def_id), DefKind::Const) + matches!(tcx.def_kind(def_id), DefKind::Const { .. }) && tcx.type_of(def_id).instantiate_identity() == ty && tcx.visibility(def_id).is_accessible_from(body_def_id, tcx) }) @@ -1291,6 +1290,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location TerminatorKind::Return | TerminatorKind::Yield { .. } | TerminatorKind::Goto { target: START_BLOCK } // Inserted for the `FnMut` case. + | TerminatorKind::Call { target: None, .. } // unwinding could be caught if self.capture_kind != CaptureKind::None => { // All indirect captures have an effect on the environment, so we mark them as live. diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index dcee54c37108..8b2e68f156f9 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -177,30 +177,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { Some(target) => TerminatorKind::Goto { target }, } } - sym::write_via_move => { - let target = target.unwrap(); - let Ok([ptr, val]) = take_array(args) else { - span_bug!( - terminator.source_info.span, - "Wrong number of arguments for write_via_move intrinsic", - ); - }; - let derefed_place = if let Some(place) = ptr.node.place() - && let Some(local) = place.as_local() - { - tcx.mk_place_deref(local.into()) - } else { - span_bug!( - terminator.source_info.span, - "Only passing a local is supported" - ); - }; - block.statements.push(Statement::new( - terminator.source_info, - StatementKind::Assign(Box::new((derefed_place, Rvalue::Use(val.node)))), - )); - terminator.kind = TerminatorKind::Goto { target }; - } + // `write_via_move` is already lowered during MIR building. sym::discriminant_value => { let target = target.unwrap(); let Ok([arg]) = take_array(args) else { diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 5e511f1a418b..05d085fafe93 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -1,50 +1,34 @@ -use std::iter; - use rustc_abi::Integer; -use rustc_index::IndexSlice; +use rustc_const_eval::const_eval::mk_eval_cx_for_const_val; use rustc_middle::mir::*; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; +use rustc_middle::ty::util::Discr; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; -use tracing::instrument; use super::simplify::simplify_cfg; use crate::patch::MirPatch; +use crate::unreachable_prop::remove_successors_from_switch; +/// Unifies all targets into one basic block if each statement can have the same statement. pub(super) struct MatchBranchSimplification; impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.mir_opt_level() >= 1 + // Enable only under -Zmir-opt-level=2 as this can make programs less debuggable. + sess.mir_opt_level() >= 2 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let typing_env = body.typing_env(tcx); - let mut apply_patch = false; - let mut patch = MirPatch::new(body); - for (bb, bb_data) in body.basic_blocks.iter_enumerated() { - match &bb_data.terminator().kind { - TerminatorKind::SwitchInt { - discr: Operand::Copy(_) | Operand::Move(_), - targets, - .. - // We require that the possible target blocks don't contain this block. - } if !targets.all_targets().contains(&bb) => {} - // Only optimize switch int statements - _ => continue, + let mut changed = false; + for bb in body.basic_blocks.indices() { + if !candidate_match(body, bb) { + continue; }; - - if SimplifyToIf.simplify(tcx, body, &mut patch, bb, typing_env).is_some() { - apply_patch = true; - continue; - } - if SimplifyToExp::default().simplify(tcx, body, &mut patch, bb, typing_env).is_some() { - apply_patch = true; - continue; - } + changed |= simplify_match(tcx, typing_env, body, bb) } - if apply_patch { - patch.apply(body); + if changed { simplify_cfg(tcx, body); } } @@ -54,224 +38,408 @@ fn is_required(&self) -> bool { } } -trait SimplifyMatch<'tcx> { - /// Simplifies a match statement, returning `Some` if the simplification succeeds, `None` - /// otherwise. Generic code is written here, and we generally don't need a custom - /// implementation. - fn simplify( - &mut self, - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - patch: &mut MirPatch<'tcx>, - switch_bb_idx: BasicBlock, - typing_env: ty::TypingEnv<'tcx>, - ) -> Option<()> { - let bbs = &body.basic_blocks; - let TerminatorKind::SwitchInt { discr, targets, .. } = - &bbs[switch_bb_idx].terminator().kind - else { - unreachable!(); - }; - - let discr_ty = discr.ty(body.local_decls(), tcx); - self.can_simplify(tcx, targets, typing_env, bbs, discr_ty)?; - - // Take ownership of items now that we know we can optimize. - let discr = discr.clone(); - - // Introduce a temporary for the discriminant value. - let source_info = bbs[switch_bb_idx].terminator().source_info; - let discr_local = patch.new_temp(discr_ty, source_info.span); - - let (_, first) = targets.iter().next().unwrap(); - let statement_index = bbs[switch_bb_idx].statements.len(); - let parent_end = Location { block: switch_bb_idx, statement_index }; - patch.add_statement(parent_end, StatementKind::StorageLive(discr_local)); - patch.add_assign(parent_end, Place::from(discr_local), Rvalue::Use(discr)); - self.new_stmts(tcx, targets, typing_env, patch, parent_end, bbs, discr_local, discr_ty); - patch.add_statement(parent_end, StatementKind::StorageDead(discr_local)); - patch.patch_terminator(switch_bb_idx, bbs[first].terminator().kind.clone()); - Some(()) - } - - /// Check that the BBs to be simplified satisfies all distinct and - /// that the terminator are the same. - /// There are also conditions for different ways of simplification. - fn can_simplify( - &mut self, - tcx: TyCtxt<'tcx>, - targets: &SwitchTargets, - typing_env: ty::TypingEnv<'tcx>, - bbs: &IndexSlice>, - discr_ty: Ty<'tcx>, - ) -> Option<()>; - - fn new_stmts( - &self, - tcx: TyCtxt<'tcx>, - targets: &SwitchTargets, - typing_env: ty::TypingEnv<'tcx>, - patch: &mut MirPatch<'tcx>, - parent_end: Location, - bbs: &IndexSlice>, - discr_local: Local, - discr_ty: Ty<'tcx>, - ); +struct SimplifyMatch<'tcx, 'a> { + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + patch: MirPatch<'tcx>, + body: &'a Body<'tcx>, + switch_bb: BasicBlock, + discr: &'a Operand<'tcx>, + discr_local: Option, + discr_ty: Ty<'tcx>, } -struct SimplifyToIf; +impl<'tcx, 'a> SimplifyMatch<'tcx, 'a> { + fn discr_local(&mut self) -> Local { + *self.discr_local.get_or_insert_with(|| { + // Introduce a temporary for the discriminant value. + let source_info = self.body.basic_blocks[self.switch_bb].terminator().source_info; + self.patch.new_temp(self.discr_ty, source_info.span) + }) + } -/// If a source block is found that switches between two blocks that are exactly -/// the same modulo const bool assignments (e.g., one assigns true another false -/// to the same place), merge a target block statements into the source block, -/// using Eq / Ne comparison with switch value where const bools value differ. -/// -/// For example: -/// -/// ```ignore (MIR) -/// bb0: { -/// switchInt(move _3) -> [42_isize: bb1, otherwise: bb2]; -/// } -/// -/// bb1: { -/// _2 = const true; -/// goto -> bb3; -/// } -/// -/// bb2: { -/// _2 = const false; -/// goto -> bb3; -/// } -/// ``` -/// -/// into: -/// -/// ```ignore (MIR) -/// bb0: { -/// _2 = Eq(move _3, const 42_isize); -/// goto -> bb3; -/// } -/// ``` -impl<'tcx> SimplifyMatch<'tcx> for SimplifyToIf { - #[instrument(level = "debug", skip(self, tcx), ret)] - fn can_simplify( + /// Unifies the assignments if all rvalues are constants and equal. + fn unify_if_equal_const( + &self, + dest: Place<'tcx>, + consts: &[(u128, &ConstOperand<'tcx>)], + otherwise: Option<&ConstOperand<'tcx>>, + ) -> Option> { + let (_, first_const, mut others) = split_first_case(consts, otherwise); + let first_scalar_int = first_const.const_.try_eval_scalar_int(self.tcx, self.typing_env)?; + if others.all(|const_| { + const_.const_.try_eval_scalar_int(self.tcx, self.typing_env) == Some(first_scalar_int) + }) { + Some(StatementKind::Assign(Box::new(( + dest, + Rvalue::Use(Operand::Constant(Box::new(first_const.clone()))), + )))) + } else { + None + } + } + + /// If a source block is found that switches between two blocks that are exactly + /// the same modulo const bool assignments (e.g., one assigns true another false + /// to the same place), unify a target block statements into the source block, + /// using Eq / Ne comparison with switch value where const bools value differ. + /// + /// For example: + /// + /// ```ignore (MIR) + /// bb0: { + /// switchInt(move _3) -> [42_isize: bb1, otherwise: bb2]; + /// } + /// + /// bb1: { + /// _2 = const true; + /// goto -> bb3; + /// } + /// + /// bb2: { + /// _2 = const false; + /// goto -> bb3; + /// } + /// ``` + /// + /// into: + /// + /// ```ignore (MIR) + /// bb0: { + /// _2 = Eq(move _3, const 42_isize); + /// goto -> bb3; + /// } + /// ``` + fn unify_by_eq_op( &mut self, - tcx: TyCtxt<'tcx>, - targets: &SwitchTargets, - typing_env: ty::TypingEnv<'tcx>, - bbs: &IndexSlice>, - _discr_ty: Ty<'tcx>, - ) -> Option<()> { - let (first, second) = match targets.all_targets() { - &[first, otherwise] => (first, otherwise), - &[first, second, otherwise] if bbs[otherwise].is_empty_unreachable() => (first, second), - _ => { - return None; - } + dest: Place<'tcx>, + consts: &[(u128, &ConstOperand<'tcx>)], + otherwise: Option<&ConstOperand<'tcx>>, + ) -> Option> { + // FIXME: extend to any case. + let (first_case, first_const, mut others) = split_first_case(consts, otherwise); + if !first_const.ty().is_bool() { + return None; + } + let first_bool = first_const.const_.try_eval_bool(self.tcx, self.typing_env)?; + if others.all(|const_| { + const_.const_.try_eval_bool(self.tcx, self.typing_env) == Some(!first_bool) + }) { + // Make value conditional on switch condition. + let size = + self.tcx.layout_of(self.typing_env.as_query_input(self.discr_ty)).unwrap().size; + let const_cmp = Operand::const_from_scalar( + self.tcx, + self.discr_ty, + rustc_const_eval::interpret::Scalar::from_uint(first_case, size), + rustc_span::DUMMY_SP, + ); + let op = if first_bool { BinOp::Eq } else { BinOp::Ne }; + let rval = Rvalue::BinaryOp( + op, + Box::new((Operand::Copy(Place::from(self.discr_local())), const_cmp)), + ); + Some(StatementKind::Assign(Box::new((dest, rval)))) + } else { + None + } + } + + /// Unifies the assignments if all rvalues can be cast from the discriminant value by IntToInt. + /// + /// For example: + /// + /// ```ignore (MIR) + /// bb0: { + /// switchInt(_1) -> [1: bb2, 2: bb3, 3: bb4, otherwise: bb1]; + /// } + /// + /// bb1: { + /// unreachable; + /// } + /// + /// bb2: { + /// _0 = const 1_i16; + /// goto -> bb5; + /// } + /// + /// bb3: { + /// _0 = const 2_i16; + /// goto -> bb5; + /// } + /// + /// bb4: { + /// _0 = const 3_i16; + /// goto -> bb5; + /// } + /// ``` + /// + /// into: + /// + /// ```ignore (MIR) + /// bb0: { + /// _0 = _1 as i16 (IntToInt); + /// goto -> bb5; + /// } + /// ``` + fn unify_by_int_to_int( + &mut self, + dest: Place<'tcx>, + consts: &[(u128, &ConstOperand<'tcx>)], + ) -> Option> { + let (_, first_const) = consts[0]; + if !first_const.ty().is_integral() { + return None; + } + let discr_layout = + self.tcx.layout_of(self.typing_env.as_query_input(self.discr_ty)).unwrap(); + if consts.iter().all(|&(case, const_)| { + let Some(scalar_int) = const_.const_.try_eval_scalar_int(self.tcx, self.typing_env) + else { + return false; + }; + can_cast(self.tcx, case, discr_layout, const_.ty(), scalar_int) + }) { + let operand = Operand::Copy(Place::from(self.discr_local())); + let rval = if first_const.ty() == self.discr_ty { + Rvalue::Use(operand) + } else { + Rvalue::Cast(CastKind::IntToInt, operand, first_const.ty()) + }; + Some(StatementKind::Assign(Box::new((dest, rval)))) + } else { + None + } + } + + /// This is primarily used to unify these copy statements that simplified the canonical enum clone method by GVN. + /// The GVN simplified + /// ```ignore (syntax-highlighting-only) + /// match a { + /// Foo::A(x) => Foo::A(*x), + /// Foo::B => Foo::B + /// } + /// ``` + /// to + /// ```ignore (syntax-highlighting-only) + /// match a { + /// Foo::A(_x) => a, // copy a + /// Foo::B => Foo::B + /// } + /// ``` + /// This will simplify into a copy statement. + fn unify_by_copy( + &self, + dest: Place<'tcx>, + rvals: &[(u128, &Rvalue<'tcx>)], + ) -> Option> { + let bbs = &self.body.basic_blocks; + // Check if the copy source matches the following pattern. + // _2 = discriminant(*_1); // "*_1" is the expected the copy source. + // switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1]; + let &Statement { + kind: StatementKind::Assign(box (discr_place, Rvalue::Discriminant(copy_src_place))), + .. + } = bbs[self.switch_bb].statements.last()? + else { + return None; + }; + if self.discr.place() != Some(discr_place) { + return None; + } + let src_ty = copy_src_place.ty(self.body.local_decls(), self.tcx); + if !src_ty.ty.is_enum() || src_ty.variant_index.is_some() { + return None; + } + let dest_ty = dest.ty(self.body.local_decls(), self.tcx); + if dest_ty.ty != src_ty.ty || dest_ty.variant_index.is_some() { + return None; + } + let ty::Adt(def, _) = dest_ty.ty.kind() else { + return None; }; - // We require that the possible target blocks all be distinct. - if first == second { - return None; - } - // Check that destinations are identical, and if not, then don't optimize this block - if bbs[first].terminator().kind != bbs[second].terminator().kind { - return None; - } - - // Check that blocks are assignments of consts to the same place or same statement, - // and match up 1-1, if not don't optimize this block. - let first_stmts = &bbs[first].statements; - let second_stmts = &bbs[second].statements; - if first_stmts.len() != second_stmts.len() { - return None; - } - for (f, s) in iter::zip(first_stmts, second_stmts) { - match (&f.kind, &s.kind) { - // If two statements are exactly the same, we can optimize. - (f_s, s_s) if f_s == s_s => {} - - // If two statements are const bool assignments to the same place, we can optimize. - ( - StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), - StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), - ) if lhs_f == lhs_s - && f_c.const_.ty().is_bool() - && s_c.const_.ty().is_bool() - && f_c.const_.try_eval_bool(tcx, typing_env).is_some() - && s_c.const_.try_eval_bool(tcx, typing_env).is_some() => {} - - // Otherwise we cannot optimize. Try another block. + for &(case, rvalue) in rvals.iter() { + match rvalue { + // Check if `_3 = const Foo::B` can be transformed to `_3 = copy *_1`. + Rvalue::Use(Operand::Constant(box constant)) + if let Const::Val(const_, ty) = constant.const_ => + { + let (ecx, op) = mk_eval_cx_for_const_val( + self.tcx.at(constant.span), + self.typing_env, + const_, + ty, + )?; + let variant = ecx.read_discriminant(&op).discard_err()?; + if !def.variants()[variant].fields.is_empty() { + return None; + } + let Discr { val, .. } = ty.discriminant_for_variant(self.tcx, variant)?; + if val != case { + return None; + } + } + Rvalue::Use(Operand::Copy(src_place)) if *src_place == copy_src_place => {} + // Check if `_3 = Foo::B` can be transformed to `_3 = copy *_1`. + Rvalue::Aggregate(box AggregateKind::Adt(_, variant_index, _, _, None), fields) + if fields.is_empty() + && let Some(Discr { val, .. }) = + src_ty.ty.discriminant_for_variant(self.tcx, *variant_index) + && val == case => {} _ => return None, } } - Some(()) + Some(StatementKind::Assign(Box::new((dest, Rvalue::Use(Operand::Copy(copy_src_place)))))) } - fn new_stmts( - &self, - tcx: TyCtxt<'tcx>, - targets: &SwitchTargets, - typing_env: ty::TypingEnv<'tcx>, - patch: &mut MirPatch<'tcx>, - parent_end: Location, - bbs: &IndexSlice>, - discr_local: Local, - discr_ty: Ty<'tcx>, - ) { - let ((val, first), second) = match (targets.all_targets(), targets.all_values()) { - (&[first, otherwise], &[val]) => ((val, first), otherwise), - (&[first, second, otherwise], &[val, _]) if bbs[otherwise].is_empty_unreachable() => { - ((val, first), second) + /// Returns a new statement if we can use the statement replace all statements. + fn try_unify_stmts( + &mut self, + index: usize, + stmts: &[(u128, &StatementKind<'tcx>)], + otherwise: Option<&StatementKind<'tcx>>, + ) -> Option> { + if let Some(new_stmt) = identical_stmts(stmts, otherwise) { + return Some(new_stmt); + } + + let (dest, rvals, otherwise) = candidate_assign(stmts, otherwise)?; + if let Some((consts, otherwise)) = candidate_const(&rvals, otherwise) { + if let Some(new_stmt) = self.unify_if_equal_const(dest, &consts, otherwise) { + return Some(new_stmt); } - _ => unreachable!(), - }; - - // We already checked that first and second are different blocks, - // and bb_idx has a different terminator from both of them. - let first = &bbs[first]; - let second = &bbs[second]; - for (f, s) in iter::zip(&first.statements, &second.statements) { - match (&f.kind, &s.kind) { - (f_s, s_s) if f_s == s_s => { - patch.add_statement(parent_end, f.kind.clone()); - } - - ( - StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), - StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(s_c)))), - ) => { - // From earlier loop we know that we are dealing with bool constants only: - let f_b = f_c.const_.try_eval_bool(tcx, typing_env).unwrap(); - let s_b = s_c.const_.try_eval_bool(tcx, typing_env).unwrap(); - if f_b == s_b { - // Same value in both blocks. Use statement as is. - patch.add_statement(parent_end, f.kind.clone()); - } else { - // Different value between blocks. Make value conditional on switch - // condition. - let size = tcx.layout_of(typing_env.as_query_input(discr_ty)).unwrap().size; - let const_cmp = Operand::const_from_scalar( - tcx, - discr_ty, - rustc_const_eval::interpret::Scalar::from_uint(val, size), - rustc_span::DUMMY_SP, - ); - let op = if f_b { BinOp::Eq } else { BinOp::Ne }; - let rhs = Rvalue::BinaryOp( - op, - Box::new((Operand::Copy(Place::from(discr_local)), const_cmp)), - ); - patch.add_assign(parent_end, *lhs, rhs); - } - } - - _ => unreachable!(), + if let Some(new_stmt) = self.unify_by_eq_op(dest, &consts, otherwise) { + return Some(new_stmt); + } + // Requires the otherwise is unreachable. + if otherwise.is_none() + && let Some(new_stmt) = self.unify_by_int_to_int(dest, &consts) + { + return Some(new_stmt); } } + + // We only know the first statement is safe to introduce new dereferences. + if index == 0 + // We cannot create overlapping assignments. + && dest.is_stable_offset() + // Requires the otherwise is unreachable. + && otherwise.is_none() + && let Some(new_stmt) = self.unify_by_copy(dest, &rvals) + { + return Some(new_stmt); + } + None } } +/// Returns the first case target if all targets have an equal number of statements and identical destination. +fn candidate_match<'tcx>(body: &Body<'tcx>, switch_bb: BasicBlock) -> bool { + use itertools::Itertools; + let targets = match &body.basic_blocks[switch_bb].terminator().kind { + TerminatorKind::SwitchInt { + discr: Operand::Copy(_) | Operand::Move(_), targets, .. + } => targets, + // Only optimize switch int statements + _ => return false, + }; + // We require that the possible target blocks don't contain this block. + if targets.all_targets().contains(&switch_bb) { + return false; + } + // We require that the possible target blocks all be distinct. + if !targets.is_distinct() { + return false; + } + // Check that destinations are identical, and if not, then don't optimize this block + targets + .all_targets() + .iter() + .map(|&bb| &body.basic_blocks[bb]) + .filter(|bb| !bb.is_empty_unreachable()) + .map(|bb| (bb.statements.len(), &bb.terminator().kind)) + .all_equal() +} + +fn simplify_match<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + body: &mut Body<'tcx>, + switch_bb: BasicBlock, +) -> bool { + let (discr, targets) = match &body.basic_blocks[switch_bb].terminator().kind { + TerminatorKind::SwitchInt { discr, targets, .. } => (discr, targets), + _ => unreachable!(), + }; + let mut simplify_match = SimplifyMatch { + tcx, + typing_env, + patch: MirPatch::new(body), + body, + switch_bb, + discr, + discr_local: None, + discr_ty: discr.ty(body.local_decls(), tcx), + }; + let reachable_cases: Vec<_> = + targets.iter().filter(|&(_, bb)| !body.basic_blocks[bb].is_empty_unreachable()).collect(); + let mut new_stmts = Vec::new(); + let otherwise = if body.basic_blocks[targets.otherwise()].is_empty_unreachable() { + None + } else { + Some(targets.otherwise()) + }; + // We can patch the terminator to goto because there is a single target. + match (reachable_cases.len(), otherwise.is_none()) { + (1, true) | (0, false) => { + let mut patch = simplify_match.patch; + remove_successors_from_switch(tcx, switch_bb, body, &mut patch, |bb| { + body.basic_blocks[bb].is_empty_unreachable() + }); + patch.apply(body); + return true; + } + _ => {} + } + let Some(&(_, first_case_bb)) = reachable_cases.first() else { + return false; + }; + let stmt_len = body.basic_blocks[first_case_bb].statements.len(); + let mut cases = Vec::with_capacity(stmt_len); + // Check at each position in the basic blocks whether these statements can be unified. + for index in 0..stmt_len { + cases.clear(); + let otherwise = otherwise.map(|bb| &body.basic_blocks[bb].statements[index].kind); + for &(case, bb) in &reachable_cases { + cases.push((case, &body.basic_blocks[bb].statements[index].kind)); + } + let Some(new_stmt) = simplify_match.try_unify_stmts(index, &cases, otherwise) else { + return false; + }; + new_stmts.push(new_stmt); + } + // Take ownership of items now that we know we can optimize. + let discr = discr.clone(); + + let statement_index = body.basic_blocks[switch_bb].statements.len(); + let parent_end = Location { block: switch_bb, statement_index }; + let mut patch = simplify_match.patch; + if let Some(discr_local) = simplify_match.discr_local { + patch.add_statement(parent_end, StatementKind::StorageLive(discr_local)); + patch.add_assign(parent_end, Place::from(discr_local), Rvalue::Use(discr)); + } + for new_stmt in new_stmts { + patch.add_statement(parent_end, new_stmt); + } + if let Some(discr_local) = simplify_match.discr_local { + patch.add_statement(parent_end, StatementKind::StorageDead(discr_local)); + } + patch.patch_terminator(switch_bb, body.basic_blocks[first_case_bb].terminator().kind.clone()); + patch.apply(body); + true +} + /// Check if the cast constant using `IntToInt` is equal to the target constant. fn can_cast( tcx: TyCtxt<'_>, @@ -298,234 +466,77 @@ fn can_cast( cast_scalar == target_scalar } -#[derive(Default)] -struct SimplifyToExp { - transform_kinds: Vec, -} - -#[derive(Clone, Copy, Debug)] -enum ExpectedTransformKind<'a, 'tcx> { - /// Identical statements. - Same(&'a StatementKind<'tcx>), - /// Assignment statements have the same value. - SameByEq { place: &'a Place<'tcx>, ty: Ty<'tcx>, scalar: ScalarInt }, - /// Enum variant comparison type. - Cast { place: &'a Place<'tcx>, ty: Ty<'tcx> }, -} - -enum TransformKind { - Same, - Cast, -} - -impl From> for TransformKind { - fn from(compare_type: ExpectedTransformKind<'_, '_>) -> Self { - match compare_type { - ExpectedTransformKind::Same(_) => TransformKind::Same, - ExpectedTransformKind::SameByEq { .. } => TransformKind::Same, - ExpectedTransformKind::Cast { .. } => TransformKind::Cast, - } - } -} - -/// If we find that the value of match is the same as the assignment, -/// merge a target block statements into the source block, -/// using cast to transform different integer types. -/// -/// For example: -/// -/// ```ignore (MIR) -/// bb0: { -/// switchInt(_1) -> [1: bb2, 2: bb3, 3: bb4, otherwise: bb1]; -/// } -/// -/// bb1: { -/// unreachable; -/// } -/// -/// bb2: { -/// _0 = const 1_i16; -/// goto -> bb5; -/// } -/// -/// bb3: { -/// _0 = const 2_i16; -/// goto -> bb5; -/// } -/// -/// bb4: { -/// _0 = const 3_i16; -/// goto -> bb5; -/// } -/// ``` -/// -/// into: -/// -/// ```ignore (MIR) -/// bb0: { -/// _0 = _3 as i16 (IntToInt); -/// goto -> bb5; -/// } -/// ``` -impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { - #[instrument(level = "debug", skip(self, tcx), ret)] - fn can_simplify( - &mut self, - tcx: TyCtxt<'tcx>, - targets: &SwitchTargets, - typing_env: ty::TypingEnv<'tcx>, - bbs: &IndexSlice>, - discr_ty: Ty<'tcx>, - ) -> Option<()> { - if targets.iter().len() < 2 || targets.iter().len() > 64 { +fn candidate_assign<'tcx, 'a>( + stmts: &'a [(u128, &'a StatementKind<'tcx>)], + otherwise: Option<&'a StatementKind<'tcx>>, +) -> Option<(Place<'tcx>, Vec<(u128, &'a Rvalue<'tcx>)>, Option<&'a Rvalue<'tcx>>)> { + let (_, first_stmt) = stmts[0]; + let (dest, _) = first_stmt.as_assign()?; + let otherwise = if let Some(otherwise) = otherwise { + let Some((otherwise_dest, rval)) = otherwise.as_assign() else { + return None; + }; + if otherwise_dest != dest { return None; } - // We require that the possible target blocks all be distinct. - if !targets.is_distinct() { - return None; - } - if !bbs[targets.otherwise()].is_empty_unreachable() { - return None; - } - let mut target_iter = targets.iter(); - let (first_case_val, first_target) = target_iter.next().unwrap(); - let first_terminator_kind = &bbs[first_target].terminator().kind; - // Check that destinations are identical, and if not, then don't optimize this block - if !targets - .iter() - .all(|(_, other_target)| first_terminator_kind == &bbs[other_target].terminator().kind) - { - return None; - } - - let discr_layout = tcx.layout_of(typing_env.as_query_input(discr_ty)).unwrap(); - let first_stmts = &bbs[first_target].statements; - let (second_case_val, second_target) = target_iter.next().unwrap(); - let second_stmts = &bbs[second_target].statements; - if first_stmts.len() != second_stmts.len() { - return None; - } - - // We first compare the two branches, and then the other branches need to fulfill the same - // conditions. - let mut expected_transform_kinds = Vec::new(); - for (f, s) in iter::zip(first_stmts, second_stmts) { - let compare_type = match (&f.kind, &s.kind) { - // If two statements are exactly the same, we can optimize. - (f_s, s_s) if f_s == s_s => ExpectedTransformKind::Same(f_s), - - // If two statements are assignments with the match values to the same place, we - // can optimize. - ( - StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), - StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), - ) if lhs_f == lhs_s - && f_c.const_.ty() == s_c.const_.ty() - && f_c.const_.ty().is_integral() => - { - match ( - f_c.const_.try_eval_scalar_int(tcx, typing_env), - s_c.const_.try_eval_scalar_int(tcx, typing_env), - ) { - (Some(f), Some(s)) if f == s => ExpectedTransformKind::SameByEq { - place: lhs_f, - ty: f_c.const_.ty(), - scalar: f, - }, - // Enum variants can also be simplified to an assignment statement, - // if we can use `IntToInt` cast to get an equal value. - (Some(f), Some(s)) - if (can_cast( - tcx, - first_case_val, - discr_layout, - f_c.const_.ty(), - f, - ) && can_cast( - tcx, - second_case_val, - discr_layout, - f_c.const_.ty(), - s, - )) => - { - ExpectedTransformKind::Cast { place: lhs_f, ty: f_c.const_.ty() } - } - _ => { - return None; - } - } - } - - // Otherwise we cannot optimize. Try another block. - _ => return None, - }; - expected_transform_kinds.push(compare_type); - } - - // All remaining BBs need to fulfill the same pattern as the two BBs from the previous step. - for (other_val, other_target) in target_iter { - let other_stmts = &bbs[other_target].statements; - if expected_transform_kinds.len() != other_stmts.len() { + Some(rval) + } else { + None + }; + let rvals = stmts + .into_iter() + .map(|&(case, stmt)| { + let (other_dest, rval) = stmt.as_assign()?; + if other_dest != dest { return None; } - for (f, s) in iter::zip(&expected_transform_kinds, other_stmts) { - match (*f, &s.kind) { - (ExpectedTransformKind::Same(f_s), s_s) if f_s == s_s => {} - ( - ExpectedTransformKind::SameByEq { place: lhs_f, ty: f_ty, scalar }, - StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), - ) if lhs_f == lhs_s - && s_c.const_.ty() == f_ty - && s_c.const_.try_eval_scalar_int(tcx, typing_env) == Some(scalar) => {} - ( - ExpectedTransformKind::Cast { place: lhs_f, ty: f_ty }, - StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), - ) if let Some(f) = s_c.const_.try_eval_scalar_int(tcx, typing_env) - && lhs_f == lhs_s - && s_c.const_.ty() == f_ty - && can_cast(tcx, other_val, discr_layout, f_ty, f) => {} - _ => return None, - } - } - } - self.transform_kinds = expected_transform_kinds.into_iter().map(|c| c.into()).collect(); - Some(()) - } - - fn new_stmts( - &self, - _tcx: TyCtxt<'tcx>, - targets: &SwitchTargets, - _typing_env: ty::TypingEnv<'tcx>, - patch: &mut MirPatch<'tcx>, - parent_end: Location, - bbs: &IndexSlice>, - discr_local: Local, - discr_ty: Ty<'tcx>, - ) { - let (_, first) = targets.iter().next().unwrap(); - let first = &bbs[first]; - - for (t, s) in iter::zip(&self.transform_kinds, &first.statements) { - match (t, &s.kind) { - (TransformKind::Same, _) => { - patch.add_statement(parent_end, s.kind.clone()); - } - ( - TransformKind::Cast, - StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), - ) => { - let operand = Operand::Copy(Place::from(discr_local)); - let r_val = if f_c.const_.ty() == discr_ty { - Rvalue::Use(operand) - } else { - Rvalue::Cast(CastKind::IntToInt, operand, f_c.const_.ty()) - }; - patch.add_assign(parent_end, *lhs, r_val); - } - _ => unreachable!(), - } - } - } + Some((case, rval)) + }) + .try_collect()?; + Some((*dest, rvals, otherwise)) +} + +// Returns all ConstOperands if all Rvalues are ConstOperands. +fn candidate_const<'tcx, 'a>( + rvals: &'a [(u128, &'a Rvalue<'tcx>)], + otherwise: Option<&'a Rvalue<'tcx>>, +) -> Option<(Vec<(u128, &'a ConstOperand<'tcx>)>, Option<&'a ConstOperand<'tcx>>)> { + let otherwise = if let Some(otherwise) = otherwise { + let Rvalue::Use(Operand::Constant(box const_)) = otherwise else { + return None; + }; + Some(const_) + } else { + None + }; + let consts = rvals + .into_iter() + .map(|&(case, rval)| { + let Rvalue::Use(Operand::Constant(box const_)) = rval else { return None }; + Some((case, const_)) + }) + .try_collect()?; + Some((consts, otherwise)) +} + +// Returns the first case and others (including otherwise if present). +fn split_first_case<'a, T>( + stmts: &'a [(u128, &'a T)], + otherwise: Option<&'a T>, +) -> (u128, &'a T, impl Iterator) { + let (first_case, first) = stmts[0]; + (first_case, first, stmts[1..].into_iter().map(|&(_, val)| val).chain(otherwise)) +} + +// If all statements are identical, we can optimize. +fn identical_stmts<'tcx>( + stmts: &[(u128, &StatementKind<'tcx>)], + otherwise: Option<&StatementKind<'tcx>>, +) -> Option> { + use itertools::Itertools; + let (_, first_stmt, others) = split_first_case(stmts, otherwise); + if std::iter::once(first_stmt).chain(others).all_equal() { + return Some(first_stmt.clone()); + } + None } diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index e225c31881f8..a08136416543 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; +use std::sync::atomic::Ordering; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_middle::mir::{Body, MirDumper, MirPhase, RuntimePhase}; @@ -285,6 +286,19 @@ fn run_passes_inner<'tcx>( continue; }; + if is_optimization_stage(body, phase_change, optimizations) + && let Some(limit) = &tcx.sess.opts.unstable_opts.mir_opt_bisect_limit + { + if limited_by_opt_bisect( + tcx, + tcx.def_path_debug_str(body.source.def_id()), + *limit, + *pass, + ) { + continue; + } + } + let dumper = if pass.is_mir_dump_enabled() && let Some(dumper) = MirDumper::new(tcx, pass_name, body) { @@ -356,3 +370,46 @@ pub(super) fn dump_mir_for_phase_change<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tc dumper.set_show_pass_num().set_disambiguator(&"after").dump_mir(body) } } + +fn is_optimization_stage( + body: &Body<'_>, + phase_change: Option, + optimizations: Optimizations, +) -> bool { + optimizations == Optimizations::Allowed + && body.phase == MirPhase::Runtime(RuntimePhase::PostCleanup) + && phase_change == Some(MirPhase::Runtime(RuntimePhase::Optimized)) +} + +fn limited_by_opt_bisect<'tcx, P>( + tcx: TyCtxt<'tcx>, + def_path: String, + limit: usize, + pass: &P, +) -> bool +where + P: MirPass<'tcx> + ?Sized, +{ + let current_opt_bisect_count = + tcx.sess.mir_opt_bisect_eval_count.fetch_add(1, Ordering::Relaxed); + + let can_run = current_opt_bisect_count < limit; + + if can_run { + eprintln!( + "BISECT: running pass ({}) {} on {}", + current_opt_bisect_count + 1, + pass.name(), + def_path + ); + } else { + eprintln!( + "BISECT: NOT running pass ({}) {} on {}", + current_opt_bisect_count + 1, + pass.name(), + def_path + ); + } + + !can_run +} diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 3d1537b95efa..3f5a630a2174 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -11,11 +11,10 @@ //! MIR. use std::cell::Cell; -use std::{cmp, iter, mem}; +use std::{assert_matches, cmp, iter, mem}; use either::{Left, Right}; use rustc_const_eval::check_consts::{ConstCx, qualifs}; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -449,8 +448,6 @@ fn validate_rvalue(&mut self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> self.validate_operand(operand)?; } - Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), - Rvalue::UnaryOp(op, operand) => { match op { // These operations can never fail. diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index 721a976eb910..0c5d8913a807 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -245,7 +245,7 @@ fn compute_replacement<'tcx>( debug!(?rvalue); match rvalue { // This is a copy, just use the value we have in store for the previous one. - // As we are visiting in `assignment_order`, ie. reverse postorder, `rhs` should + // As we are visiting in `assignment_order`, i.e. reverse postorder, `rhs` should // have been visited before. Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) => { if let Some(rhs) = place.as_local() diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index cc1cb3d4f3ff..1abbfed1a822 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -1,7 +1,6 @@ -use std::{fmt, iter}; +use std::{assert_matches, fmt, iter}; use rustc_abi::{ExternAbi, FIRST_VARIANT, FieldIdx, VariantIdx}; -use rustc_data_structures::assert_matches; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; diff --git a/compiler/rustc_mir_transform/src/single_use_consts.rs b/compiler/rustc_mir_transform/src/single_use_consts.rs index 02caa92ad3fc..6d33736d64ec 100644 --- a/compiler/rustc_mir_transform/src/single_use_consts.rs +++ b/compiler/rustc_mir_transform/src/single_use_consts.rs @@ -85,7 +85,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } fn is_required(&self) -> bool { - true + false } } diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index a56f04cf4842..4f9f2e5fabb9 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -25,7 +25,7 @@ pub(super) struct SsaLocals { assignment_order: Vec, /// Copy equivalence classes between locals. See `copy_classes` for documentation. copy_classes: IndexVec, - /// Number of "direct" uses of each local, ie. uses that are not dereferences. + /// Number of "direct" uses of each local, i.e. uses that are not dereferences. /// We ignore non-uses (Storage statements, debuginfo). direct_uses: IndexVec, /// Set of SSA locals that are immutably borrowed. @@ -314,7 +314,7 @@ fn compute_copy_classes(ssa: &mut SsaLocals, body: &Body<'_>) { continue; } - // We visit in `assignment_order`, ie. reverse post-order, so `rhs` has been + // We visit in `assignment_order`, i.e. reverse post-order, so `rhs` has been // visited before `local`, and we just have to copy the representing local. let head = copies[rhs]; diff --git a/compiler/rustc_mir_transform/src/trivial_const.rs b/compiler/rustc_mir_transform/src/trivial_const.rs index 3b80ae30be42..529c8c65acfc 100644 --- a/compiler/rustc_mir_transform/src/trivial_const.rs +++ b/compiler/rustc_mir_transform/src/trivial_const.rs @@ -52,7 +52,10 @@ pub(crate) fn trivial_const<'a, 'tcx: 'a, F, B>( F: FnOnce() -> B, B: Deref>, { - if !matches!(tcx.def_kind(def), DefKind::AssocConst | DefKind::Const | DefKind::AnonConst) { + if !matches!( + tcx.def_kind(def), + DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst + ) { return None; } diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index c417a9272f2a..ddc33eafc913 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -35,7 +35,9 @@ fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } // Try to remove unreachable targets from the switch. TerminatorKind::SwitchInt { .. } => { - remove_successors_from_switch(tcx, bb, &unreachable_blocks, body, &mut patch) + remove_successors_from_switch(tcx, bb, body, &mut patch, |bb| { + unreachable_blocks.contains(&bb) + }) } _ => false, }; @@ -60,20 +62,18 @@ fn is_required(&self) -> bool { } /// Return whether the current terminator is fully unreachable. -fn remove_successors_from_switch<'tcx>( +pub(crate) fn remove_successors_from_switch<'tcx>( tcx: TyCtxt<'tcx>, bb: BasicBlock, - unreachable_blocks: &FxHashSet, body: &Body<'tcx>, patch: &mut MirPatch<'tcx>, + is_unreachable_block: impl Fn(BasicBlock) -> bool, ) -> bool { let terminator = body.basic_blocks[bb].terminator(); let TerminatorKind::SwitchInt { discr, targets } = &terminator.kind else { bug!() }; let source_info = terminator.source_info; let location = body.terminator_loc(bb); - let is_unreachable = |bb| unreachable_blocks.contains(&bb); - // If there are multiple targets, we want to keep information about reachability for codegen. // For example (see tests/codegen-llvm/match-optimizes-away.rs) // @@ -116,10 +116,10 @@ fn remove_successors_from_switch<'tcx>( }; let otherwise = targets.otherwise(); - let otherwise_unreachable = is_unreachable(otherwise); + let otherwise_unreachable = is_unreachable_block(otherwise); let reachable_iter = targets.iter().filter(|&(value, bb)| { - let is_unreachable = is_unreachable(bb); + let is_unreachable = is_unreachable_block(bb); // We remove this target from the switch, so record the inequality using `Assume`. if is_unreachable && !otherwise_unreachable { add_assumption(BinOp::Ne, value); diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 1afdb4639a0c..656b74e15f72 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -440,7 +440,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location } } - if let ty::FnDef(did, ..) = func.ty(&self.body.local_decls, self.tcx).kind() + if let ty::FnDef(did, ..) = *func.ty(&self.body.local_decls, self.tcx).kind() && self.body.phase >= MirPhase::Runtime(RuntimePhase::Optimized) && matches!(self.tcx.codegen_fn_attrs(did).inline, InlineAttr::Force { .. }) { @@ -1259,14 +1259,6 @@ macro_rules! check_kinds { } } } - Rvalue::ShallowInitBox(operand, _) => { - if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { - self.fail(location, format!("ShallowInitBox after ElaborateBoxDerefs")) - } - - let a = operand.ty(&self.body.local_decls, self.tcx); - check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..)); - } Rvalue::Cast(kind, operand, target_type) => { let op_ty = operand.ty(self.body, self.tcx); match kind { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 4f6e2cc00516..3aa55cc8eb9f 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1559,7 +1559,7 @@ fn process_item(&mut self, id: hir::ItemId) { debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id)); self.output.push(dummy_spanned(MonoItem::Static(def_id))); } - DefKind::Const => { + DefKind::Const { .. } => { // Const items only generate mono items if they are actually used somewhere. // Just declaring them is insufficient. diff --git a/compiler/rustc_monomorphize/src/collector/autodiff.rs b/compiler/rustc_monomorphize/src/collector/autodiff.rs index e3646596e75e..67d4b8c8afff 100644 --- a/compiler/rustc_monomorphize/src/collector/autodiff.rs +++ b/compiler/rustc_monomorphize/src/collector/autodiff.rs @@ -28,13 +28,13 @@ fn collect_autodiff_fn_from_arg<'tcx>( output: &mut MonoItems<'tcx>, ) { let (instance, span) = match arg.kind() { - ty::GenericArgKind::Type(ty) => match ty.kind() { + ty::GenericArgKind::Type(ty) => match *ty.kind() { ty::FnDef(def_id, substs) => { let span = tcx.def_span(def_id); let instance = ty::Instance::expect_resolve( tcx, ty::TypingEnv::non_body_analysis(tcx, def_id), - *def_id, + def_id, substs, span, ); diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 62a8743873bd..27705a9837ad 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -1,4 +1,4 @@ -use rustc_macros::{Diagnostic, LintDiagnostic}; +use rustc_macros::Diagnostic; use rustc_middle::ty::{Instance, Ty}; use rustc_span::{Span, Symbol}; @@ -24,7 +24,7 @@ pub(crate) struct NoOptimizedMir { pub instance: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("moving {$size} bytes")] #[note( "the current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = \"...\"]`" diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 51753563b67c..ae97bf830d8c 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -1,6 +1,5 @@ // tidy-alphabetical-start #![feature(file_buffered)] -#![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(once_cell_get_mut)] // tidy-alphabetical-end @@ -37,7 +36,7 @@ fn custom_coerce_unsize_info<'tcx>( Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData { impl_def_id, .. - })) => Ok(tcx.coerce_unsized_info(impl_def_id)?.custom_kind.unwrap()), + })) => Ok(tcx.coerce_unsized_info(*impl_def_id)?.custom_kind.unwrap()), impl_source => { bug!( "invalid `CoerceUnsized` from {source_ty} to {target_ty}: impl_source: {:?}", diff --git a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs index 0adf1b089b53..a8f3104c0d98 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs @@ -190,7 +190,7 @@ fn lint_large_assignment( } fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option { - for impl_def_id in tcx.inherent_impls(def_id) { + for &impl_def_id in tcx.inherent_impls(def_id) { if let Some(new) = tcx.associated_items(impl_def_id).find_by_ident_and_kind( tcx, fn_ident, diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs index 31fce0601697..83f3bdf01dc8 100644 --- a/compiler/rustc_next_trait_solver/src/placeholder.rs +++ b/compiler/rustc_next_trait_solver/src/placeholder.rs @@ -32,8 +32,10 @@ impl<'a, Infcx, I> BoundVarReplacer<'a, Infcx, I> Infcx: InferCtxtLike, I: Interner, { - /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that - /// use a binding level above `universe_indices.len()`, we fail. + /// Returns a type with all bound vars replaced by placeholders, + /// together with mappings from the new placeholders back to the original variable. + /// + /// Panics if there are any bound vars that use a binding level above `universe_indices.len()`. pub fn replace_bound_vars>( infcx: &'a Infcx, universe_indices: &'a mut Vec>, 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 63f246db8a5f..038168ca638b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -348,6 +348,11 @@ fn consider_structural_builtin_unsize_candidates( ecx: &mut EvalCtxt<'_, D>, goal: Goal, ) -> Vec>; + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution>; } /// Allows callers of `assemble_and_evaluate_candidates` to choose whether to limit @@ -617,6 +622,7 @@ fn assemble_builtin_impl_candidates>( Some(SolverTraitLangItem::BikeshedGuaranteedNoDrop) => { G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal) } + Some(SolverTraitLangItem::Field) => G::consider_builtin_field_candidate(self, goal), _ => Err(NoSolution), } }; diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 2837b8565f60..4da76b88b5de 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -430,6 +430,13 @@ fn consider_structural_builtin_unsize_candidates( ) -> Vec> { unreachable!("Unsize is not const") } + + fn consider_builtin_field_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<::Interner, Self>, + ) -> Result::Interner>, NoSolution> { + unreachable!("Field is not const") + } } impl EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 52a1479d70a1..58bd7cf663d9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -39,9 +39,9 @@ /// /// We previously used `cx.recursion_limit().0.checked_ilog2().unwrap_or(0)` for this. /// However, it feels unlikely that uncreasing the recursion limit by a power of two -/// to get one more iteration is every useful or desirable. We now instead used a constant +/// to get one more iteration is ever useful or desirable. We now instead used a constant /// here. If there ever ends up some use-cases where a bigger number of fixpoint iterations -/// is required, we can add a new attribute for that or revert this to be dependant on the +/// is required, we can add a new attribute for that or revert this to be dependent on the /// recursion limit again. However, this feels very unlikely. const FIXPOINT_STEP_LIMIT: usize = 8; diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 70c28421c57e..13f2ad8f82eb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -7,7 +7,7 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; -use rustc_type_ir::{self as ty, Interner, NormalizesTo, PredicateKind, Upcast as _}; +use rustc_type_ir::{self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Upcast as _}; use tracing::instrument; use crate::delegate::SolverDelegate; @@ -950,6 +950,29 @@ fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( ) -> Result, NoSolution> { unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal) } + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + let self_ty = goal.predicate.self_ty(); + let ty::Adt(def, args) = self_ty.kind() else { + return Err(NoSolution); + }; + let Some(FieldInfo { base, ty, .. }) = def.field_representing_type_info(ecx.cx(), args) + else { + return Err(NoSolution); + }; + let ty = match ecx.cx().as_lang_item(goal.predicate.def_id()) { + Some(SolverLangItem::FieldBase) => base, + Some(SolverLangItem::FieldType) => ty, + _ => panic!("unexpected associated type {:?} in `Field`", goal.predicate), + }; + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.instantiate_normalizes_to_term(goal, ty.into()); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } } impl EvalCtxt<'_, D> 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 dd012a5146e6..6589a12c4cc2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -8,7 +8,7 @@ AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, SizedTraitKind, }; use rustc_type_ir::{ - self as ty, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef, + self as ty, FieldInfo, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode, Upcast as _, elaborate, }; use tracing::{debug, instrument, trace}; @@ -844,6 +844,54 @@ fn consider_structural_builtin_unsize_candidates( } }) } + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + 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()), + }, + ); + 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) + } + } } /// Small helper function to change the `def_id` of a trait predicate - this is not normally diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 0d8d403625a7..f4f718292205 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -11,7 +11,7 @@ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, Subdiagnostic, SuggestionStyle, msg, }; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; use rustc_span::{Ident, Span, Symbol}; @@ -1016,7 +1016,6 @@ pub(crate) struct LeadingPlusNotSupported { pub(crate) struct ParenthesesWithStructFields { #[primary_span] pub span: Span, - pub r#type: Path, #[subdiagnostic] pub braces_for_struct: BracesForStructLiteral, #[subdiagnostic] @@ -1029,6 +1028,7 @@ pub(crate) struct ParenthesesWithStructFields { applicability = "maybe-incorrect" )] pub(crate) struct BracesForStructLiteral { + pub r#type: Path, #[suggestion_part(code = " {{ ")] pub first: Span, #[suggestion_part(code = " }}")] @@ -1041,6 +1041,7 @@ pub(crate) struct BracesForStructLiteral { applicability = "maybe-incorrect" )] pub(crate) struct NoFieldsForFnCall { + pub r#type: Path, #[suggestion_part(code = "")] pub fields: Vec, } @@ -1178,6 +1179,7 @@ pub(crate) enum MatchArmBodyWithoutBracesSugg { left: Span, #[suggestion_part(code = " }}")] right: Span, + num_statements: usize, }, #[suggestion( "replace `;` with `,` to end a `match` arm expression", @@ -1268,6 +1270,26 @@ pub(crate) struct IncorrectVisibilityRestriction { pub inner_str: String, } +#[derive(Diagnostic)] +#[diag("incorrect `impl` restriction")] +#[help( + "some possible `impl` restrictions are: + `impl(crate)`: can only be implemented in the current crate + `impl(super)`: can only be implemented in the parent module + `impl(self)`: can only be implemented in current module + `impl(in path::to::module)`: can only be implemented in the specified path" +)] +pub(crate) struct IncorrectImplRestriction { + #[primary_span] + #[suggestion( + "help: use `in` to restrict implementations to the path `{$inner_str}`", + code = "in {inner_str}", + applicability = "machine-applicable" + )] + pub span: Span, + pub inner_str: String, +} + #[derive(Diagnostic)] #[diag(" ... else {\"{\"} ... {\"}\"} is not allowed")] pub(crate) struct AssignmentElseNotAllowed { @@ -1514,7 +1536,6 @@ pub(crate) struct HelpIdentifierStartsWithNumber { pub(crate) struct ExpectedSemi { pub span: Span, pub token: Token, - pub unexpected_token_label: Option, pub sugg: ExpectedSemiSugg, } @@ -2203,6 +2224,15 @@ pub(crate) struct DefaultNotFollowedByItem { pub span: Span, } +#[derive(Diagnostic)] +#[diag("`final` is not followed by an item")] +#[note("only associated functions in traits may be prefixed by `final`")] +pub(crate) struct FinalNotFollowedByItem { + #[primary_span] + #[label("the `final` qualifier")] + pub span: Span, +} + #[derive(Diagnostic)] pub(crate) enum MissingKeywordForItemDefinition { #[diag("missing `enum` for enum definition")] @@ -2394,6 +2424,14 @@ pub(crate) struct TraitAliasCannotBeUnsafe { pub span: Span, } +#[derive(Diagnostic)] +#[diag("trait aliases cannot be `impl`-restricted")] +pub(crate) struct TraitAliasCannotBeImplRestricted { + #[primary_span] + #[label("trait aliases cannot be `impl`-restricted")] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("associated `static` items are not allowed")] pub(crate) struct AssociatedStaticItemNotAllowed { @@ -3168,6 +3206,7 @@ pub(crate) struct UnexpectedVertVertInPattern { pub(crate) struct TrailingVertSuggestion { #[primary_span] pub span: Span, + pub token: Token, } #[derive(Diagnostic)] @@ -3787,6 +3826,17 @@ pub(crate) struct RecoverImportAsUse { pub token_name: String, } +#[derive(Diagnostic)] +#[diag("{$article} {$descr} cannot be `final`")] +#[note("only associated functions in traits can be `final`")] +pub(crate) struct InappropriateFinal { + #[primary_span] + #[label("`final` because of this")] + pub span: Span, + pub article: &'static str, + pub descr: &'static str, +} + #[derive(Diagnostic)] #[diag("expected `::`, found `:`")] #[note("import paths are delimited using `::`")] @@ -4256,7 +4306,7 @@ pub(crate) struct ExpectedRegisterClassOrExplicitRegister { pub(crate) span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unicode codepoint changing visible direction of text present in {$label}")] #[note( "these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen" @@ -4339,7 +4389,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("missing pattern for `...` argument")] pub(crate) struct VarargsWithoutPattern { #[suggestion( diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 31506ae38fa0..6b8d6baac945 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -1,12 +1,10 @@ //! The main parser interface. // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] #![cfg_attr(test, feature(iter_order_by))] #![feature(box_patterns)] #![feature(debug_closure_helpers)] #![feature(default_field_values)] -#![feature(if_let_guard)] #![feature(iter_intersperse)] #![recursion_limit = "256"] // tidy-alphabetical-end @@ -118,21 +116,14 @@ pub fn new_parser_from_file<'a>( let msg = format!("couldn't read `{}`: {}", path.display(), e); let mut err = psess.dcx().struct_fatal(msg); if let Ok(contents) = std::fs::read(path) - && let Err(utf8err) = String::from_utf8(contents.clone()) + && let Err(utf8err) = std::str::from_utf8(&contents) { - utf8_error( - sm, - &path.display().to_string(), - sp, - &mut err, - utf8err.utf8_error(), - &contents, - ); + utf8_error(sm, &path.display().to_string(), sp, &mut err, utf8err, &contents); } if let Some(sp) = sp { err.span(sp); } - err.emit(); + err.emit() }); new_parser_from_source_file(psess, source_file, strip_tokens) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index b8672f6cafdf..284e651d94ad 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -236,7 +236,7 @@ fn emit(self, err: &mut Diag<'_>) { } fn emit_verbose(self, err: &mut Diag<'_>) { - err.multipart_suggestion_verbose(self.msg, self.patches, self.applicability); + err.multipart_suggestion(self.msg, self.patches, self.applicability); } } @@ -389,7 +389,7 @@ pub(super) fn expected_ident_found( && let Ok(snippet) = self.psess.source_map().span_to_snippet(generic.span) { - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!("place the generic parameter name after the {ident_name} name"), vec![ (self.token.span.shrink_to_hi(), snippet), @@ -647,7 +647,7 @@ fn tokens_to_string(tokens: &[TokenType]) -> String { // 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::c, IdentIsRaw::No) + if ((self.prev_token == TokenKind::Ident(sym::character('c'), IdentIsRaw::No) && matches!(&self.token.kind, TokenKind::Literal(token::Lit { kind: token::Str, .. }))) || (self.prev_token == TokenKind::Ident(sym::cr, IdentIsRaw::No) && matches!( @@ -1056,7 +1056,7 @@ pub(super) fn recover_closure_body( // and recover. self.eat_to_tokens(&[exp!(CloseParen), exp!(Comma)]); - err.multipart_suggestion_verbose( + err.multipart_suggestion( "you might have meant to open the body of the closure", vec![ (prev.span.shrink_to_hi(), " {".to_string()), @@ -1069,7 +1069,7 @@ pub(super) fn recover_closure_body( _ if token.kind != token::OpenBrace => { // We don't have a heuristic to correctly identify where the block // should be closed. - err.multipart_suggestion_verbose( + err.multipart_suggestion( "you might have meant to open the body of the closure", vec![(prev.span.shrink_to_hi(), " {".to_string())], Applicability::HasPlaceholders, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 5c44bfb0cf3f..4297086983b4 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1140,7 +1140,7 @@ enum FloatComponent { /// Parse the field access used in offset_of, matched by `$(e:expr)+`. /// Currently returns a list of idents. However, it should be possible in /// future to also do array indices, which might be arbitrary expressions. - fn parse_floating_field_access(&mut self) -> PResult<'a, Vec> { + pub(crate) fn parse_floating_field_access(&mut self) -> PResult<'a, Vec> { let mut fields = Vec::new(); let mut trailing_dot = None; @@ -1309,12 +1309,13 @@ fn maybe_recover_struct_lit_bad_delims( self.dcx() .create_err(errors::ParenthesesWithStructFields { span, - r#type: path, braces_for_struct: errors::BracesForStructLiteral { first: open_paren, second: close_paren, + r#type: path.clone(), }, no_fields_for_fn: errors::NoFieldsForFnCall { + r#type: path, fields: fields .into_iter() .map(|field| field.span.until(field.expr.span)) @@ -1628,8 +1629,7 @@ fn parse_expr_array_or_repeat(&mut self, close: ExpTokenPair) -> PResult<'a, Box let first_expr = self.parse_expr()?; if self.eat(exp!(Semi)) { // Repeating array syntax: `[ 0; 512 ]` - let count = - self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?; + let count = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?; self.expect(close)?; ExprKind::Repeat(first_expr, count) } else if self.eat(exp!(Comma)) { @@ -2521,7 +2521,7 @@ fn parse_closure_block_body(&mut self, ret_span: Span) -> PResult<'a, Box> ret_span, "explicit return type requires closure body to be enclosed in braces", ); - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( "wrap the expression in curly braces", vec![ (expr.span.shrink_to_lo(), "{ ".to_string()), @@ -3207,6 +3207,7 @@ fn parse_arm_body_missing_braces( errors::MatchArmBodyWithoutBracesSugg::AddBraces { left: span.shrink_to_lo(), right: span.shrink_to_hi(), + num_statements: stmts.len(), } } else { errors::MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp } @@ -3459,40 +3460,16 @@ pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { } fn parse_match_arm_guard(&mut self) -> PResult<'a, Option>> { - // Used to check the `if_let_guard` feature mostly by scanning - // `&&` tokens. - fn has_let_expr(expr: &Expr) -> bool { - match &expr.kind { - ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, lhs, rhs) => { - let lhs_rslt = has_let_expr(lhs); - let rhs_rslt = has_let_expr(rhs); - lhs_rslt || rhs_rslt - } - ExprKind::Let(..) => true, - _ => false, - } - } if !self.eat_keyword(exp!(If)) { // No match arm guard present. return Ok(None); } - let if_span = self.prev_token.span; let mut cond = self.parse_match_guard_condition()?; - let mut checker = CondChecker::new(self, LetChainsPolicy::AlwaysAllowed); - checker.visit_expr(&mut cond); + CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond); - if has_let_expr(&cond) { - let span = if_span.to(cond.span); - self.psess.gated_spans.gate(sym::if_let_guard, span); - } - - Ok(Some(if let Some(guar) = checker.found_incorrect_let_chain { - self.mk_expr_err(cond.span, guar) - } else { - cond - })) + Ok(Some(cond)) } fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option>)> { @@ -3866,6 +3843,15 @@ pub(super) fn parse_struct_fields( recovered_async = Some(guar); } + // If we encountered an error which we are recovering from, treat the struct + // as if it has a `..` in it, because we don’t know what fields the user + // might have *intended* it to have. + // + // This assignment will be overwritten if we actually parse a `..` later. + // + // (Note that this code is duplicated between here and below in comma parsing. + base = ast::StructRest::NoneWithError(guar); + // If the next token is a comma, then try to parse // what comes next as additional fields, rather than // bailing out until next `}`. @@ -3916,6 +3902,10 @@ pub(super) fn parse_struct_fields( } else if let Some(f) = field_ident(self, guar) { fields.push(f); } + + // See comment above on this same assignment inside of field parsing. + base = ast::StructRest::NoneWithError(guar); + self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); let _ = self.eat(exp!(Comma)); } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index c0f5afb952fd..db5be5feaeb6 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2,11 +2,11 @@ use std::mem; use ast::token::IdentIsRaw; +use rustc_ast as ast; use rustc_ast::ast::*; use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::util::case::Case; -use rustc_ast::{self as ast}; use rustc_ast_pretty::pprust; use rustc_errors::codes::*; use rustc_errors::{Applicability, PResult, StashKey, msg, struct_span_code_err}; @@ -197,6 +197,8 @@ pub(super) fn parse_item_common( if let Defaultness::Default(span) = def { this.dcx().emit_err(errors::DefaultNotFollowedByItem { span }); + } else if let Defaultness::Final(span) = def { + this.dcx().emit_err(errors::FinalNotFollowedByItem { span }); } if !attrs_allowed { @@ -206,14 +208,24 @@ pub(super) fn parse_item_common( }) } - /// Error in-case `default` was parsed in an in-appropriate context. + /// Error in-case `default`/`final` was parsed in an in-appropriate context. fn error_on_unconsumed_default(&self, def: Defaultness, kind: &ItemKind) { - if let Defaultness::Default(span) = def { - self.dcx().emit_err(errors::InappropriateDefault { - span, - article: kind.article(), - descr: kind.descr(), - }); + match def { + Defaultness::Default(span) => { + self.dcx().emit_err(errors::InappropriateDefault { + span, + article: kind.article(), + descr: kind.descr(), + }); + } + Defaultness::Final(span) => { + self.dcx().emit_err(errors::InappropriateFinal { + span, + article: kind.article(), + descr: kind.descr(), + }); + } + Defaultness::Implicit => (), } } @@ -229,8 +241,8 @@ fn parse_item_kind( fn_parse_mode: FnParseMode, case: Case, ) -> PResult<'a, Option> { - let check_pub = def == &Defaultness::Final; - let mut def_ = || mem::replace(def, Defaultness::Final); + let check_pub = def == &Defaultness::Implicit; + let mut def_ = || mem::replace(def, Defaultness::Implicit); let info = if !self.is_use_closure() && self.eat_keyword_case(exp!(Use), case) { self.parse_use_item()? @@ -284,7 +296,7 @@ fn parse_item_kind( // CONST ITEM self.recover_const_mut(const_span); self.recover_missing_kw_before_item()?; - let (ident, generics, ty, rhs_kind) = self.parse_const_item(false)?; + let (ident, generics, ty, rhs_kind) = self.parse_const_item(false, const_span)?; ItemKind::Const(Box::new(ConstItem { defaultness: def_(), ident, @@ -305,7 +317,7 @@ fn parse_item_kind( // TYPE CONST (mgca) self.recover_const_mut(const_span); self.recover_missing_kw_before_item()?; - let (ident, generics, ty, rhs_kind) = self.parse_const_item(true)?; + let (ident, generics, ty, rhs_kind) = self.parse_const_item(true, const_span)?; // Make sure this is only allowed if the feature gate is enabled. // #![feature(mgca_type_const_syntax)] self.psess.gated_spans.gate(sym::mgca_type_const_syntax, lo.to(const_span)); @@ -1005,22 +1017,87 @@ fn parse_defaultness(&mut self) -> Defaultness { { self.bump(); // `default` Defaultness::Default(self.prev_token_uninterpolated_span()) + } else if self.eat_keyword(exp!(Final)) { + self.psess.gated_spans.gate(sym::final_associated_functions, self.prev_token.span); + Defaultness::Final(self.prev_token_uninterpolated_span()) } else { - Defaultness::Final + Defaultness::Implicit } } - /// Is this an `(const unsafe? auto?| unsafe auto? | auto) trait` item? - fn check_trait_front_matter(&mut self) -> bool { - // auto trait - self.check_keyword(exp!(Auto)) && self.is_keyword_ahead(1, &[kw::Trait]) - // unsafe auto trait - || self.check_keyword(exp!(Unsafe)) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto]) - || self.check_keyword(exp!(Const)) && ((self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait])) - || self.is_keyword_ahead(1, &[kw::Unsafe]) && self.is_keyword_ahead(2, &[kw::Trait, kw::Auto])) + /// Is there an `[ impl(in? path) ]? trait` item `dist` tokens ahead? + fn is_trait_with_maybe_impl_restriction_in_front(&self, dist: usize) -> bool { + // `trait` + if self.is_keyword_ahead(dist, &[kw::Trait]) { + return true; + } + // `impl(` + if !self.is_keyword_ahead(dist, &[kw::Impl]) + || !self.look_ahead(dist + 1, |t| t == &token::OpenParen) + { + return false; + } + // `crate | super | self) trait` + if self.is_keyword_ahead(dist + 2, &[kw::Crate, kw::Super, kw::SelfLower]) + && self.look_ahead(dist + 3, |t| t == &token::CloseParen) + && self.is_keyword_ahead(dist + 4, &[kw::Trait]) + { + return true; + } + // `impl(in? something) trait` + // We catch cases where the `in` keyword is missing to provide a + // better error message. This is handled later in + // `self.recover_incorrect_impl_restriction`. + self.tree_look_ahead(dist + 2, |t| { + if let TokenTree::Token(token, _) = t { token.is_keyword(kw::Trait) } else { false } + }) + .unwrap_or(false) } - /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`. + /// Is this an `(const unsafe? auto? [ impl(in? path) ]? | unsafe auto? [ impl(in? path) ]? | auto [ impl(in? path) ]? | [ impl(in? path) ]?) trait` item? + fn check_trait_front_matter(&mut self) -> bool { + // `[ impl(in? path) ]? trait` + if self.is_trait_with_maybe_impl_restriction_in_front(0) { + return true; + } + // `auto [ impl(in? path) ]? trait` + if self.check_keyword(exp!(Auto)) && self.is_trait_with_maybe_impl_restriction_in_front(1) { + return true; + } + // `unsafe auto? [ impl(in? path) ]? trait` + if self.check_keyword(exp!(Unsafe)) + && (self.is_trait_with_maybe_impl_restriction_in_front(1) + || self.is_keyword_ahead(1, &[kw::Auto]) + && self.is_trait_with_maybe_impl_restriction_in_front(2)) + { + return true; + } + // `const` ... + if !self.check_keyword(exp!(Const)) { + return false; + } + // `const [ impl(in? path) ]? trait` + if self.is_trait_with_maybe_impl_restriction_in_front(1) { + return true; + } + // `const (unsafe | auto) [ impl(in? path) ]? trait` + if self.is_keyword_ahead(1, &[kw::Unsafe, kw::Auto]) + && self.is_trait_with_maybe_impl_restriction_in_front(2) + { + return true; + } + // `const unsafe auto [ impl(in? path) ]? trait` + self.is_keyword_ahead(1, &[kw::Unsafe]) + && self.is_keyword_ahead(2, &[kw::Auto]) + && self.is_trait_with_maybe_impl_restriction_in_front(3) + } + + /// Parses `const? unsafe? auto? [impl(in? path)]? trait Foo { ... }` or `trait Foo = Bar;`. + /// + /// FIXME(restrictions): The current keyword order follows the grammar specified in RFC 3323. + /// However, whether the restriction should be grouped closer to the visibility modifier + /// (e.g., `pub impl(crate) const unsafe auto trait`) remains an unresolved design question. + /// This ordering must be kept in sync with the logic in `check_trait_front_matter`. fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemKind> { let constness = self.parse_constness(Case::Sensitive); if let Const::Yes(span) = constness { @@ -1035,6 +1112,8 @@ fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, Ite IsAuto::No }; + let impl_restriction = self.parse_impl_restriction()?; + self.expect_keyword(exp!(Trait))?; let ident = self.parse_ident()?; let mut generics = self.parse_generics()?; @@ -1063,6 +1142,9 @@ fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, Ite if let Safety::Unsafe(_) = safety { self.dcx().emit_err(errors::TraitAliasCannotBeUnsafe { span: whole_span }); } + if let RestrictionKind::Restricted { .. } = impl_restriction.kind { + self.dcx().emit_err(errors::TraitAliasCannotBeImplRestricted { span: whole_span }); + } self.psess.gated_spans.gate(sym::trait_alias, whole_span); @@ -1075,6 +1157,7 @@ fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, Ite constness, is_auto, safety, + impl_restriction, ident, generics, bounds, @@ -1130,7 +1213,7 @@ fn parse_assoc_item( }) => { self.dcx().emit_err(errors::AssociatedStaticItemNotAllowed { span }); AssocItemKind::Const(Box::new(ConstItem { - defaultness: Defaultness::Final, + defaultness: Defaultness::Implicit, ident, generics: Generics::default(), ty, @@ -1543,6 +1626,7 @@ fn parse_static_item( fn parse_const_item( &mut self, const_arg: bool, + const_span: Span, ) -> PResult<'a, (Ident, Generics, Box, ConstItemRhsKind)> { let ident = self.parse_ident_or_underscore()?; @@ -1572,9 +1656,7 @@ fn parse_const_item( let rhs = match (self.eat(exp!(Eq)), const_arg) { (true, true) => ConstItemRhsKind::TypeConst { - rhs: Some( - self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?, - ), + rhs: Some(self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?), }, (true, false) => ConstItemRhsKind::Body { rhs: Some(self.parse_expr()?) }, (false, true) => ConstItemRhsKind::TypeConst { rhs: None }, @@ -1634,6 +1716,9 @@ fn parse_const_item( generics.where_clause = where_clause; + if let Some(rhs) = self.try_recover_const_missing_semi(&rhs, const_span) { + return Ok((ident, generics, ty, ConstItemRhsKind::Body { rhs: Some(rhs) })); + } self.expect_semi()?; Ok((ident, generics, ty, rhs)) @@ -2731,8 +2816,21 @@ fn parse_fn_body( *sig_hi = self.prev_token.span; (AttrVec::new(), None) } else if self.check(exp!(OpenBrace)) || self.token.is_metavar_block() { - self.parse_block_common(self.token.span, BlockCheckMode::Default, None) - .map(|(attrs, body)| (attrs, Some(body)))? + let prev_in_fn_body = self.in_fn_body; + self.in_fn_body = true; + let res = self.parse_block_common(self.token.span, BlockCheckMode::Default, None).map( + |(attrs, mut body)| { + if let Some(guar) = self.fn_body_missing_semi_guar.take() { + body.stmts.push(self.mk_stmt( + body.span, + StmtKind::Expr(self.mk_expr(body.span, ExprKind::Err(guar))), + )); + } + (attrs, Some(body)) + }, + ); + self.in_fn_body = prev_in_fn_body; + res? } else if self.token == token::Eq { // Recover `fn foo() = $expr;`. self.bump(); // `=` @@ -2828,8 +2926,8 @@ pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> b && !self.is_unsafe_foreign_mod() // Rule out `async gen {` and `async gen move {` && !self.is_async_gen_block() - // Rule out `const unsafe auto` and `const unsafe trait`. - && !self.is_keyword_ahead(2, &[kw::Auto, kw::Trait]) + // Rule out `const unsafe auto` and `const unsafe trait` and `const unsafe impl`. + && !self.is_keyword_ahead(2, &[kw::Auto, kw::Trait, kw::Impl]) ) }) // `extern ABI fn` @@ -3488,6 +3586,37 @@ fn recover_self_param(&mut self) -> bool { Ok(Some(_)) ) } + + /// Try to recover from over-parsing in const item when a semicolon is missing. + /// + /// This detects cases where we parsed too much because a semicolon was missing + /// and the next line started an expression that the parser treated as a continuation + /// (e.g., `foo() \n &bar` was parsed as `foo() & bar`). + /// + /// Returns a corrected expression if recovery is successful. + fn try_recover_const_missing_semi( + &mut self, + rhs: &ConstItemRhsKind, + const_span: Span, + ) -> Option> { + if self.token == TokenKind::Semi { + return None; + } + let ConstItemRhsKind::Body { rhs: Some(rhs) } = rhs else { + return None; + }; + if !self.in_fn_body || !self.may_recover() || rhs.span.from_expansion() { + return None; + } + if let Some((span, guar)) = + self.missing_semi_from_binop("const", rhs, Some(const_span.shrink_to_lo())) + { + self.fn_body_missing_semi_guar = Some(guar); + Some(self.mk_expr(span, ExprKind::Err(guar))) + } else { + None + } + } } enum IsMacroRulesItem { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index f95fe61b0abd..36ca8a7d6133 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -16,7 +16,7 @@ pub mod asm; pub mod cfg_select; -use std::{fmt, mem, slice}; +use std::{debug_assert_matches, fmt, mem, slice}; use attr_wrapper::{AttrWrapper, UsePreAttrPos}; pub use diagnostics::AttemptLocalParseRecovery; @@ -32,24 +32,28 @@ ParserRange, ParserReplacement, Spacing, TokenCursor, TokenStream, TokenTree, TokenTreeCursor, }; use rustc_ast::util::case::Case; +use rustc_ast::util::classify; use rustc_ast::{ - self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, - DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, MgcaDisambiguation, Mutability, - Recovered, Safety, StrLit, Visibility, VisibilityKind, + self as ast, AnonConst, AttrArgs, AttrId, BinOpKind, ByRef, Const, CoroutineKind, + DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, ImplRestriction, + MgcaDisambiguation, Mutability, Recovered, RestrictionKind, Safety, StrLit, Visibility, + VisibilityKind, }; use rustc_ast_pretty::pprust; -use rustc_data_structures::debug_assert_matches; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diag, FatalError, MultiSpan, PResult}; use rustc_index::interval::IntervalSet; use rustc_session::parse::ParseSess; -use rustc_span::{Ident, Span, Symbol, kw, sym}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use thin_vec::ThinVec; use token_type::TokenTypeSet; pub use token_type::{ExpKeywordPair, ExpTokenPair, TokenType}; use tracing::debug; -use crate::errors::{self, IncorrectVisibilityRestriction, NonStringAbiLiteral, TokenDescription}; +use crate::errors::{ + self, IncorrectImplRestriction, IncorrectVisibilityRestriction, NonStringAbiLiteral, + TokenDescription, +}; use crate::exp; #[cfg(test)] @@ -232,6 +236,10 @@ pub struct Parser<'a> { /// Whether the parser is allowed to do recovery. /// This is disabled when parsing macro arguments, see #103534 recovery: Recovery = Recovery::Allowed, + /// Whether we're parsing a function body. + in_fn_body: bool = false, + /// Whether we have detected a missing semicolon in function body. + pub fn_body_missing_semi_guar: Option = None, } // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with @@ -1525,6 +1533,60 @@ fn recover_incorrect_vis_restriction(&mut self) -> PResult<'a, ()> { Ok(()) } + /// Parses an optional `impl` restriction. + /// Enforces the `impl_restriction` feature gate whenever an explicit restriction is encountered. + fn parse_impl_restriction(&mut self) -> PResult<'a, ImplRestriction> { + if self.eat_keyword(exp!(Impl)) { + let lo = self.prev_token.span; + // No units or tuples are allowed to follow `impl` here, so we can safely bump `(`. + self.expect(exp!(OpenParen))?; + if self.eat_keyword(exp!(In)) { + let path = self.parse_path(PathStyle::Mod)?; // `in path` + self.expect(exp!(CloseParen))?; // `)` + let restriction = RestrictionKind::Restricted { + path: Box::new(path), + id: ast::DUMMY_NODE_ID, + shorthand: false, + }; + let span = lo.to(self.prev_token.span); + self.psess.gated_spans.gate(sym::impl_restriction, span); + return Ok(ImplRestriction { kind: restriction, span, tokens: None }); + } else if self.look_ahead(1, |t| t == &token::CloseParen) + && self.is_keyword_ahead(0, &[kw::Crate, kw::Super, kw::SelfLower]) + { + let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self` + self.expect(exp!(CloseParen))?; // `)` + let restriction = RestrictionKind::Restricted { + path: Box::new(path), + id: ast::DUMMY_NODE_ID, + shorthand: true, + }; + let span = lo.to(self.prev_token.span); + self.psess.gated_spans.gate(sym::impl_restriction, span); + return Ok(ImplRestriction { kind: restriction, span, tokens: None }); + } else { + self.recover_incorrect_impl_restriction(lo)?; + // Emit diagnostic, but continue with no impl restriction. + } + } + Ok(ImplRestriction { + kind: RestrictionKind::Unrestricted, + span: self.token.span.shrink_to_lo(), + tokens: None, + }) + } + + /// Recovery for e.g. `impl(something) trait` + fn recover_incorrect_impl_restriction(&mut self, lo: Span) -> PResult<'a, ()> { + let path = self.parse_path(PathStyle::Mod)?; + self.expect(exp!(CloseParen))?; // `)` + let path_str = pprust::path_to_string(&path); + self.dcx().emit_err(IncorrectImplRestriction { span: path.span, inner_str: path_str }); + let end = self.prev_token.span; + self.psess.gated_spans.gate(sym::impl_restriction, lo.to(end)); + Ok(()) + } + /// Parses `extern string_literal?`. fn parse_extern(&mut self, case: Case) -> Extern { if self.eat_keyword_case(exp!(Extern), case) { @@ -1649,6 +1711,65 @@ pub fn prev_token_uninterpolated_span(&self) -> Span { _ => self.prev_token.span, } } + + fn missing_semi_from_binop( + &self, + kind_desc: &str, + expr: &Expr, + decl_lo: Option, + ) -> Option<(Span, ErrorGuaranteed)> { + if self.token == TokenKind::Semi { + return None; + } + if !self.may_recover() || expr.span.from_expansion() { + return None; + } + let sm = self.psess.source_map(); + if let ExprKind::Binary(op, lhs, rhs) = &expr.kind + && sm.is_multiline(lhs.span.shrink_to_hi().until(rhs.span.shrink_to_lo())) + && matches!(op.node, BinOpKind::Mul | BinOpKind::BitAnd) + && classify::expr_requires_semi_to_be_stmt(rhs) + { + let lhs_end_span = lhs.span.shrink_to_hi(); + let token_str = token_descr(&self.token); + let mut err = self + .dcx() + .struct_span_err(lhs_end_span, format!("expected `;`, found {token_str}")); + err.span_label(self.token.span, "unexpected token"); + + // Use the declaration start if provided, otherwise fall back to lhs_end_span. + let continuation_start = decl_lo.unwrap_or(lhs_end_span); + let continuation_span = continuation_start.until(rhs.span.shrink_to_hi()); + err.span_label( + continuation_span, + format!( + "to finish parsing this {kind_desc}, expected this to be followed by a `;`", + ), + ); + let op_desc = match op.node { + BinOpKind::BitAnd => "a bit-and", + BinOpKind::Mul => "a multiplication", + _ => "a binary", + }; + let mut note_spans = MultiSpan::new(); + note_spans.push_span_label(lhs.span, "parsed as the left-hand expression"); + note_spans.push_span_label(rhs.span, "parsed as the right-hand expression"); + note_spans.push_span_label(op.span, format!("this was parsed as {op_desc}")); + err.span_note( + note_spans, + format!("the {kind_desc} was parsed as having {op_desc} binary expression"), + ); + + err.span_suggestion( + lhs_end_span, + format!("you may have meant to write a `;` to terminate the {kind_desc} earlier"), + ";", + Applicability::MaybeIncorrect, + ); + return Some((lhs.span, err.emit())); + } + None + } } // Metavar captures of various kinds. diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index bc73c3a2007a..528b69abbf1a 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -364,6 +364,7 @@ fn recover_trailing_vert(&mut self, lo: Option) -> bool { start: lo, suggestion: TrailingVertSuggestion { span: self.prev_token.span.shrink_to_hi().with_hi(self.token.span.hi()), + token: self.token, }, token: self.token, note_double_vert: self.token.kind == token::OrOr, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 9196d8d156d8..e514a08c8fb3 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -915,28 +915,7 @@ pub(super) fn parse_unambiguous_unbraced_const_arg( }); } - let mgca_disambiguation = self.mgca_direct_lit_hack(&expr); - Ok((expr, mgca_disambiguation)) - } - - /// Under `min_generic_const_args` we still allow *some* anon consts to be written without - /// a `const` block as it makes things quite a lot nicer. This function is useful for contexts - /// where we would like to use `MgcaDisambiguation::Direct` but need to fudge it to be `AnonConst` - /// in the presence of literals. - // - /// FIXME(min_generic_const_args): In the long term it would be nice to have a way to directly - /// represent literals in `hir::ConstArgKind` so that we can remove this special case by not - /// needing an anon const. - pub fn mgca_direct_lit_hack(&self, expr: &Expr) -> MgcaDisambiguation { - match &expr.kind { - ast::ExprKind::Lit(_) => MgcaDisambiguation::AnonConst, - ast::ExprKind::Unary(ast::UnOp::Neg, expr) - if matches!(expr.kind, ast::ExprKind::Lit(_)) => - { - MgcaDisambiguation::AnonConst - } - _ => MgcaDisambiguation::Direct, - } + Ok((expr, MgcaDisambiguation::Direct)) } /// Parse a generic argument in a path segment. diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 3c6629572ae5..3b2102484bdf 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -924,6 +924,21 @@ fn recover_missing_dot(&mut self, err: &mut Diag<'_>) { } } + fn try_recover_let_missing_semi(&mut self, local: &mut Local) -> Option { + let expr = match &mut local.kind { + LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => expr, + LocalKind::Decl => return None, + }; + if let Some((span, guar)) = + self.missing_semi_from_binop("`let` binding", expr, Some(local.span.shrink_to_lo())) + { + self.fn_body_missing_semi_guar = Some(guar); + *expr = self.mk_expr(span, ExprKind::Err(guar)); + return Some(guar); + } + None + } + /// Parses a statement, including the trailing semicolon. pub fn parse_full_stmt( &mut self, @@ -1066,71 +1081,74 @@ pub fn parse_full_stmt( } } StmtKind::Expr(_) | StmtKind::MacCall(_) => {} - StmtKind::Let(local) if let Err(mut e) = self.expect_semi() => { - // We might be at the `,` in `let x = foo;`. Try to recover. - match &mut local.kind { - LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => { - self.check_mistyped_turbofish_with_multiple_type_params(e, expr).map_err( - |mut e| { - self.recover_missing_dot(&mut e); - self.recover_missing_let_else(&mut e, &local.pat, stmt.span); - e - }, - )?; - // We found `foo`, have we fully recovered? - self.expect_semi()?; - } - LocalKind::Decl => { - if let Some(colon_sp) = local.colon_sp { - e.span_label( - colon_sp, - format!( - "while parsing the type for {}", - local.pat.descr().map_or_else( - || "the binding".to_string(), - |n| format!("`{n}`") - ) - ), - ); - let suggest_eq = if self.token == token::Dot - && let _ = self.bump() - && let mut snapshot = self.create_snapshot_for_diagnostic() - && let Ok(_) = snapshot - .parse_dot_suffix_expr( - colon_sp, - self.mk_expr_err( - colon_sp, - self.dcx() - .delayed_bug("error during `:` -> `=` recovery"), - ), - ) - .map_err(Diag::cancel) - { - true - } else if let Some(op) = self.check_assoc_op() - && op.node.can_continue_expr_unambiguously() - { - true - } else { - false - }; - if suggest_eq { - e.span_suggestion_short( - colon_sp, - "use `=` if you meant to assign", - "=", - Applicability::MaybeIncorrect, - ); - } + StmtKind::Let(local) => { + if self.try_recover_let_missing_semi(local).is_some() { + return Ok(Some(stmt)); + } + if let Err(mut e) = self.expect_semi() { + // We might be at the `,` in `let x = foo;`. Try to recover. + match &mut local.kind { + LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => { + self.check_mistyped_turbofish_with_multiple_type_params(e, expr) + .map_err(|mut e| { + self.recover_missing_dot(&mut e); + self.recover_missing_let_else(&mut e, &local.pat, stmt.span); + e + })?; + // We found `foo`, have we fully recovered? + self.expect_semi()?; + } + LocalKind::Decl => { + if let Some(colon_sp) = local.colon_sp { + e.span_label( + colon_sp, + format!( + "while parsing the type for {}", + local.pat.descr().map_or_else( + || "the binding".to_string(), + |n| format!("`{n}`") + ) + ), + ); + let suggest_eq = if self.token == token::Dot + && let _ = self.bump() + && let mut snapshot = self.create_snapshot_for_diagnostic() + && let Ok(_) = snapshot + .parse_dot_suffix_expr( + colon_sp, + self.mk_expr_err( + colon_sp, + self.dcx().delayed_bug( + "error during `:` -> `=` recovery", + ), + ), + ) + .map_err(Diag::cancel) + { + true + } else if let Some(op) = self.check_assoc_op() + && op.node.can_continue_expr_unambiguously() + { + true + } else { + false + }; + if suggest_eq { + e.span_suggestion_short( + colon_sp, + "use `=` if you meant to assign", + "=", + Applicability::MaybeIncorrect, + ); + } + } + return Err(e); } - return Err(e); } } eat_semi = false; } - StmtKind::Empty | StmtKind::Item(_) | StmtKind::Let(_) | StmtKind::Semi(_) => { - eat_semi = false - } + StmtKind::Empty | StmtKind::Item(_) | StmtKind::Semi(_) => eat_semi = false, } if add_semi_to_stmt || (eat_semi && self.eat(exp!(Semi))) { diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index ac9c3e63e853..5286873f3dc5 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -3,17 +3,15 @@ use std::iter::Peekable; use std::path::PathBuf; use std::sync::{Arc, Mutex}; -use std::{io, str}; +use std::{assert_matches, io, str}; use ast::token::IdentIsRaw; use rustc_ast::token::{self, Delimiter, Token}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast::{self as ast, PatKind, visit}; use rustc_ast_pretty::pprust::item_to_string; -use rustc_data_structures::assert_matches; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; use rustc_errors::emitter::OutputTheme; -use rustc_errors::translation::Translator; use rustc_errors::{AutoStream, DiagCtxt, MultiSpan, PResult}; use rustc_session::parse::ParseSess; use rustc_span::source_map::{FilePathMapping, SourceMap}; @@ -42,11 +40,10 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> { fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Arc, Arc>>) { let output = Arc::new(Mutex::new(Vec::new())); let source_map = Arc::new(SourceMap::new(FilePathMapping::empty())); - let translator = Translator::new(); let shared: Box = Box::new(Shared { data: output.clone() }); let auto_stream = AutoStream::never(shared); let dcx = DiagCtxt::new(Box::new( - AnnotateSnippetEmitter::new(auto_stream, translator) + AnnotateSnippetEmitter::new(auto_stream) .sm(Some(source_map.clone())) .diagnostic_width(Some(140)) .theme(theme), @@ -2326,7 +2323,7 @@ fn string_to_tts_1() { let expected = TokenStream::new(vec![ TokenTree::token_alone(token::Ident(kw::Fn, IdentIsRaw::No), sp(0, 2)), TokenTree::token_joint_hidden( - token::Ident(Symbol::intern("a"), IdentIsRaw::No), + token::Ident(sym::character('a'), IdentIsRaw::No), sp(3, 4), ), TokenTree::Delimited( @@ -2337,7 +2334,7 @@ fn string_to_tts_1() { Delimiter::Parenthesis, TokenStream::new(vec![ TokenTree::token_joint( - token::Ident(Symbol::intern("b"), IdentIsRaw::No), + token::Ident(sym::character('b'), IdentIsRaw::No), sp(5, 6), ), TokenTree::token_alone(token::Colon, sp(6, 7)), @@ -2357,7 +2354,7 @@ fn string_to_tts_1() { Delimiter::Brace, TokenStream::new(vec![ TokenTree::token_joint( - token::Ident(Symbol::intern("b"), IdentIsRaw::No), + token::Ident(sym::character('b'), IdentIsRaw::No), sp(15, 16), ), // `Alone` because the `;` is followed by whitespace. @@ -2545,10 +2542,10 @@ fn look(p: &Parser<'_>, dist: usize, kind: rustc_ast::token::TokenKind) { #[test] fn look_ahead() { create_default_session_globals_then(|| { - let sym_f = Symbol::intern("f"); - let sym_x = Symbol::intern("x"); + let sym_f = sym::character('f'); + let sym_x = sym::character('x'); #[allow(non_snake_case)] - let sym_S = Symbol::intern("S"); + let sym_S = sym::character('S'); let raw_no = IdentIsRaw::No; let psess = ParseSess::new(); @@ -2620,10 +2617,10 @@ fn look_ahead() { #[test] fn look_ahead_non_outermost_stream() { create_default_session_globals_then(|| { - let sym_f = Symbol::intern("f"); - let sym_x = Symbol::intern("x"); + let sym_f = sym::character('f'); + let sym_x = sym::character('x'); #[allow(non_snake_case)] - let sym_S = Symbol::intern("S"); + let sym_S = sym::character('S'); let raw_no = IdentIsRaw::No; let psess = ParseSess::new(); diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index 567b1be5e5d9..2d1db430ac27 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -91,6 +91,7 @@ pub enum TokenType { KwElse, KwEnum, KwExtern, + KwFinal, KwFn, KwFor, KwGen, @@ -233,6 +234,7 @@ fn from_u32(val: u32) -> TokenType { KwExtern, KwFn, KwFor, + KwFinal, KwGen, KwIf, KwImpl, @@ -309,6 +311,7 @@ pub(super) fn is_keyword(&self) -> Option { TokenType::KwExtern => Some(kw::Extern), TokenType::KwFn => Some(kw::Fn), TokenType::KwFor => Some(kw::For), + TokenType::KwFinal => Some(kw::Final), TokenType::KwGen => Some(kw::Gen), TokenType::KwIf => Some(kw::If), TokenType::KwImpl => Some(kw::Impl), @@ -524,6 +527,7 @@ macro_rules! exp { (Extern) => { exp!(@kw, Extern, KwExtern) }; (Fn) => { exp!(@kw, Fn, KwFn) }; (For) => { exp!(@kw, For, KwFor) }; + (Final) => { exp!(@kw, Final, KwFinal) }; (Gen) => { exp!(@kw, Gen, KwGen) }; (If) => { exp!(@kw, If, KwIf) }; (Impl) => { exp!(@kw, Impl, KwImpl) }; diff --git a/compiler/rustc_parse/src/parser/tokenstream/tests.rs b/compiler/rustc_parse/src/parser/tokenstream/tests.rs index 63177a727449..b54fdf6d8eaf 100644 --- a/compiler/rustc_parse/src/parser/tokenstream/tests.rs +++ b/compiler/rustc_parse/src/parser/tokenstream/tests.rs @@ -2,7 +2,7 @@ use rustc_ast::token::{self, IdentIsRaw}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; -use rustc_span::{BytePos, Span, Symbol, create_default_session_globals_then}; +use rustc_span::{BytePos, Span, create_default_session_globals_then, sym}; use crate::parser::tests::string_to_stream; @@ -92,7 +92,7 @@ fn test_is_empty() { create_default_session_globals_then(|| { let test0 = TokenStream::default(); let test1 = - TokenStream::token_alone(token::Ident(Symbol::intern("a"), IdentIsRaw::No), sp(0, 1)); + TokenStream::token_alone(token::Ident(sym::character('a'), IdentIsRaw::No), sp(0, 1)); let test2 = string_to_ts("foo(bar::baz)"); assert_eq!(test0.is_empty(), true); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6ff165eb22b7..2a5cd4cd55ae 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -329,6 +329,8 @@ fn parse_ty_common( self.parse_borrowed_pointee()? } else if self.eat_keyword_noexpect(kw::Typeof) { self.parse_typeof_ty(lo)? + } else if self.is_builtin() { + self.parse_builtin_ty()? } else if self.eat_keyword(exp!(Underscore)) { // A type to be inferred `_` TyKind::Infer @@ -658,8 +660,7 @@ fn parse_array_or_slice_ty(&mut self) -> PResult<'a, TyKind> { }; let ty = if self.eat(exp!(Semi)) { - let mut length = - self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?; + let mut length = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?; if let Err(e) = self.expect(exp!(CloseBracket)) { // Try to recover from `X` when `X::` works @@ -803,6 +804,52 @@ fn parse_typeof_ty(&mut self, lo: Span) -> PResult<'a, TyKind> { Ok(TyKind::Err(guar)) } + fn parse_builtin_ty(&mut self) -> PResult<'a, TyKind> { + self.parse_builtin(|this, lo, ident| { + Ok(match ident.name { + sym::field_of => Some(this.parse_ty_field_of(lo)?), + _ => None, + }) + }) + } + + pub(crate) fn parse_ty_field_of(&mut self, _lo: Span) -> PResult<'a, TyKind> { + let container = self.parse_ty()?; + self.expect(exp!(Comma))?; + + let fields = self.parse_floating_field_access()?; + let trailing_comma = self.eat_noexpect(&TokenKind::Comma); + + if let Err(mut e) = self.expect_one_of(&[], &[exp!(CloseParen)]) { + if trailing_comma { + e.note("unexpected third argument to field_of"); + } else { + e.note("field_of expects dot-separated field and variant names"); + } + e.emit(); + } + + // Eat tokens until the macro call ends. + if self.may_recover() { + while !self.token.kind.is_close_delim_or_eof() { + self.bump(); + } + } + + match *fields { + [] => Err(self.dcx().struct_span_err( + self.token.span, + "`field_of!` expects dot-separated field and variant names", + )), + [field] => Ok(TyKind::FieldOf(container, None, field)), + [variant, field] => Ok(TyKind::FieldOf(container, Some(variant), field)), + _ => Err(self.dcx().struct_span_err( + fields.iter().map(|f| f.span).collect::>(), + "`field_of!` only supports a single field or a variant with a field", + )), + } + } + /// Parses a function pointer type (`TyKind::FnPtr`). /// ```ignore (illustrative) /// [unsafe] [extern "ABI"] fn (S) -> T diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 2338268a874f..c7ffc1e5f368 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -934,7 +934,11 @@ fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: &Argument<'_>) 0, ParseError { description: "field access isn't supported".to_string(), - note: None, + note: Some( + "consider moving this expression to a local variable and then \ + using the local here instead" + .to_owned(), + ), label: "not supported".to_string(), span: arg.position_span.start..field.position_span.end, secondary_label: None, @@ -947,7 +951,11 @@ fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: &Argument<'_>) 0, ParseError { description: "tuple index access isn't supported".to_string(), - note: None, + note: Some( + "consider moving this expression to a local variable and then \ + using the local here instead" + .to_owned(), + ), label: "not supported".to_string(), span: arg.position_span.start..field.position_span.end, secondary_label: None, diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index c3e80208e2d5..3da0978e5ff8 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::{AttributeKind, RustcAbiAttrKind}; +use rustc_hir::attrs::RustcAbiAttrKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::find_attr; @@ -18,7 +18,8 @@ pub fn test_abi(tcx: TyCtxt<'_>) { return; } for id in tcx.hir_crate_items(()).definitions() { - let Some((attr_span, attr_kind)) = find_attr!(tcx.get_all_attrs(id), AttributeKind::RustcAbi{ attr_span, kind } => (*attr_span, *kind)) + let Some((attr_span, attr_kind)) = + find_attr!(tcx, id, RustcAbi{ attr_span, kind } => (*attr_span, *kind)) else { continue; }; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 33d45b648cb8..bec6ab7e8355 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -9,7 +9,7 @@ use std::collections::hash_map::Entry; use std::slice; -use rustc_abi::{Align, ExternAbi, Size}; +use rustc_abi::ExternAbi; use rustc_ast::{AttrStyle, MetaItemKind, ast}; use rustc_attr_parsing::{AttributeParser, Late}; use rustc_data_structures::fx::FxHashMap; @@ -20,6 +20,7 @@ ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, }; +use rustc_hir::attrs::diagnostic::Directive; use rustc_hir::attrs::{ AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr, MirDialect, MirPhase, ReprAttr, SanitizerSet, @@ -28,11 +29,11 @@ use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, HirId, Item, ItemKind, - MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target, TraitItem, - find_attr, + self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, GenericParamKind, HirId, + Item, ItemKind, MethodKind, Node, ParamName, PartialConstStability, Safety, Stability, + StabilityLevel, Target, TraitItem, find_attr, }; -use rustc_macros::LintDiagnostic; +use rustc_macros::Diagnostic; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::query::Providers; @@ -43,8 +44,8 @@ use rustc_session::config::CrateType; use rustc_session::lint; use rustc_session::lint::builtin::{ - CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, - UNUSED_ATTRIBUTES, + CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; @@ -52,22 +53,21 @@ use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; use rustc_trait_selection::traits::ObligationCtxt; -use tracing::debug; use crate::errors; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`#[diagnostic::on_unimplemented]` can only be applied to trait definitions")] struct DiagnosticOnUnimplementedOnlyForTraits; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`#[diagnostic::on_const]` can only be applied to trait impls")] struct DiagnosticOnConstOnlyForTraitImpls { #[label("not a trait impl")] item_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`#[diagnostic::on_const]` can only be applied to non-const trait impls")] struct DiagnosticOnConstOnlyForNonConstTraitImpls { #[label("this is a const trait impl")] @@ -178,20 +178,20 @@ fn check_attributes( Attribute::Parsed(AttributeKind::RustcAllowConstFnUnstable(_, first_span)) => { self.check_rustc_allow_const_fn_unstable(hir_id, *first_span, span, target) } - Attribute::Parsed(AttributeKind::Deprecation {span: attr_span, .. }) => { + Attribute::Parsed(AttributeKind::Deprecated { span: attr_span, .. }) => { self.check_deprecated(hir_id, *attr_span, target) } Attribute::Parsed(AttributeKind::TargetFeature{ attr_span, ..}) => { self.check_target_feature(hir_id, *attr_span, target, attrs) } - Attribute::Parsed(AttributeKind::RustcObjectLifetimeDefault) => { - self.check_object_lifetime_default(hir_id); + Attribute::Parsed(AttributeKind::RustcDumpObjectLifetimeDefaults) => { + self.check_dump_object_lifetime_defaults(hir_id); } &Attribute::Parsed(AttributeKind::RustcPubTransparent(attr_span)) => { self.check_rustc_pub_transparent(attr_span, span, attrs) } - Attribute::Parsed(AttributeKind::Align { align, span: attr_span }) => { - self.check_align(*align, *attr_span) + Attribute::Parsed(AttributeKind::RustcAlign {..}) => { + } Attribute::Parsed(AttributeKind::Naked(..)) => { self.check_naked(hir_id, target) @@ -231,6 +231,8 @@ fn check_attributes( self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id,target) }, Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)}, + Attribute::Parsed(AttributeKind::OnUnimplemented{span, directive}) => {self.check_diagnostic_on_unimplemented(*span, hir_id, target,directive.as_deref())}, + Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)} Attribute::Parsed( // tidy-alphabetical-start AttributeKind::RustcAllowIncoherentImpl(..) @@ -246,12 +248,13 @@ fn check_attributes( | AttributeKind::CrateName { .. } | AttributeKind::CrateType(..) | AttributeKind::DebuggerVisualizer(..) + | AttributeKind::DefaultLibAllocator // `#[doc]` is actually a lot more than just doc comments, so is checked below | AttributeKind::DocComment {..} | AttributeKind::EiiDeclaration { .. } - | AttributeKind::EiiForeignItem | AttributeKind::ExportName { .. } | AttributeKind::ExportStable + | AttributeKind::Feature(..) | AttributeKind::FfiConst(..) | AttributeKind::Fundamental | AttributeKind::Ignore { .. } @@ -287,6 +290,7 @@ fn check_attributes( | AttributeKind::ProfilerRuntime | AttributeKind::RecursionLimit { .. } | AttributeKind::ReexportTestHarnessMain(..) + | AttributeKind::RegisterTool(..) // handled below this loop and elsewhere | AttributeKind::Repr { .. } | AttributeKind::RustcAbi { .. } @@ -294,6 +298,7 @@ fn check_attributes( | AttributeKind::RustcAllocatorZeroed | AttributeKind::RustcAllocatorZeroedVariant { .. } | AttributeKind::RustcAsPtr(..) + | AttributeKind::RustcAutodiff(..) | AttributeKind::RustcBodyStability { .. } | AttributeKind::RustcBuiltinMacro { .. } | AttributeKind::RustcCaptureAnalysis @@ -302,7 +307,7 @@ fn check_attributes( | AttributeKind::RustcCoherenceIsCore(..) | AttributeKind::RustcCoinductive(..) | AttributeKind::RustcConfusables { .. } - | AttributeKind::RustcConstStabilityIndirect + | AttributeKind::RustcConstStableIndirect | AttributeKind::RustcConversionSuggestion | AttributeKind::RustcDeallocator | AttributeKind::RustcDefPath(..) @@ -310,18 +315,25 @@ fn check_attributes( | AttributeKind::RustcDenyExplicitImpl(..) | AttributeKind::RustcDeprecatedSafe2024 {..} | AttributeKind::RustcDiagnosticItem(..) + | AttributeKind::RustcDoNotConstCheck + | AttributeKind::RustcDocPrimitive(..) | AttributeKind::RustcDummy | AttributeKind::RustcDumpDefParents + | AttributeKind::RustcDumpInferredOutlives | AttributeKind::RustcDumpItemBounds | AttributeKind::RustcDumpPredicates | AttributeKind::RustcDumpUserArgs + | AttributeKind::RustcDumpVariances + | AttributeKind::RustcDumpVariancesOfOpaques | AttributeKind::RustcDumpVtable(..) | AttributeKind::RustcDynIncompatibleTrait(..) | AttributeKind::RustcEffectiveVisibility + | AttributeKind::RustcEiiForeignItem | AttributeKind::RustcEvaluateWhereClauses | AttributeKind::RustcHasIncoherentInherentImpls | AttributeKind::RustcHiddenTypeOfOpaques | AttributeKind::RustcIfThisChanged(..) + | AttributeKind::RustcInheritOverflowChecks | AttributeKind::RustcInsignificantDtor | AttributeKind::RustcIntrinsic | AttributeKind::RustcIntrinsicConstStableIndirect @@ -335,17 +347,17 @@ fn check_attributes( | AttributeKind::RustcMacroTransparency(_) | AttributeKind::RustcMain | AttributeKind::RustcMir(_) - | AttributeKind::RustcNeverReturnsNullPointer + | AttributeKind::RustcNeverReturnsNullPtr | AttributeKind::RustcNeverTypeOptions {..} | AttributeKind::RustcNoImplicitAutorefs | AttributeKind::RustcNoImplicitBounds | AttributeKind::RustcNoMirInline | AttributeKind::RustcNonConstTraitMethod + | AttributeKind::RustcNonnullOptimizationGuaranteed | AttributeKind::RustcNounwind | AttributeKind::RustcObjcClass { .. } | AttributeKind::RustcObjcSelector { .. } | AttributeKind::RustcOffloadKernel - | AttributeKind::RustcOutlives | AttributeKind::RustcParenSugar(..) | AttributeKind::RustcPassByValue (..) | AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) @@ -366,8 +378,6 @@ fn check_attributes( | AttributeKind::RustcThenThisWouldNeed(..) | AttributeKind::RustcTrivialFieldReads | AttributeKind::RustcUnsafeSpecializationMarker(..) - | AttributeKind::RustcVariance - | AttributeKind::RustcVarianceOfOpaques | AttributeKind::ShouldPanic { .. } | AttributeKind::TestRunner(..) | AttributeKind::ThreadLocal @@ -380,36 +390,14 @@ fn check_attributes( Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); match attr.path().as_slice() { - [sym::diagnostic, sym::on_unimplemented, ..] => { - self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) - } - [sym::diagnostic, sym::on_const, ..] => { - self.check_diagnostic_on_const(attr.span(), hir_id, target, item) - } - [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { - self.check_autodiff(hir_id, attr, span, target) - } [ // ok sym::allow | sym::expect | sym::warn | sym::deny - | sym::forbid - // internal - | sym::default_lib_allocator - | sym::rustc_nonnull_optimization_guaranteed - | sym::rustc_inherit_overflow_checks - | sym::rustc_on_unimplemented - | sym::rustc_do_not_const_check - | sym::rustc_doc_primitive - | sym::rustc_layout - | sym::rustc_autodiff - | sym::rustc_capture_analysis - | sym::rustc_mir - // crate-level attrs, are checked below - | sym::feature - | sym::register_tool, .. + | sym::forbid, + .. ] => {} [name, rest@..] => { match BUILTIN_ATTRIBUTE_MAP.get(name) { @@ -568,7 +556,7 @@ fn check_eii_impl(&self, impls: &[EiiImpl], target: Target) { } if let EiiImplResolution::Macro(eii_macro) = resolution - && find_attr!(self.tcx.get_all_attrs(*eii_macro), AttributeKind::EiiDeclaration(EiiDecl { impl_unsafe, .. }) if *impl_unsafe) + && find_attr!(self.tcx, *eii_macro, EiiDeclaration(EiiDecl { impl_unsafe, .. }) if *impl_unsafe) && !impl_marked_unsafe { self.dcx().emit_err(errors::EiiImplRequiresUnsafe { @@ -608,7 +596,13 @@ fn check_do_not_recommend( } /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition - fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) { + fn check_diagnostic_on_unimplemented( + &self, + attr_span: Span, + hir_id: HirId, + target: Target, + directive: Option<&Directive>, + ) { if !matches!(target, Target::Trait) { self.tcx.emit_node_span_lint( MISPLACED_DIAGNOSTIC_ATTRIBUTES, @@ -617,6 +611,39 @@ fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, targ DiagnosticOnUnimplementedOnlyForTraits, ); } + + if let Some(directive) = directive { + if let Node::Item(Item { + kind: ItemKind::Trait(_, _, _, trait_name, generics, _, _), + .. + }) = self.tcx.hir_node(hir_id) + { + directive.visit_params(&mut |argument_name, span| { + let has_generic = generics.params.iter().any(|p| { + if !matches!(p.kind, GenericParamKind::Lifetime { .. }) + && let ParamName::Plain(name) = p.name + && name.name == argument_name + { + true + } else { + false + } + }); + if !has_generic { + self.tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + hir_id, + span, + errors::UnknownFormatParameterForOnUnimplementedAttr { + argument_name, + trait_name: *trait_name, + help: !directive.is_rustc_attr, + }, + ) + } + }) + } + } } /// Checks if `#[diagnostic::on_const]` is applied to a trait impl @@ -652,6 +679,9 @@ fn check_diagnostic_on_const( attr_span, DiagnosticOnConstOnlyForTraitImpls { item_span }, ); + + // We don't check the validity of generic args here...whose generics would that be, anyway? + // The traits' or the impls'? } /// Checks if an `#[inline]` is applied to a function or a closure. @@ -672,7 +702,7 @@ fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target UNUSED_ATTRIBUTES, hir_id, attr_span, - errors::InlineIgnoredForExported {}, + errors::InlineIgnoredForExported, ); } } @@ -751,8 +781,8 @@ fn check_naked(&self, hir_id: HirId, target: Target) { } } - /// Debugging aid for `object_lifetime_default` query. - fn check_object_lifetime_default(&self, hir_id: HirId) { + /// Debugging aid for the `object_lifetime_default` query. + fn check_dump_object_lifetime_defaults(&self, hir_id: HirId) { let tcx = self.tcx; if let Some(owner_id) = hir_id.as_owner() && let Some(generics) = tcx.hir_get_generics(owner_id.def_id) @@ -766,7 +796,7 @@ fn check_object_lifetime_default(&self, hir_id: HirId) { ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(), ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(), }; - tcx.dcx().emit_err(errors::ObjectLifetimeErr { span: p.span, repr }); + tcx.dcx().span_err(p.span, repr); } } } @@ -783,7 +813,7 @@ fn check_track_caller( Target::Fn => { // `#[track_caller]` is not valid on weak lang items because they are called via // `extern` declarations and `#[track_caller]` would alter their ABI. - if let Some(item) = find_attr!(attrs, AttributeKind::Lang(item, _) => item) + if let Some(item) = find_attr!(attrs, Lang(item, _) => item) && item.is_weak() { let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); @@ -795,7 +825,7 @@ fn check_track_caller( }); } - if let Some(impls) = find_attr!(attrs, AttributeKind::EiiImpls(impls) => impls) { + if let Some(impls) = find_attr!(attrs, EiiImpls(impls) => impls) { let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); for i in impls { let name = match i.resolution { @@ -854,7 +884,7 @@ fn check_target_feature( Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) | Target::Fn => { // `#[target_feature]` is not allowed in lang items. - if let Some(lang_item) = find_attr!(attrs, AttributeKind::Lang(lang, _) => lang) + if let Some(lang_item) = find_attr!(attrs, Lang(lang, _) => lang) // Calling functions with `#[target_feature]` is // not unsafe on WASM, see #84988 && !self.tcx.sess.target.is_like_wasm @@ -1162,7 +1192,7 @@ fn check_doc_attrs(&self, attr: &DocAttribute, hir_id: HirId, target: Target) { } fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute]) { - if find_attr!(attrs, AttributeKind::FfiConst(_)) { + if find_attr!(attrs, FfiConst(_)) { // `#[ffi_const]` functions cannot be `#[ffi_pure]` self.dcx().emit_err(errors::BothFfiConstAndPure { attr_span }); } @@ -1269,7 +1299,9 @@ fn check_repr( // #[repr(foo)] // #[repr(bar, align(8))] // ``` - let (reprs, first_attr_span) = find_attr!(attrs, AttributeKind::Repr { reprs, first_span } => (reprs.as_slice(), Some(*first_span))).unwrap_or((&[], None)); + let (reprs, first_attr_span) = + find_attr!(attrs, Repr { reprs, first_span } => (reprs.as_slice(), Some(*first_span))) + .unwrap_or((&[], None)); let mut int_reprs = 0; let mut is_explicit_rust = false; @@ -1303,31 +1335,27 @@ fn check_repr( } } } - ReprAttr::ReprAlign(align) => { - match target { - Target::Struct | Target::Union | Target::Enum => {} - Target::Fn | Target::Method(_) if self.tcx.features().fn_align() => { - self.dcx().emit_err(errors::ReprAlignShouldBeAlign { - span: *repr_span, - item: target.plural_name(), - }); - } - Target::Static if self.tcx.features().static_align() => { - self.dcx().emit_err(errors::ReprAlignShouldBeAlignStatic { - span: *repr_span, - item: target.plural_name(), - }); - } - _ => { - self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { - hint_span: *repr_span, - span, - }); - } + ReprAttr::ReprAlign(..) => match target { + Target::Struct | Target::Union | Target::Enum => {} + Target::Fn | Target::Method(_) if self.tcx.features().fn_align() => { + self.dcx().emit_err(errors::ReprAlignShouldBeAlign { + span: *repr_span, + item: target.plural_name(), + }); } - - self.check_align(*align, *repr_span); - } + Target::Static if self.tcx.features().static_align() => { + self.dcx().emit_err(errors::ReprAlignShouldBeAlignStatic { + span: *repr_span, + item: target.plural_name(), + }); + } + _ => { + self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { + hint_span: *repr_span, + span, + }); + } + }, ReprAttr::ReprPacked(_) => { if target != Target::Struct && target != Target::Union { self.dcx().emit_err(errors::AttrApplication::StructUnion { @@ -1414,7 +1442,7 @@ fn check_repr( // `#[rustc_pass_indirectly_in_non_rustic_abis]` if is_transparent && let Some(&pass_indirectly_span) = - find_attr!(attrs, AttributeKind::RustcPassIndirectlyInNonRusticAbis(span) => span) + find_attr!(attrs, RustcPassIndirectlyInNonRusticAbis(span) => span) { self.dcx().emit_err(errors::TransparentIncompatible { hint_spans: vec![span, pass_indirectly_span], @@ -1443,25 +1471,6 @@ fn check_repr( } } - fn check_align(&self, align: Align, span: Span) { - if align.bytes() > 2_u64.pow(29) { - // for values greater than 2^29, a different error will be emitted, make sure that happens - self.dcx().span_delayed_bug( - span, - "alignment greater than 2^29 should be errored on elsewhere", - ); - } else { - // only do this check when <= 2^29 to prevent duplicate errors: - // alignment greater than 2^29 not supported - // alignment is too large for the current target - - let max = Size::from_bits(self.tcx.sess.target.pointer_width).signed_int_max() as u64; - if align.bytes() > max { - self.dcx().emit_err(errors::InvalidReprAlignForTarget { span, size: max }); - } - } - } - /// Outputs an error for attributes that can only be applied to macros, such as /// `#[allow_internal_unsafe]` and `#[allow_internal_unstable]`. /// (Allows proc_macro functions) @@ -1563,75 +1572,84 @@ fn check_macro_export(&self, hir_id: HirId, attr_span: Span, target: Target) { fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option) { // Warn on useless empty attributes. // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute` - let note = if attr.has_any_name(&[ - sym::allow, - sym::expect, - sym::warn, - sym::deny, - sym::forbid, - sym::feature, - ]) && attr.meta_item_list().is_some_and(|list| list.is_empty()) - { - errors::UnusedNote::EmptyList { name: attr.name().unwrap() } - } else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) - && let Some(meta) = attr.meta_item_list() - && let [meta] = meta.as_slice() - && let Some(item) = meta.meta_item() - && let MetaItemKind::NameValue(_) = &item.kind - && item.path == sym::reason - { - errors::UnusedNote::NoLints { name: attr.name().unwrap() } - } else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) - && let Some(meta) = attr.meta_item_list() - && meta.iter().any(|meta| { - meta.meta_item().map_or(false, |item| item.path == sym::linker_messages) - }) - { - if hir_id != CRATE_HIR_ID { - match style { - Some(ast::AttrStyle::Outer) => { - let attr_span = attr.span(); - let bang_position = self - .tcx - .sess - .source_map() - .span_until_char(attr_span, '[') - .shrink_to_hi(); + let note = + if attr.has_any_name(&[sym::allow, sym::expect, sym::warn, sym::deny, sym::forbid]) + && attr.meta_item_list().is_some_and(|list| list.is_empty()) + { + errors::UnusedNote::EmptyList { name: attr.name().unwrap() } + } else if attr.has_any_name(&[ + sym::allow, + sym::warn, + sym::deny, + sym::forbid, + sym::expect, + ]) && let Some(meta) = attr.meta_item_list() + && let [meta] = meta.as_slice() + && let Some(item) = meta.meta_item() + && let MetaItemKind::NameValue(_) = &item.kind + && item.path == sym::reason + { + errors::UnusedNote::NoLints { name: attr.name().unwrap() } + } else if attr.has_any_name(&[ + sym::allow, + sym::warn, + sym::deny, + sym::forbid, + sym::expect, + ]) && let Some(meta) = attr.meta_item_list() + && meta.iter().any(|meta| { + meta.meta_item().map_or(false, |item| { + item.path == sym::linker_messages || item.path == sym::linker_info + }) + }) + { + if hir_id != CRATE_HIR_ID { + match style { + Some(ast::AttrStyle::Outer) => { + let attr_span = attr.span(); + let bang_position = self + .tcx + .sess + .source_map() + .span_until_char(attr_span, '[') + .shrink_to_hi(); - self.tcx.emit_node_span_lint( + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr_span, + errors::OuterCrateLevelAttr { + suggestion: errors::OuterCrateLevelAttrSuggestion { + bang_position, + }, + }, + ) + } + Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr_span, - errors::OuterCrateLevelAttr { - suggestion: errors::OuterCrateLevelAttrSuggestion { bang_position }, - }, - ) - } - Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr.span(), - errors::InnerCrateLevelAttr, - ), - }; - return; - } else { - let never_needs_link = self - .tcx - .crate_types() - .iter() - .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib)); - if never_needs_link { - errors::UnusedNote::LinkerMessagesBinaryCrateOnly - } else { + attr.span(), + errors::InnerCrateLevelAttr, + ), + }; return; + } else { + let never_needs_link = self + .tcx + .crate_types() + .iter() + .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib)); + if never_needs_link { + errors::UnusedNote::LinkerMessagesBinaryCrateOnly + } else { + return; + } } - } - } else if attr.has_name(sym::default_method_body_is_const) { - errors::UnusedNote::DefaultMethodBodyConst - } else { - return; - }; + } else if attr.has_name(sym::default_method_body_is_const) { + errors::UnusedNote::DefaultMethodBodyConst + } else { + return; + }; self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, @@ -1752,7 +1770,7 @@ fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) { } fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) { - if !find_attr!(attrs, AttributeKind::Repr { reprs, .. } => reprs.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent)) + if !find_attr!(attrs, Repr { reprs, .. } => reprs.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent)) .unwrap_or(false) { self.dcx().emit_err(errors::RustcPubTransparent { span, attr_span }); @@ -1762,7 +1780,7 @@ fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attr fn check_rustc_force_inline(&self, hir_id: HirId, attrs: &[Attribute], target: Target) { if let (Target::Closure, None) = ( target, - find_attr!(attrs, AttributeKind::Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span), + find_attr!(attrs, Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span), ) { let is_coro = matches!( self.tcx.hir_expect_expr(hir_id).kind, @@ -1775,8 +1793,8 @@ fn check_rustc_force_inline(&self, hir_id: HirId, attrs: &[Attribute], target: T let parent_span = self.tcx.def_span(parent_did); if let Some(attr_span) = find_attr!( - self.tcx.get_all_attrs(parent_did), - AttributeKind::Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span + self.tcx, parent_did, + Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span ) && is_coro { self.dcx().emit_err(errors::RustcForceInlineCoro { attr_span, span: parent_span }); @@ -1785,9 +1803,10 @@ fn check_rustc_force_inline(&self, hir_id: HirId, attrs: &[Attribute], target: T } fn check_mix_no_mangle_export(&self, hir_id: HirId, attrs: &[Attribute]) { - if let Some(export_name_span) = find_attr!(attrs, AttributeKind::ExportName { span: export_name_span, .. } => *export_name_span) + if let Some(export_name_span) = + find_attr!(attrs, ExportName { span: export_name_span, .. } => *export_name_span) && let Some(no_mangle_span) = - find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span) + find_attr!(attrs, NoMangle(no_mangle_span) => *no_mangle_span) { let no_mangle_attr = if no_mangle_span.edition() >= Edition::Edition2024 { "#[unsafe(no_mangle)]" @@ -1814,18 +1833,6 @@ fn check_mix_no_mangle_export(&self, hir_id: HirId, attrs: &[Attribute]) { } } - /// Checks if `#[autodiff]` is applied to an item other than a function item. - fn check_autodiff(&self, _hir_id: HirId, _attr: &Attribute, span: Span, target: Target) { - debug!("check_autodiff"); - match target { - Target::Fn => {} - _ => { - self.dcx().emit_err(errors::AutoDiffAttr { attr_span: span }); - self.abort.set(true); - } - } - } - fn check_loop_match(&self, hir_id: HirId, attr_span: Span, target: Target) { let node_span = self.tcx.hir_span(hir_id); @@ -1906,9 +1913,7 @@ fn visit_item(&mut self, item: &'tcx Item<'tcx>) { // In the long run, the checks should be harmonized. if let ItemKind::Macro(_, macro_def, _) = item.kind { let def_id = item.owner_id.to_def_id(); - if macro_def.macro_rules - && !find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }) - { + if macro_def.macro_rules && !find_attr!(self.tcx, def_id, MacroExport { .. }) { check_non_exported_macro_for_invalid_attrs(self.tcx, item); } } @@ -2098,7 +2103,8 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) { let attrs = tcx.hir_attrs(item.hir_id()); - if let Some(attr_span) = find_attr!(attrs, AttributeKind::Inline(i, span) if !matches!(i, InlineAttr::Force{..}) => *span) + if let Some(attr_span) = + find_attr!(attrs, Inline(i, span) if !matches!(i, InlineAttr::Force{..}) => *span) { tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span }); } diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs index fee920221e1d..f1c89face6a0 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -4,7 +4,6 @@ use rustc_abi::ExternAbi; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::find_attr; @@ -46,7 +45,7 @@ fn report_wrong_site(&self, def_id: LocalDefId) { } fn item_is_exportable(&self, def_id: LocalDefId) -> bool { - let has_attr = find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::ExportStable); + let has_attr = find_attr!(self.tcx, def_id, ExportStable); if !self.in_exportable_mod && !has_attr { return false; } @@ -82,7 +81,7 @@ fn add_exportable(&mut self, def_id: LocalDefId) { fn walk_item_with_mod(&mut self, item: &'tcx hir::Item<'tcx>) { let def_id = item.hir_id().owner.def_id; let old_exportable_mod = self.in_exportable_mod; - if find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::ExportStable) { + if find_attr!(self.tcx, def_id, ExportStable) { self.in_exportable_mod = true; } let old_seen_exportable_in_mod = std::mem::replace(&mut self.seen_exportable_in_mod, false); diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index e56d27721bdd..15c91deef247 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -10,7 +10,6 @@ use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{ErrorGuaranteed, MultiSpan}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; @@ -44,10 +43,10 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { | DefKind::TraitAlias | DefKind::AssocTy | DefKind::Fn - | DefKind::Const + | DefKind::Const { .. } | DefKind::Static { .. } | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::Macro(_) | DefKind::GlobalAsm | DefKind::Impl { .. } @@ -121,7 +120,10 @@ fn insert_def_id(&mut self, def_id: DefId) { fn handle_res(&mut self, res: Res) { match res { Res::Def( - DefKind::Const | DefKind::AssocConst | DefKind::AssocTy | DefKind::TyAlias, + DefKind::Const { .. } + | DefKind::AssocConst { .. } + | DefKind::AssocTy + | DefKind::TyAlias, def_id, ) => { self.check_def_id(def_id); @@ -381,10 +383,7 @@ fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { && let impl_of = self.tcx.parent(impl_item.owner_id.to_def_id()) && self.tcx.is_automatically_derived(impl_of) && let trait_ref = self.tcx.impl_trait_ref(impl_of).instantiate_identity() - && find_attr!( - self.tcx.get_all_attrs(trait_ref.def_id), - AttributeKind::RustcTrivialFieldReads - ) + && find_attr!(self.tcx, trait_ref.def_id, RustcTrivialFieldReads) { if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() && let Some(adt_def_id) = adt_def.did().as_local() @@ -489,7 +488,7 @@ fn mark_as_used_if_union(&mut self, adt: ty::AdtDef<'tcx>, fields: &[hir::ExprFi fn check_impl_or_impl_item_live(&mut self, local_def_id: LocalDefId) -> bool { let (impl_block_id, trait_def_id) = match self.tcx.def_kind(local_def_id) { // assoc impl items of traits are live if the corresponding trait items are live - DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => { + DefKind::AssocConst { .. } | DefKind::AssocTy | DefKind::AssocFn => { let trait_item_id = self.tcx.trait_item_of(local_def_id).and_then(|def_id| def_id.as_local()); (self.tcx.local_parent(local_def_id), trait_item_id) @@ -726,9 +725,7 @@ fn has_used_like_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { if has_allow_expect_dead_code(tcx, def_id) { Some(ComesFromAllowExpect::Yes) - } else if has_used_like_attr(tcx, def_id) - || find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Lang(..)) - { + } else if has_used_like_attr(tcx, def_id) || find_attr!(tcx, def_id, Lang(..)) { Some(ComesFromAllowExpect::No) } else { None @@ -772,7 +769,7 @@ fn maybe_record_as_seed<'tcx>( ); } } - DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy => { + DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::AssocTy => { if allow_dead_code.is_none() { let parent = tcx.local_parent(owner_id.def_id); match tcx.def_kind(parent) { @@ -815,7 +812,7 @@ fn maybe_record_as_seed<'tcx>( // global_asm! is always live. worklist.push((owner_id.def_id, ComesFromAllowExpect::No)); } - DefKind::Const => { + DefKind::Const { .. } => { if tcx.item_name(owner_id.def_id) == kw::Underscore { // `const _` is always live, as that syntax only exists for the side effects // of type checking and evaluating the constant expression, and marking them @@ -1075,7 +1072,7 @@ fn lint_at_single_level( let enum_variants_with_same_name = dead_codes .iter() .filter_map(|dead_item| { - if let DefKind::AssocFn | DefKind::AssocConst = + if let DefKind::AssocFn | DefKind::AssocConst { .. } = tcx.def_kind(dead_item.def_id) && let impl_did = tcx.local_parent(dead_item.def_id) && let DefKind::Impl { of_trait: false } = tcx.def_kind(impl_did) @@ -1148,12 +1145,12 @@ fn check_definition(&mut self, def_id: LocalDefId) { return; } match self.tcx.def_kind(def_id) { - DefKind::AssocConst + DefKind::AssocConst { .. } | DefKind::AssocTy | DefKind::AssocFn | DefKind::Fn | DefKind::Static { .. } - | DefKind::Const + | DefKind::Const { .. } | DefKind::TyAlias | DefKind::Enum | DefKind::Union diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index c7b1dcb95e33..be7ae5b97f55 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -9,7 +9,6 @@ //! //! * Compiler internal types like `Ty` and `TyCtxt` -use rustc_hir::attrs::AttributeKind; use rustc_hir::diagnostic_items::DiagnosticItems; use rustc_hir::{CRATE_OWNER_ID, OwnerId, find_attr}; use rustc_middle::query::{LocalCrate, Providers}; @@ -21,7 +20,7 @@ fn observe_item<'tcx>(tcx: TyCtxt<'tcx>, diagnostic_items: &mut DiagnosticItems, owner: OwnerId) { let attrs = tcx.hir_attrs(owner.into()); - if let Some(name) = find_attr!(attrs, AttributeKind::RustcDiagnosticItem(name) => name) { + if let Some(name) = find_attr!(attrs, RustcDiagnosticItem(name) => name) { // insert into our table collect_item(tcx, diagnostic_items, *name, owner.to_def_id()); } diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index bd737518ed47..9fc9df7604c5 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -1,8 +1,7 @@ use rustc_ast::entry::EntryPointType; use rustc_errors::codes::*; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId}; -use rustc_hir::{CRATE_HIR_ID, ItemId, Node, find_attr}; +use rustc_hir::{ItemId, Node, find_attr}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::config::{CrateType, EntryFnType, sigpipe}; @@ -29,7 +28,7 @@ fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> { } // If the user wants no main function at all, then stop here. - if find_attr!(tcx.hir_attrs(CRATE_HIR_ID), AttributeKind::NoMain) { + if find_attr!(tcx, crate, NoMain) { return None; } @@ -47,7 +46,7 @@ fn check_and_search_item(id: ItemId, ctxt: &mut EntryContext<'_>) { let attrs = ctxt.tcx.hir_attrs(id.hir_id()); let entry_point_type = rustc_ast::entry::entry_point_type( - find_attr!(attrs, AttributeKind::RustcMain), + find_attr!(attrs, RustcMain), at_root, ctxt.tcx.opt_item_name(id.owner_id.to_def_id()), ); diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 281279abd1e1..7c7698ac3bb8 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -8,25 +8,17 @@ }; use rustc_hir::Target; use rustc_hir::attrs::{MirDialect, MirPhase}; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::{MainDefinition, Ty}; -use rustc_span::{DUMMY_SP, Span, Symbol}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; use crate::check_attr::ProcMacroKind; use crate::lang_items::Duplicate; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`#[diagnostic::do_not_recommend]` can only be placed on trait implementations")] pub(crate) struct IncorrectDoNotRecommendLocation; -#[derive(Diagnostic)] -#[diag("`#[autodiff]` should be applied to a function")] -pub(crate) struct AutoDiffAttr { - #[primary_span] - #[label("not a function")] - pub attr_span: Span, -} - #[derive(Diagnostic)] #[diag("`#[loop_match]` should be applied to a loop")] pub(crate) struct LoopMatchAttr { @@ -45,7 +37,7 @@ pub(crate) struct ConstContinueAttr { pub node_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`{$no_mangle_attr}` attribute may not be used in combination with `{$export_name_attr}`")] pub(crate) struct MixedExportNameAndNoMangle { #[label("`{$no_mangle_attr}` is ignored")] @@ -62,7 +54,7 @@ pub(crate) struct MixedExportNameAndNoMangle { pub export_name_attr: &'static str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("crate-level attribute should be an inner attribute")] pub(crate) struct OuterCrateLevelAttr { #[subdiagnostic] @@ -76,7 +68,7 @@ pub(crate) struct OuterCrateLevelAttrSuggestion { pub bang_position: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("crate-level attribute should be in the root module")] pub(crate) struct InnerCrateLevelAttr; @@ -152,7 +144,7 @@ pub(crate) struct DocInlineConflict { pub spans: MultiSpan, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("this attribute can only be applied to a `use` item")] #[note( "read for more information" @@ -164,7 +156,7 @@ pub(crate) struct DocInlineOnlyUse { pub item_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("this attribute can only be applied to an `extern crate` item")] #[note( "read for more information" @@ -176,7 +168,7 @@ pub(crate) struct DocMaskedOnlyExternCrate { pub item_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("this attribute cannot be applied to an `extern crate self` item")] pub(crate) struct DocMaskedNotExternCrateSelf { #[label("not applicable on `extern crate self` items")] @@ -192,7 +184,7 @@ pub(crate) struct BothFfiConstAndPure { pub attr_span: Span, } -#[derive(LintDiagnostic)] +#[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!" @@ -245,15 +237,6 @@ pub(crate) struct ReprConflicting { } #[derive(Diagnostic)] -#[diag("alignment must not be greater than `isize::MAX` bytes", code = E0589)] -#[note("`isize::MAX` is {$size} for the current target")] -pub(crate) struct InvalidReprAlignForTarget { - #[primary_span] - pub span: Span, - pub size: u64, -} - -#[derive(LintDiagnostic)] #[diag("conflicting representation hints", code = E0566)] pub(crate) struct ReprConflictingLint; @@ -302,7 +285,7 @@ pub(crate) struct RustcForceInlineCoro { pub span: Span, } -#[derive(LintDiagnostic)] +#[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")] @@ -318,12 +301,12 @@ pub(crate) enum UnusedNote { #[note("`default_method_body_is_const` has been replaced with `const` on traits")] DefaultMethodBodyConst, #[note( - "the `linker_messages` lint can only be controlled at the root of a crate that needs to be linked" + "the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked" )] LinkerMessagesBinaryCrateOnly, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused attribute")] pub(crate) struct Unused { #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] @@ -347,7 +330,7 @@ pub(crate) struct InvalidMayDangle { pub attr_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused attribute")] pub(crate) struct UnusedDuplicate { #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] @@ -371,7 +354,7 @@ pub(crate) struct UnusedMultiple { pub name: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("this `#[deprecated]` annotation has no effect")] pub(crate) struct DeprecatedAnnotationHasNoEffect { #[suggestion( @@ -803,7 +786,7 @@ pub(crate) struct IncorrectCrateType { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "useless assignment of {$is_field_assign -> [true] field @@ -815,20 +798,12 @@ pub(crate) struct UselessAssignment<'a> { pub ty: Ty<'a>, } -#[derive(LintDiagnostic)] +#[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("{$repr}")] -pub(crate) struct ObjectLifetimeErr { - #[primary_span] - pub span: Span, - pub repr: String, -} +pub(crate) struct InlineIgnoredForExported; #[derive(Diagnostic)] pub(crate) enum AttrApplication { @@ -1041,7 +1016,7 @@ pub(crate) struct ConstStableNotStable { pub const_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum MultipleDeadCodes<'tcx> { #[diag( "{ $multiple -> @@ -1169,7 +1144,7 @@ pub(crate) struct ProcMacroBadSig { pub kind: ProcMacroKind, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "the feature `{$feature}` has been stable since {$since} and no longer requires an attribute to enable" )] @@ -1178,7 +1153,7 @@ pub(crate) struct UnnecessaryStableFeature { pub since: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "the feature `{$feature}` has been partially stabilized since {$since} and is succeeded by the feature `{$implies}`" )] @@ -1200,7 +1175,7 @@ pub(crate) struct UnnecessaryPartialStableFeature { pub implies: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("an `#[unstable]` annotation here has no effect")] #[note("see issue #55436 for more information")] pub(crate) struct IneffectiveUnstableImpl; @@ -1449,3 +1424,13 @@ pub(crate) struct FunctionNamesDuplicated { #[primary_span] pub spans: Vec, } + +#[derive(Diagnostic)] +#[diag("there is no parameter `{$argument_name}` on trait `{$trait_name}`")] +pub(crate) struct UnknownFormatParameterForOnUnimplementedAttr { + pub argument_name: Symbol, + pub trait_name: Ident, + // `false` if we're in rustc_on_unimplemented, since its syntax is a lot more complex. + #[help(r#"expect either a generic argument name or {"`{Self}`"} as format argument"#)] + pub help: bool, +} diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 23fbb9ab3a2b..e424cc09fb60 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -410,6 +410,7 @@ fn visit_ty(&mut self, t: &'v hir::Ty<'v, AmbigArg>) { TraitObject, Infer, Pat, + FieldOf, Err ] ); @@ -688,6 +689,7 @@ fn visit_ty(&mut self, t: &'v ast::Ty) { MacCall, CVarArgs, Dummy, + FieldOf, Err ] ); diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 25aea8e9f82a..b6fb9937dfa4 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -32,7 +32,7 @@ pub(crate) enum Duplicate { struct LanguageItemCollector<'ast, 'tcx> { items: LanguageItems, tcx: TyCtxt<'tcx>, - resolver: &'ast ResolverAstLowering, + resolver: &'ast ResolverAstLowering<'tcx>, // FIXME(#118552): We should probably feed def_span eagerly on def-id creation // so we can avoid constructing this map for local def-ids. item_spans: FxHashMap, @@ -42,7 +42,7 @@ struct LanguageItemCollector<'ast, 'tcx> { impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { fn new( tcx: TyCtxt<'tcx>, - resolver: &'ast ResolverAstLowering, + resolver: &'ast ResolverAstLowering<'tcx>, ) -> LanguageItemCollector<'ast, 'tcx> { LanguageItemCollector { tcx, diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index 354a98d6d0dc..68834cf7d55a 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -1,5 +1,5 @@ use rustc_abi::{HasDataLayout, TargetDataLayout}; -use rustc_hir::attrs::{AttributeKind, RustcLayoutType}; +use rustc_hir::attrs::RustcLayoutType; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::find_attr; @@ -20,8 +20,7 @@ pub fn test_layout(tcx: TyCtxt<'_>) { return; } for id in tcx.hir_crate_items(()).definitions() { - let attrs = tcx.get_all_attrs(id); - if let Some(attrs) = find_attr!(attrs, AttributeKind::RustcLayout(attrs) => attrs) { + if let Some(attrs) = find_attr!(tcx, id, RustcLayout(attrs) => attrs) { // Attribute parsing handles error reporting if matches!( tcx.def_kind(id), diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 225487cc989f..f72899a66cae 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -360,7 +360,7 @@ fn propagate_item(&mut self, res: Res) { } // Reachable constants and reachable statics can have their contents inlined // into other crates. Mark them as reachable and recurse into their body. - DefKind::Const | DefKind::AssocConst | DefKind::Static { .. } => { + DefKind::Const { .. } | DefKind::AssocConst { .. } | DefKind::Static { .. } => { self.worklist.push(def_id); } _ => { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 657b362d5ca1..ec6d48cbea2e 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -52,7 +52,7 @@ fn inherit_deprecation(def_kind: DefKind) -> bool { fn inherit_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { let def_kind = tcx.def_kind(def_id); match def_kind { - DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { + DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst { .. } => { match tcx.def_kind(tcx.local_parent(def_id)) { DefKind::Impl { .. } => true, _ => false, @@ -84,7 +84,7 @@ fn annotation_kind(tcx: TyCtxt<'_>, def_id: LocalDefId) -> AnnotationKind { } // Impl items in trait impls cannot have stability. - DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst => { + DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst { .. } => { match tcx.def_kind(tcx.local_parent(def_id)) { DefKind::Impl { of_trait: true } => AnnotationKind::Prohibited, _ => AnnotationKind::Required, @@ -96,9 +96,8 @@ fn annotation_kind(tcx: TyCtxt<'_>, def_id: LocalDefId) -> AnnotationKind { } fn lookup_deprecation_entry(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); - let depr = find_attr!(attrs, - AttributeKind::Deprecation { deprecation, span: _ } => *deprecation + let depr = find_attr!(tcx, def_id, + Deprecated { deprecation, span: _ } => *deprecation ); let Some(depr) = depr else { @@ -161,8 +160,7 @@ fn lookup_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { } // # Regular stability - let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); - let stab = find_attr!(attrs, AttributeKind::Stability { stability, span: _ } => *stability); + let stab = find_attr!(tcx, def_id, Stability { stability, span: _ } => *stability); if let Some(stab) = stab { return Some(stab); @@ -195,9 +193,8 @@ fn lookup_default_body_stability( return None; } - let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); // FIXME: check that this item can have body stability - find_attr!(attrs, AttributeKind::RustcBodyStability { stability, .. } => *stability) + find_attr!(tcx, def_id, RustcBodyStability { stability, .. } => *stability) } #[instrument(level = "debug", skip(tcx))] @@ -212,25 +209,22 @@ fn lookup_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option *stability); + find_attr!(tcx, def_id, RustcConstStability { stability, span: _ } => *stability); // After checking the immediate attributes, get rid of the span and compute implied // const stability: inherit feature gate from regular stability. let mut const_stab = const_stab - .map(|const_stab| ConstStability::from_partial(const_stab, const_stability_indirect)); + .map(|const_stab| ConstStability::from_partial(const_stab, const_stable_indirect)); // If this is a const fn but not annotated with stability markers, see if we can inherit // regular stability. @@ -341,7 +335,7 @@ macro_rules! find_attr_span { if stab.is_none() && depr.map_or(false, |d| d.attr.is_since_rustc_version()) - && let Some(span) = find_attr_span!(Deprecation) + && let Some(span) = find_attr_span!(Deprecated) { self.tcx.dcx().emit_err(errors::DeprecatedAttribute { span }); } @@ -599,15 +593,15 @@ fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { let features = self.tcx.features(); if features.staged_api() { let attrs = self.tcx.hir_attrs(item.hir_id()); - let stab = find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span)); + let stab = find_attr!(attrs, Stability{stability, span} => (*stability, *span)); // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem - let const_stab = find_attr!(attrs, AttributeKind::RustcConstStability{stability, ..} => *stability); + let const_stab = + find_attr!(attrs, RustcConstStability{stability, ..} => *stability); - let unstable_feature_stab = - find_attr!(attrs, AttributeKind::UnstableFeatureBound(i) => i) - .map(|i| i.as_slice()) - .unwrap_or_default(); + let unstable_feature_stab = find_attr!(attrs, UnstableFeatureBound(i) => i) + .map(|i| i.as_slice()) + .unwrap_or_default(); // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index e7448613cc14..514d1e2a9729 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -1,5 +1,5 @@ use rustc_errors::{Diag, EmissionGuarantee, Subdiagnostic}; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; use rustc_span::Span; @@ -46,7 +46,7 @@ pub fn new<'p, 'tcx>( } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("multiple patterns overlap on their endpoints")] #[note("you likely meant to write mutually exclusive ranges")] pub struct OverlappingRangeEndpoints { @@ -64,7 +64,7 @@ pub struct Overlap { pub range: String, // a printed pattern } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("exclusive range missing `{$max}`")] pub struct ExclusiveRangeMissingMax { #[label("this range doesn't match `{$max}` because `..` is an exclusive range")] @@ -80,7 +80,7 @@ pub struct ExclusiveRangeMissingMax { pub max: String, // a printed pattern } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("multiple ranges are one apart")] pub struct ExclusiveRangeMissingGap { #[label("this range doesn't match `{$gap}` because `..` is an exclusive range")] @@ -109,8 +109,7 @@ impl Subdiagnostic for GappedRange { fn add_to_diag(self, diag: &mut Diag<'_, G>) { let GappedRange { span, gap, first_range } = self; - // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]` - // does not support `#[subdiagnostic(eager)]`... + // FIXME(mejrs) Use `#[subdiagnostic(eager)]` instead let message = format!( "this could appear to continue range `{first_range}`, but `{gap}` isn't matched by \ either of them" @@ -119,7 +118,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("some variants are not matched explicitly")] #[help("ensure that all variants are matched explicitly by adding the suggested match arms")] #[note( @@ -131,10 +130,12 @@ pub(crate) struct NonExhaustiveOmittedPattern<'tcx> { pub uncovered: Uncovered, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("the lint level must be set on the whole match")] #[help("it no longer has any effect to set the lint level on an individual match arm")] pub(crate) struct NonExhaustiveOmittedPatternLintOnArm { + #[primary_span] + pub span: Span, #[label("remove this attribute")] pub lint_span: Span, #[suggestion( diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 3da744dc8c02..5a8adf4841c4 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -92,17 +92,13 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>( let LevelAndSource { level, src, .. } = rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data); if !matches!(level, rustc_session::lint::Level::Allow) { - let decorator = NonExhaustiveOmittedPatternLintOnArm { + rcx.tcx.dcx().emit_warn(NonExhaustiveOmittedPatternLintOnArm { + span: arm.pat.data().span, lint_span: src.span(), suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()), lint_level: level.as_str(), lint_name: "non_exhaustive_omitted_patterns", - }; - - use rustc_errors::LintDiagnostic; - let mut err = rcx.tcx.dcx().struct_span_warn(arm.pat.data().span, ""); - decorator.decorate_lint(&mut err); - err.emit(); + }); } } } diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs index af4f0d61aa11..afa91ddb3b01 100644 --- a/compiler/rustc_privacy/src/errors.rs +++ b/compiler/rustc_privacy/src/errors.rs @@ -1,6 +1,6 @@ use rustc_errors::codes::*; use rustc_errors::{DiagArgFromDisplay, MultiSpan}; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] @@ -87,7 +87,7 @@ pub(crate) struct ReportEffectiveVisibility { pub descr: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$kind} `{$descr}` from private dependency '{$krate}' in public interface")] pub(crate) struct FromPrivateDependencyInPublicInterface<'a> { pub kind: &'a str, @@ -95,7 +95,7 @@ pub(crate) struct FromPrivateDependencyInPublicInterface<'a> { pub krate: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$kind} `{$descr}` is reachable but cannot be named")] pub(crate) struct UnnameableTypesLint<'a> { #[label( @@ -111,7 +111,7 @@ pub(crate) struct UnnameableTypesLint<'a> { // Used for `private_interfaces` and `private_bounds` lints. // They will replace private-in-public errors and compatibility lints in future. // See https://rust-lang.github.io/rfcs/2145-type-privacy.html for more details. -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$ty_kind} `{$ty_descr}` is more private than the item `{$item_descr}`")] pub(crate) struct PrivateInterfacesOrBoundsLint<'a> { #[label("{$item_kind} `{$item_descr}` is reachable at visibility `{$item_vis_descr}`")] diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 9a952bb72195..63860dfdd1a6 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -21,7 +21,6 @@ use rustc_data_structures::intern::Interned; use rustc_errors::{MultiSpan, listify}; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, InferKind, Visitor}; @@ -508,7 +507,7 @@ fn update_reachability_from_macro( let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id); let attrs = self.tcx.hir_attrs(hir_id); - if find_attr!(attrs, AttributeKind::RustcMacroTransparency(x) => *x) + if find_attr!(attrs, RustcMacroTransparency(x) => *x) .unwrap_or(Transparency::fallback(md.macro_rules)) != Transparency::Opaque { @@ -575,7 +574,10 @@ fn update_macro_reachable_def( self.update(def_id, macro_ev, Level::Reachable); match def_kind { // No type privacy, so can be directly marked as reachable. - DefKind::Const | DefKind::Static { .. } | DefKind::TraitAlias | DefKind::TyAlias => { + DefKind::Const { .. } + | DefKind::Static { .. } + | DefKind::TraitAlias + | DefKind::TyAlias => { if vis.is_accessible_from(module, self.tcx) { self.update(def_id, macro_ev, Level::Reachable); } @@ -622,7 +624,7 @@ fn update_macro_reachable_def( // These have type privacy, so are not reachable unless they're // public, or are not namespaced at all. - DefKind::AssocConst + DefKind::AssocConst { .. } | DefKind::AssocTy | DefKind::ConstParam | DefKind::Ctor(_, _) @@ -680,7 +682,7 @@ fn check_def_id(&mut self, owner_id: OwnerId) { } } DefKind::ForeignTy - | DefKind::Const + | DefKind::Const { .. } | DefKind::Static { .. } | DefKind::Fn | DefKind::TyAlias => { @@ -802,7 +804,7 @@ fn check_def_id(&mut self, owner_id: OwnerId) { | DefKind::Variant | DefKind::AssocFn | DefKind::AssocTy - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::TyParam | DefKind::AnonConst | DefKind::InlineConst @@ -876,7 +878,7 @@ pub struct TestReachabilityVisitor<'a, 'tcx> { impl<'a, 'tcx> TestReachabilityVisitor<'a, 'tcx> { fn effective_visibility_diagnostic(&self, def_id: LocalDefId) { - if find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::RustcEffectiveVisibility) { + if find_attr!(self.tcx, def_id, RustcEffectiveVisibility) { let mut error_msg = String::new(); let span = self.tcx.def_span(def_id.to_def_id()); if let Some(effective_vis) = self.effective_visibilities.effective_vis(def_id) { @@ -1098,7 +1100,7 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { qpath.span(), ); } - hir::StructTailExpr::None => { + hir::StructTailExpr::None | hir::StructTailExpr::NoneWithError(_) => { let mut failed_fields = vec![]; for field in fields { let (hir_id, use_ctxt) = (field.hir_id, field.ident.span); @@ -1288,7 +1290,10 @@ fn visit_qpath(&mut self, qpath: &'tcx hir::QPath<'tcx>, id: hir::HirId, span: S let def = def.filter(|(kind, _)| { matches!( kind, - DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy | DefKind::Static { .. } + DefKind::AssocFn + | DefKind::AssocConst { .. } + | DefKind::AssocTy + | DefKind::Static { .. } ) }); if let Some((kind, def_id)) = def { @@ -1614,7 +1619,7 @@ fn check_item(&self, id: ItemId) { let def_kind = tcx.def_kind(def_id); match def_kind { - DefKind::Const | DefKind::Static { .. } | DefKind::Fn | DefKind::TyAlias => { + DefKind::Const { .. } | DefKind::Static { .. } | DefKind::Fn | DefKind::TyAlias => { if let DefKind::TyAlias = def_kind { self.check_unnameable(def_id, effective_vis); } diff --git a/compiler/rustc_public/src/compiler_interface.rs b/compiler/rustc_public/src/compiler_interface.rs index 82ee546c5991..b0ea1e0f5b84 100644 --- a/compiler/rustc_public/src/compiler_interface.rs +++ b/compiler/rustc_public/src/compiler_interface.rs @@ -562,12 +562,12 @@ pub(crate) fn mir_const_pretty(&self, cnst: &MirConst) -> String { cnst.internal(&mut *tables, cx.tcx).to_string() } - /// `Span` of an item. - pub(crate) fn span_of_an_item(&self, def_id: DefId) -> Span { + /// `Span` of a `DefId`. + pub(crate) fn span_of_a_def(&self, def_id: DefId) -> Span { let mut tables = self.tables.borrow_mut(); let cx = &*self.cx.borrow(); let did = tables[def_id]; - cx.span_of_an_item(did).stable(&mut *tables, cx) + cx.span_of_a_def(did).stable(&mut *tables, cx) } pub(crate) fn ty_const_pretty(&self, ct: TyConstId) -> String { diff --git a/compiler/rustc_public/src/crate_def.rs b/compiler/rustc_public/src/crate_def.rs index 02297c531762..7773abdcb4d2 100644 --- a/compiler/rustc_public/src/crate_def.rs +++ b/compiler/rustc_public/src/crate_def.rs @@ -2,7 +2,7 @@ //! such as, a function, a trait, an enum, and any other definitions. use crate::ty::{GenericArgs, Span, Ty, index_impl}; -use crate::{AssocItems, Crate, Symbol, ThreadLocalIndex, with}; +use crate::{Crate, Symbol, ThreadLocalIndex, with}; /// A unique identification number for each item accessible for the current compilation unit. #[derive(Clone, Copy, PartialEq, Eq, Hash)] @@ -34,6 +34,10 @@ pub fn trimmed_name(&self) -> Symbol { pub fn parent(&self) -> Option { with(|cx| cx.def_parent(*self)) } + + pub fn span(&self) -> Span { + with(|cx| cx.span_of_a_def(*self)) + } } /// A trait for retrieving information about a particular definition. @@ -68,8 +72,7 @@ fn krate(&self) -> Crate { /// Return the span of this definition. fn span(&self) -> Span { - let def_id = self.def_id(); - with(|cx| cx.span_of_an_item(def_id)) + self.def_id().span() } /// Return registered tool attributes with the given attribute name. @@ -108,14 +111,6 @@ fn ty_with_args(&self, args: &GenericArgs) -> Ty { } } -/// A trait for retrieving all items from a definition within a crate. -pub trait CrateDefItems: CrateDef { - /// Retrieve all associated items from a definition. - fn associated_items(&self) -> AssocItems { - with(|cx| cx.associated_items(self.def_id())) - } -} - #[derive(Clone, Debug, PartialEq, Eq)] pub struct Attribute { value: String, @@ -171,9 +166,3 @@ fn def_id(&self) -> DefId { impl CrateDefType for $name {} }; } - -macro_rules! impl_crate_def_items { - ( $name:ident $(;)? ) => { - impl CrateDefItems for $name {} - }; -} diff --git a/compiler/rustc_public/src/lib.rs b/compiler/rustc_public/src/lib.rs index 5da79196dd4e..e1e02e282bf2 100644 --- a/compiler/rustc_public/src/lib.rs +++ b/compiler/rustc_public/src/lib.rs @@ -30,7 +30,7 @@ use serde::Serialize; use crate::compiler_interface::with; -pub use crate::crate_def::{CrateDef, CrateDefItems, CrateDefType, DefId}; +pub use crate::crate_def::{CrateDef, CrateDefType, DefId}; pub use crate::error::*; use crate::mir::mono::StaticDef; use crate::mir::{Body, Mutability}; @@ -155,7 +155,7 @@ pub fn has_body(&self) -> bool { } pub fn span(&self) -> Span { - with(|cx| cx.span_of_an_item(self.0)) + self.0.span() } pub fn kind(&self) -> ItemKind { @@ -238,35 +238,40 @@ pub fn opaque(value: &T) -> Opaque { } macro_rules! bridge_impl { - ($name: ident, $ty: ty) => { - impl rustc_public_bridge::bridge::$name for $ty { - fn new(def: crate::DefId) -> Self { - Self(def) + ( $( $name:ident, $ty:ty ),* $(,)? ) => { + $( + impl rustc_public_bridge::bridge::$name for $ty { + fn new(def: crate::DefId) -> Self { + Self(def) + } } - } + )* }; } -bridge_impl!(CrateItem, crate::CrateItem); -bridge_impl!(AdtDef, crate::ty::AdtDef); -bridge_impl!(ForeignModuleDef, crate::ty::ForeignModuleDef); -bridge_impl!(ForeignDef, crate::ty::ForeignDef); -bridge_impl!(FnDef, crate::ty::FnDef); -bridge_impl!(ClosureDef, crate::ty::ClosureDef); -bridge_impl!(CoroutineDef, crate::ty::CoroutineDef); -bridge_impl!(CoroutineClosureDef, crate::ty::CoroutineClosureDef); -bridge_impl!(AliasDef, crate::ty::AliasDef); -bridge_impl!(ParamDef, crate::ty::ParamDef); -bridge_impl!(BrNamedDef, crate::ty::BrNamedDef); -bridge_impl!(TraitDef, crate::ty::TraitDef); -bridge_impl!(GenericDef, crate::ty::GenericDef); -bridge_impl!(ConstDef, crate::ty::ConstDef); -bridge_impl!(ImplDef, crate::ty::ImplDef); -bridge_impl!(RegionDef, crate::ty::RegionDef); -bridge_impl!(CoroutineWitnessDef, crate::ty::CoroutineWitnessDef); -bridge_impl!(AssocDef, crate::ty::AssocDef); -bridge_impl!(OpaqueDef, crate::ty::OpaqueDef); -bridge_impl!(StaticDef, crate::mir::mono::StaticDef); +#[rustfmt::skip] +bridge_impl!( + CrateItem, crate::CrateItem, + AdtDef, crate::ty::AdtDef, + ForeignModuleDef, crate::ty::ForeignModuleDef, + ForeignDef, crate::ty::ForeignDef, + FnDef, crate::ty::FnDef, + ClosureDef, crate::ty::ClosureDef, + CoroutineDef, crate::ty::CoroutineDef, + CoroutineClosureDef, crate::ty::CoroutineClosureDef, + AliasDef, crate::ty::AliasDef, + ParamDef, crate::ty::ParamDef, + BrNamedDef, crate::ty::BrNamedDef, + TraitDef, crate::ty::TraitDef, + GenericDef, crate::ty::GenericDef, + ConstDef, crate::ty::ConstDef, + ImplDef, crate::ty::ImplDef, + RegionDef, crate::ty::RegionDef, + CoroutineWitnessDef, crate::ty::CoroutineWitnessDef, + AssocDef, crate::ty::AssocDef, + OpaqueDef, crate::ty::OpaqueDef, + StaticDef, crate::mir::mono::StaticDef +); impl rustc_public_bridge::bridge::Prov for crate::ty::Prov { fn new(aid: crate::mir::alloc::AllocId) -> Self { diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index e81b32ec9acf..51757c582722 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -567,13 +567,6 @@ pub enum Rvalue { /// [#74836]: https://github.com/rust-lang/rust/issues/74836 Repeat(Operand, TyConst), - /// Transmutes a `*mut u8` into shallow-initialized `Box`. - /// - /// This is different from a normal transmute because dataflow analysis will treat the box as - /// initialized but its content as uninitialized. Like other pointer casts, this in general - /// affects alias analysis. - ShallowInitBox(Operand, Ty), - /// Creates a pointer/reference to the given thread local. /// /// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a @@ -651,7 +644,6 @@ pub fn ty(&self, locals: &[LocalDecl]) -> Result { } AggregateKind::RawPtr(ty, mutability) => Ok(Ty::new_ptr(ty, mutability)), }, - Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)), Rvalue::CopyForDeref(place) => place.ty(locals), } } diff --git a/compiler/rustc_public/src/mir/pretty.rs b/compiler/rustc_public/src/mir/pretty.rs index 5ba72965cb29..bf2655e9a789 100644 --- a/compiler/rustc_public/src/mir/pretty.rs +++ b/compiler/rustc_public/src/mir/pretty.rs @@ -383,7 +383,6 @@ fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { Rvalue::Repeat(op, cnst) => { write!(writer, "[{}; {}]", pretty_operand(op), pretty_ty_const(cnst)) } - Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::ThreadLocalRef(item) => { write!(writer, "thread_local_ref{item:?}") } diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs index 678205171ecf..e1d9cf31036e 100644 --- a/compiler/rustc_public/src/mir/visit.rs +++ b/compiler/rustc_public/src/mir/visit.rs @@ -277,10 +277,6 @@ fn super_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) self.visit_operand(op, location); self.visit_ty_const(constant, location); } - Rvalue::ShallowInitBox(op, ty) => { - self.visit_ty(ty, location); - self.visit_operand(op, location) - } Rvalue::ThreadLocalRef(_) => {} Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => { self.visit_operand(op, location); diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index d4f128f87d6f..8205d29c4534 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -7,11 +7,11 @@ use super::mir::{Body, Mutability, Safety}; use super::{DefId, Error, Symbol, with}; use crate::abi::{FnAbi, Layout}; -use crate::crate_def::{CrateDef, CrateDefItems, CrateDefType}; +use crate::crate_def::{CrateDef, CrateDefType}; use crate::mir::alloc::{AllocId, read_target_int, read_target_uint}; use crate::mir::mono::StaticDef; use crate::target::MachineInfo; -use crate::{Filename, IndexedVal, Opaque, ThreadLocalIndex}; +use crate::{AssocItems, Filename, IndexedVal, Opaque, ThreadLocalIndex}; #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct Ty(usize, ThreadLocalIndex); @@ -862,17 +862,11 @@ pub struct Discr { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] pub struct VariantDef { /// The variant index. - /// - /// ## Warning - /// Do not access this field directly! - pub idx: VariantIdx, + pub(crate) idx: VariantIdx, /// The data type where this variant comes from. /// For now, we use this to retrieve information about the variant itself so we don't need to /// cache more information. - /// - /// ## Warning - /// Do not access this field directly! - pub adt_def: AdtDef, + pub(crate) adt_def: AdtDef, } impl VariantDef { @@ -894,10 +888,7 @@ pub fn fields(&self) -> Vec { #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct FieldDef { /// The field definition. - /// - /// ## Warning - /// Do not access this field directly! This is public for the compiler to have access to it. - pub def: DefId, + pub(crate) def: DefId, /// The field name. pub name: Symbol, @@ -952,14 +943,14 @@ pub fn is_union(&self) -> bool { pub TraitDef; } -impl_crate_def_items! { - TraitDef; -} - impl TraitDef { pub fn declaration(trait_def: &TraitDef) -> TraitDecl { with(|cx| cx.trait_decl(trait_def)) } + + pub fn associated_items(&self) -> AssocItems { + with(|cx| cx.associated_items(self.def_id())) + } } crate_def! { @@ -978,15 +969,15 @@ pub fn declaration(trait_def: &TraitDef) -> TraitDecl { pub ImplDef; } -impl_crate_def_items! { - ImplDef; -} - impl ImplDef { /// Retrieve information about this implementation. pub fn trait_impl(&self) -> ImplTrait { with(|cx| cx.trait_impl(self)) } + + pub fn associated_items(&self) -> AssocItems { + with(|cx| cx.associated_items(self.def_id())) + } } crate_def! { @@ -1586,7 +1577,8 @@ fn serialize(&self, serializer: S) -> Result } }; } -pub(crate) use {index_impl, serialize_index_impl}; +pub(crate) use index_impl; +pub(crate) use serialize_index_impl; index_impl!(TyConstId); index_impl!(MirConstId); diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index a77808cfb275..d25751c81f3f 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -240,9 +240,6 @@ fn stable<'cx>( let operands = operands.iter().map(|op| op.stable(tables, cx)).collect(); crate::mir::Rvalue::Aggregate(agg_kind.stable(tables, cx), operands) } - ShallowInitBox(op, ty) => { - crate::mir::Rvalue::ShallowInitBox(op.stable(tables, cx), ty.stable(tables, cx)) - } CopyForDeref(place) => crate::mir::Rvalue::CopyForDeref(place.stable(tables, cx)), WrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"), } diff --git a/compiler/rustc_public/src/unstable/convert/stable/mod.rs b/compiler/rustc_public/src/unstable/convert/stable/mod.rs index add52fc18caa..4d550487525f 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mod.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mod.rs @@ -82,6 +82,18 @@ fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) } } +impl<'tcx> Stable<'tcx> for rustc_span::def_id::DefId { + type T = crate::DefId; + + fn stable<'cx>( + &self, + tables: &mut Tables<'cx, BridgeTys>, + _: &CompilerCtxt<'cx, BridgeTys>, + ) -> Self::T { + tables.create_def_id(*self) + } +} + impl<'tcx> Stable<'tcx> for rustc_span::Span { type T = crate::ty::Span; diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 70cdae43126c..4bf98c4f0731 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -1054,7 +1054,7 @@ fn stable<'cx>( ) -> Self::T { use crate::ty::{AssocKind, AssocTypeData}; match *self { - ty::AssocKind::Const { name } => AssocKind::Const { name: name.to_string() }, + ty::AssocKind::Const { name, .. } => AssocKind::Const { name: name.to_string() }, ty::AssocKind::Fn { name, has_self } => { AssocKind::Fn { name: name.to_string(), has_self } } diff --git a/compiler/rustc_public/src/unstable/mod.rs b/compiler/rustc_public/src/unstable/mod.rs index 583493c23d66..fa5ef5a9ca6d 100644 --- a/compiler/rustc_public/src/unstable/mod.rs +++ b/compiler/rustc_public/src/unstable/mod.rs @@ -121,9 +121,10 @@ pub(crate) fn new_item_kind(kind: DefKind) -> ItemKind { DefKind::Closure | DefKind::AssocFn | DefKind::Fn | DefKind::SyntheticCoroutineBody => { ItemKind::Fn } - DefKind::Const | DefKind::InlineConst | DefKind::AssocConst | DefKind::AnonConst => { - ItemKind::Const - } + DefKind::Const { .. } + | DefKind::InlineConst + | DefKind::AssocConst { .. } + | DefKind::AnonConst => ItemKind::Const, DefKind::Static { .. } => ItemKind::Static, DefKind::Ctor(_, rustc_hir::def::CtorKind::Const) => ItemKind::Ctor(CtorKind::Const), DefKind::Ctor(_, rustc_hir::def::CtorKind::Fn) => ItemKind::Ctor(CtorKind::Fn), diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs index 56aa22378072..4418d68c5c3a 100644 --- a/compiler/rustc_public_bridge/src/context/impls.rs +++ b/compiler/rustc_public_bridge/src/context/impls.rs @@ -554,8 +554,8 @@ pub fn def_ty_with_args(&self, item: DefId, args_ref: GenericArgsRef<'tcx>) -> T ) } - /// `Span` of an item. - pub fn span_of_an_item(&self, def_id: DefId) -> Span { + /// `Span` of a `DefId`. + pub fn span_of_a_def(&self, def_id: DefId) -> Span { self.tcx.def_span(def_id) } diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index a8edc1129481..449d2ed5334a 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -9,12 +9,10 @@ measureme = "12.0.1" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } -rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_query_impl/src/dep_kind_vtables.rs b/compiler/rustc_query_impl/src/dep_kind_vtables.rs index 99feeb86bac9..3bc2e35b02bc 100644 --- a/compiler/rustc_query_impl/src/dep_kind_vtables.rs +++ b/compiler/rustc_query_impl/src/dep_kind_vtables.rs @@ -1,10 +1,10 @@ +use rustc_middle::arena::Arena; use rustc_middle::bug; -use rustc_middle::dep_graph::{DepKindVTable, DepNodeKey, FingerprintStyle}; +use rustc_middle::dep_graph::{DepKindVTable, DepNodeKey, KeyFingerprintStyle}; use rustc_middle::query::QueryCache; -use rustc_middle::ty::TyCtxt; -use crate::plumbing::{force_from_dep_node_inner, try_load_from_on_disk_cache_inner}; -use crate::{QueryCtxt, QueryDispatcherUnerased, QueryFlags}; +use crate::GetQueryVTable; +use crate::plumbing::{force_from_dep_node_inner, promote_from_disk_inner}; /// [`DepKindVTable`] constructors for special dep kinds that aren't queries. #[expect(non_snake_case, reason = "use non-snake case to avoid collision with query names")] @@ -14,160 +14,174 @@ mod non_query { // We use this for most things when incr. comp. is turned off. pub(crate) fn Null<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: false, is_eval_always: false, - fingerprint_style: FingerprintStyle::Unit, - force_from_dep_node: Some(|_, dep_node, _| { + key_fingerprint_style: KeyFingerprintStyle::Unit, + force_from_dep_node_fn: Some(|_, dep_node, _| { bug!("force_from_dep_node: encountered {dep_node:?}") }), - try_load_from_on_disk_cache: None, - name: &"Null", + promote_from_disk_fn: None, } } // We use this for the forever-red node. pub(crate) fn Red<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: false, is_eval_always: false, - fingerprint_style: FingerprintStyle::Unit, - force_from_dep_node: Some(|_, dep_node, _| { + key_fingerprint_style: KeyFingerprintStyle::Unit, + force_from_dep_node_fn: Some(|_, dep_node, _| { bug!("force_from_dep_node: encountered {dep_node:?}") }), - try_load_from_on_disk_cache: None, - name: &"Red", + promote_from_disk_fn: None, } } pub(crate) fn SideEffect<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: false, is_eval_always: false, - fingerprint_style: FingerprintStyle::Unit, - force_from_dep_node: Some(|tcx, _, prev_index| { - tcx.dep_graph.force_diagnostic_node(QueryCtxt::new(tcx), prev_index); + key_fingerprint_style: KeyFingerprintStyle::Unit, + force_from_dep_node_fn: Some(|tcx, _, prev_index| { + tcx.dep_graph.force_side_effect(tcx, prev_index); true }), - try_load_from_on_disk_cache: None, - name: &"SideEffect", + promote_from_disk_fn: None, } } pub(crate) fn AnonZeroDeps<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: true, is_eval_always: false, - fingerprint_style: FingerprintStyle::Opaque, - force_from_dep_node: Some(|_, _, _| bug!("cannot force an anon node")), - try_load_from_on_disk_cache: None, - name: &"AnonZeroDeps", + key_fingerprint_style: KeyFingerprintStyle::Opaque, + force_from_dep_node_fn: Some(|_, _, _| bug!("cannot force an anon node")), + promote_from_disk_fn: None, } } pub(crate) fn TraitSelect<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: true, is_eval_always: false, - fingerprint_style: FingerprintStyle::Unit, - force_from_dep_node: None, - try_load_from_on_disk_cache: None, - name: &"TraitSelect", + key_fingerprint_style: KeyFingerprintStyle::Unit, + force_from_dep_node_fn: None, + promote_from_disk_fn: None, } } pub(crate) fn CompileCodegenUnit<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: false, is_eval_always: false, - fingerprint_style: FingerprintStyle::Opaque, - force_from_dep_node: None, - try_load_from_on_disk_cache: None, - name: &"CompileCodegenUnit", + key_fingerprint_style: KeyFingerprintStyle::Opaque, + force_from_dep_node_fn: None, + promote_from_disk_fn: None, } } pub(crate) fn CompileMonoItem<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: false, is_eval_always: false, - fingerprint_style: FingerprintStyle::Opaque, - force_from_dep_node: None, - try_load_from_on_disk_cache: None, - name: &"CompileMonoItem", + key_fingerprint_style: KeyFingerprintStyle::Opaque, + force_from_dep_node_fn: None, + promote_from_disk_fn: None, } } pub(crate) fn Metadata<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: false, is_eval_always: false, - fingerprint_style: FingerprintStyle::Unit, - force_from_dep_node: None, - try_load_from_on_disk_cache: None, - name: &"Metadata", + key_fingerprint_style: KeyFingerprintStyle::Unit, + force_from_dep_node_fn: None, + promote_from_disk_fn: None, } } } /// Shared implementation of the [`DepKindVTable`] constructor for queries. /// Called from macro-generated code for each query. -pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q, Cache, const FLAGS: QueryFlags>( +pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q>( + is_anon: bool, + is_cache_on_disk: bool, is_eval_always: bool, ) -> DepKindVTable<'tcx> where - Q: QueryDispatcherUnerased<'tcx, Cache, FLAGS>, - Cache: QueryCache + 'tcx, + Q: GetQueryVTable<'tcx>, { - let is_anon = FLAGS.is_anon; - let fingerprint_style = if is_anon { - FingerprintStyle::Opaque + let key_fingerprint_style = if is_anon { + KeyFingerprintStyle::Opaque } else { - >>::fingerprint_style() + ::Key::key_fingerprint_style() }; - if is_anon || !fingerprint_style.reconstructible() { - return DepKindVTable { - is_anon, - is_eval_always, - fingerprint_style, - force_from_dep_node: None, - try_load_from_on_disk_cache: None, - name: Q::NAME, - }; + // A query dep-node can only be forced or promoted if it can recover a key + // from its key fingerprint. + let can_recover = key_fingerprint_style.is_maybe_recoverable(); + if is_anon { + assert!(!can_recover); } DepKindVTable { - is_anon, is_eval_always, - fingerprint_style, - force_from_dep_node: Some(|tcx, dep_node, _| { - force_from_dep_node_inner(Q::query_dispatcher(tcx), tcx, dep_node) - }), - try_load_from_on_disk_cache: Some(|tcx, dep_node| { - try_load_from_on_disk_cache_inner(Q::query_dispatcher(tcx), tcx, dep_node) - }), - name: Q::NAME, + key_fingerprint_style, + force_from_dep_node_fn: can_recover.then_some(force_from_dep_node_inner::), + promote_from_disk_fn: (can_recover && is_cache_on_disk) + .then_some(promote_from_disk_inner::), } } -/// Helper module containing a [`DepKindVTable`] constructor for each dep kind, -/// for use with [`rustc_middle::make_dep_kind_array`]. -/// -/// That macro will check that we gave it a constructor for every known dep kind. -mod _dep_kind_vtable_ctors { - // Re-export all of the vtable constructors for non-query and query dep kinds. +macro_rules! define_dep_kind_vtables { + ( + queries { + $( + $(#[$attr:meta])* + fn $name:ident($K:ty) -> $V:ty + { + // Search for (QMODLIST) to find all occurrences of this query modifier list. + anon: $anon:literal, + arena_cache: $arena_cache:literal, + cache_on_disk: $cache_on_disk:literal, + cycle_error_handling: $cycle_error_handling:ident, + depth_limit: $depth_limit:literal, + eval_always: $eval_always:literal, + feedable: $feedable:literal, + no_hash: $no_hash:literal, + returns_error_guaranteed: $returns_error_guaranteed:literal, + separate_provide_extern: $separate_provide_extern:literal, + } + )* + } + non_queries { + $( + $(#[$nq_attr:meta])* + $nq_name:ident, + )* + } + ) => {{ + // The small number of non-query vtables: `Null`, `Red`, etc. + let nq_vtables = [ + $( + non_query::$nq_name(), + )* + ]; - // Non-query vtable constructors are defined in normal code. - pub(crate) use super::non_query::*; - // Query vtable constructors are defined via a macro. - pub(crate) use crate::_dep_kind_vtable_ctors_for_queries::*; + // The large number of query vtables. + let q_vtables: [DepKindVTable<'tcx>; _] = [ + $( + $crate::dep_kind_vtables::make_dep_kind_vtable_for_query::< + $crate::query_impl::$name::VTableGetter, + >( + $anon, + $cache_on_disk, + $eval_always, + ) + ),* + ]; + + (nq_vtables, q_vtables) + }} } -pub fn make_dep_kind_vtables<'tcx>( - arena: &'tcx rustc_middle::arena::Arena<'tcx>, -) -> &'tcx [DepKindVTable<'tcx>] { - // Create an array of vtables, one for each dep kind (non-query and query). - let dep_kind_vtables: [DepKindVTable<'tcx>; _] = - rustc_middle::make_dep_kind_array!(_dep_kind_vtable_ctors); - arena.alloc_from_iter(dep_kind_vtables) +// Create an array of vtables, one for each dep kind (non-query and query). +pub fn make_dep_kind_vtables<'tcx>(arena: &'tcx Arena<'tcx>) -> &'tcx [DepKindVTable<'tcx>] { + let (nq_vtables, q_vtables) = + rustc_middle::rustc_with_all_queries! { define_dep_kind_vtables! }; + + // Non-query vtables must come before query vtables, to match the order of `DepKind`. + arena.alloc_from_iter(nq_vtables.into_iter().chain(q_vtables.into_iter())) } diff --git a/compiler/rustc_query_impl/src/error.rs b/compiler/rustc_query_impl/src/error.rs index 6d3eb2950982..44d53f87aae2 100644 --- a/compiler/rustc_query_impl/src/error.rs +++ b/compiler/rustc_query_impl/src/error.rs @@ -35,9 +35,9 @@ pub(crate) struct CycleStack { #[derive(Subdiagnostic)] pub(crate) enum StackCount { #[note("...which immediately requires {$stack_bottom} again")] - Single, + Single { stack_bottom: String }, #[note("...which again requires {$stack_bottom}, completing the cycle")] - Multiple, + Multiple { stack_bottom: String }, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 4df55e46e13e..11749f3fb82d 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -3,24 +3,27 @@ use rustc_data_structures::hash_table::{Entry, HashTable}; use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_data_structures::{outline, sharded, sync}; -use rustc_errors::{Diag, FatalError, StashKey}; -use rustc_middle::dep_graph::{DepGraphData, DepNodeKey, DepsType, HasDepContext}; +use rustc_errors::{FatalError, StashKey}; +use rustc_middle::dep_graph::{DepGraphData, DepNodeKey, SerializedDepNodeIndex}; +use rustc_middle::query::plumbing::QueryVTable; use rustc_middle::query::{ - ActiveKeyStatus, CycleError, CycleErrorHandling, QueryCache, QueryJob, QueryJobId, QueryLatch, - QueryMode, QueryStackDeferred, QueryStackFrame, QueryState, + ActiveKeyStatus, CycleError, CycleErrorHandling, EnsureMode, QueryCache, QueryJob, QueryJobId, + QueryKey, QueryLatch, QueryMode, QueryState, }; use rustc_middle::ty::TyCtxt; use rustc_middle::verify_ich::incremental_verify_ich; use rustc_span::{DUMMY_SP, Span}; -use crate::dep_graph::{DepContext, DepNode, DepNodeIndex}; +use crate::collect_active_jobs_from_all_queries; +use crate::dep_graph::{DepNode, DepNodeIndex}; use crate::job::{QueryJobInfo, QueryJobMap, find_cycle_in_stack, report_cycle}; -use crate::{QueryCtxt, QueryFlags, SemiDynamicQueryDispatcher}; +use crate::plumbing::{current_query_job, next_job_id, start_query}; #[inline] -fn equivalent_key(k: &K) -> impl Fn(&(K, V)) -> bool + '_ { - move |x| x.0 == *k +fn equivalent_key(k: K) -> impl Fn(&(K, V)) -> bool { + move |x| x.0 == k } /// Obtains the enclosed [`QueryJob`], or panics if this query evaluation @@ -40,18 +43,25 @@ pub(crate) fn all_inactive<'tcx, K>(state: &QueryState<'tcx, K>) -> bool { /// Internal plumbing for collecting the set of active jobs for this query. /// -/// Should only be called from `gather_active_jobs`. -pub(crate) fn gather_active_jobs_inner<'tcx, K: Copy>( - state: &QueryState<'tcx, K>, +/// Should only be called from `collect_active_jobs_from_all_queries`. +/// +/// (We arbitrarily use the word "gather" when collecting the jobs for +/// each individual query, so that we have distinct function names to +/// grep for.) +pub(crate) fn gather_active_jobs<'tcx, C>( + query: &'tcx QueryVTable<'tcx, C>, tcx: TyCtxt<'tcx>, - make_frame: fn(TyCtxt<'tcx>, K) -> QueryStackFrame>, require_complete: bool, job_map_out: &mut QueryJobMap<'tcx>, // Out-param; job info is gathered into this map -) -> Option<()> { +) -> Option<()> +where + C: QueryCache, + QueryVTable<'tcx, C>: DynSync, +{ let mut active = Vec::new(); // Helper to gather active jobs from a single shard. - let mut gather_shard_jobs = |shard: &HashTable<(K, ActiveKeyStatus<'tcx>)>| { + let mut gather_shard_jobs = |shard: &HashTable<(C::Key, ActiveKeyStatus<'tcx>)>| { for (k, v) in shard.iter() { if let ActiveKeyStatus::Started(ref job) = *v { active.push((*k, job.clone())); @@ -61,22 +71,33 @@ pub(crate) fn gather_active_jobs_inner<'tcx, K: Copy>( // Lock shards and gather jobs from each shard. if require_complete { - for shard in state.active.lock_shards() { + for shard in query.state.active.lock_shards() { gather_shard_jobs(&shard); } } else { // We use try_lock_shards here since we are called from the // deadlock handler, and this shouldn't be locked. - for shard in state.active.try_lock_shards() { - let shard = shard?; - gather_shard_jobs(&shard); + for shard in query.state.active.try_lock_shards() { + // This can be called during unwinding, and the function has a `try_`-prefix, so + // don't `unwrap()` here, just manually check for `None` and do best-effort error + // reporting. + match shard { + None => { + tracing::warn!( + "Failed to collect active jobs for query with name `{}`!", + query.name + ); + return None; + } + Some(shard) => gather_shard_jobs(&shard), + } } } // Call `make_frame` while we're not holding a `state.active` lock as `make_frame` may call // queries leading to a deadlock. for (key, job) in active { - let frame = make_frame(tcx, key); + let frame = crate::plumbing::create_deferred_query_stack_frame(tcx, query, key); job_map_out.insert(job.id, QueryJobInfo { frame, job }); } @@ -99,34 +120,20 @@ struct ActiveJobGuard<'tcx, K> #[cold] #[inline(never)] -fn mk_cycle<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, +fn mk_cycle<'tcx, C: QueryCache>( + query: &'tcx QueryVTable<'tcx, C>, + tcx: TyCtxt<'tcx>, cycle_error: CycleError, ) -> C::Value { - let error = report_cycle(qcx.tcx.sess, &cycle_error); - handle_cycle_error(query, qcx, &cycle_error, error) -} - -fn handle_cycle_error<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, - cycle_error: &CycleError, - error: Diag<'_>, -) -> C::Value { - match query.cycle_error_handling() { + let error = report_cycle(tcx.sess, &cycle_error); + match query.cycle_error_handling { CycleErrorHandling::Error => { let guar = error.emit(); - query.value_from_cycle_error(qcx.tcx, cycle_error, guar) - } - CycleErrorHandling::Fatal => { - error.emit(); - qcx.tcx.dcx().abort_if_errors(); - unreachable!() + (query.value_from_cycle_error)(tcx, cycle_error, guar) } CycleErrorHandling::DelayBug => { let guar = error.delay_as_bug(); - query.value_from_cycle_error(qcx.tcx, cycle_error, guar) + (query.value_from_cycle_error)(tcx, cycle_error, guar) } CycleErrorHandling::Stash => { let guar = if let Some(root) = cycle_error.cycle.first() @@ -136,7 +143,7 @@ fn handle_cycle_error<'tcx, C: QueryCache, const FLAGS: QueryFlags>( } else { error.emit() }; - query.value_from_cycle_error(qcx.tcx, cycle_error, guar) + (query.value_from_cycle_error)(tcx, cycle_error, guar) } } } @@ -166,7 +173,7 @@ fn complete(self, cache: &C, result: C::Value, dep_node_index: DepNodeIndex) // since unwinding also wants to look at this map, this can also prevent a double // panic. let mut shard = state.active.lock_shard_by_hash(key_hash); - match shard.find_entry(key_hash, equivalent_key(&key)) { + match shard.find_entry(key_hash, equivalent_key(key)) { Err(_) => None, Ok(occupied) => Some(occupied.remove().0.1), } @@ -188,7 +195,7 @@ fn drop(&mut self) { let Self { state, key, key_hash } = *self; let job = { let mut shard = state.active.lock_shard_by_hash(key_hash); - match shard.find_entry(key_hash, equivalent_key(&key)) { + match shard.find_entry(key_hash, equivalent_key(key)) { Err(_) => panic!(), Ok(occupied) => { let ((key, value), vacant) = occupied.remove(); @@ -205,27 +212,26 @@ fn drop(&mut self) { #[cold] #[inline(never)] -fn cycle_error<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, +fn cycle_error<'tcx, C: QueryCache>( + query: &'tcx QueryVTable<'tcx, C>, + tcx: TyCtxt<'tcx>, try_execute: QueryJobId, span: Span, ) -> (C::Value, Option) { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - let job_map = qcx - .collect_active_jobs_from_all_queries(false) + let job_map = collect_active_jobs_from_all_queries(tcx, false) .ok() .expect("failed to collect active queries"); - let error = find_cycle_in_stack(try_execute, job_map, &qcx.current_query_job(), span); - (mk_cycle(query, qcx, error.lift()), None) + let error = find_cycle_in_stack(try_execute, job_map, ¤t_query_job(), span); + (mk_cycle(query, tcx, error.lift()), None) } #[inline(always)] -fn wait_for_query<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, +fn wait_for_query<'tcx, C: QueryCache>( + query: &'tcx QueryVTable<'tcx, C>, + tcx: TyCtxt<'tcx>, span: Span, key: C::Key, latch: QueryLatch<'tcx>, @@ -234,51 +240,53 @@ fn wait_for_query<'tcx, C: QueryCache, const FLAGS: QueryFlags>( // For parallel queries, we'll block and wait until the query running // in another thread has completed. Record how long we wait in the // self-profiler. - let query_blocked_prof_timer = qcx.tcx.prof.query_blocked(); + let query_blocked_prof_timer = tcx.prof.query_blocked(); // With parallel queries we might just have to wait on some other // thread. - let result = latch.wait_on(qcx.tcx, current, span); + let result = latch.wait_on(tcx, current, span); match result { Ok(()) => { - let Some((v, index)) = query.query_cache(qcx).lookup(&key) else { + let Some((v, index)) = query.cache.lookup(&key) else { outline(|| { // We didn't find the query result in the query cache. Check if it was // poisoned due to a panic instead. let key_hash = sharded::make_hash(&key); - let shard = query.query_state(qcx).active.lock_shard_by_hash(key_hash); - match shard.find(key_hash, equivalent_key(&key)) { + let shard = query.state.active.lock_shard_by_hash(key_hash); + match shard.find(key_hash, equivalent_key(key)) { // The query we waited on panicked. Continue unwinding here. Some((_, ActiveKeyStatus::Poisoned)) => FatalError.raise(), _ => panic!( "query '{}' result must be in the cache or the query must be poisoned after a wait", - query.name() + query.name ), } }) }; - qcx.tcx.prof.query_cache_hit(index.into()); + tcx.prof.query_cache_hit(index.into()); query_blocked_prof_timer.finish_with_query_invocation_id(index.into()); (v, Some(index)) } - Err(cycle) => (mk_cycle(query, qcx, cycle.lift()), None), + Err(cycle) => (mk_cycle(query, tcx, cycle.lift()), None), } } +/// Shared main part of both [`execute_query_incr_inner`] and [`execute_query_non_incr_inner`]. #[inline(never)] -fn try_execute_query<'tcx, C: QueryCache, const FLAGS: QueryFlags, const INCR: bool>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, +fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>( + query: &'tcx QueryVTable<'tcx, C>, + tcx: TyCtxt<'tcx>, span: Span, key: C::Key, + // If present, some previous step has already created a `DepNode` for this + // query+key, which we should reuse instead of creating a new one. dep_node: Option, ) -> (C::Value, Option) { - let state = query.query_state(qcx); let key_hash = sharded::make_hash(&key); - let mut state_lock = state.active.lock_shard_by_hash(key_hash); + let mut state_lock = query.state.active.lock_shard_by_hash(key_hash); // For the parallel compiler we need to check both the query cache and query state structures // while holding the state lock to ensure that 1) the query has not yet completed and 2) the @@ -286,27 +294,27 @@ fn try_execute_query<'tcx, C: QueryCache, const FLAGS: QueryFlags, const INCR: b // re-executing the query since `try_start` only checks that the query is not currently // executing, but another thread may have already completed the query and stores it result // in the query cache. - if qcx.tcx.sess.threads() > 1 { - if let Some((value, index)) = query.query_cache(qcx).lookup(&key) { - qcx.tcx.prof.query_cache_hit(index.into()); + if tcx.sess.threads() > 1 { + if let Some((value, index)) = query.cache.lookup(&key) { + tcx.prof.query_cache_hit(index.into()); return (value, Some(index)); } } - let current_job_id = qcx.current_query_job(); + let current_job_id = current_query_job(); - match state_lock.entry(key_hash, equivalent_key(&key), |(k, _)| sharded::make_hash(k)) { + match state_lock.entry(key_hash, equivalent_key(key), |(k, _)| sharded::make_hash(k)) { Entry::Vacant(entry) => { // Nothing has computed or is computing the query, so we start a new job and insert it in the // state map. - let id = qcx.next_job_id(); + let id = next_job_id(tcx); let job = QueryJob::new(id, span, current_job_id); entry.insert((key, ActiveKeyStatus::Started(job))); // Drop the lock before we start executing the query drop(state_lock); - execute_job::(query, qcx, state, key, key_hash, id, dep_node) + execute_job::(query, tcx, key, key_hash, id, dep_node) } Entry::Occupied(mut entry) => { match &mut entry.get_mut().1 { @@ -318,15 +326,15 @@ fn try_execute_query<'tcx, C: QueryCache, const FLAGS: QueryFlags, const INCR: b // Only call `wait_for_query` if we're using a Rayon thread pool // as it will attempt to mark the worker thread as blocked. - return wait_for_query(query, qcx, span, key, latch, current_job_id); + wait_for_query(query, tcx, span, key, latch, current_job_id) + } else { + let id = job.id; + drop(state_lock); + + // If we are single-threaded we know that we have cycle error, + // so we just return the error. + cycle_error(query, tcx, id, span) } - - let id = job.id; - drop(state_lock); - - // If we are single-threaded we know that we have cycle error, - // so we just return the error. - cycle_error(query, qcx, id, span) } ActiveKeyStatus::Poisoned => FatalError.raise(), } @@ -335,10 +343,9 @@ fn try_execute_query<'tcx, C: QueryCache, const FLAGS: QueryFlags, const INCR: b } #[inline(always)] -fn execute_job<'tcx, C: QueryCache, const FLAGS: QueryFlags, const INCR: bool>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, - state: &'tcx QueryState<'tcx, C::Key>, +fn execute_job<'tcx, C: QueryCache, const INCR: bool>( + query: &'tcx QueryVTable<'tcx, C>, + tcx: TyCtxt<'tcx>, key: C::Key, key_hash: u64, id: QueryJobId, @@ -346,136 +353,144 @@ fn execute_job<'tcx, C: QueryCache, const FLAGS: QueryFlags, const INCR: bool>( ) -> (C::Value, Option) { // Set up a guard object that will automatically poison the query if a // panic occurs while executing the query (or any intermediate plumbing). - let job_guard = ActiveJobGuard { state, key, key_hash }; + let job_guard = ActiveJobGuard { state: &query.state, key, key_hash }; - debug_assert_eq!(qcx.tcx.dep_graph.is_fully_enabled(), INCR); + debug_assert_eq!(tcx.dep_graph.is_fully_enabled(), INCR); // Delegate to another function to actually execute the query job. - let (result, dep_node_index) = if INCR { - execute_job_incr(query, qcx, qcx.tcx.dep_graph.data().unwrap(), key, dep_node, id) + let (value, dep_node_index) = if INCR { + execute_job_incr(query, tcx, key, dep_node, id) } else { - execute_job_non_incr(query, qcx, key, id) + execute_job_non_incr(query, tcx, key, id) }; - let cache = query.query_cache(qcx); - if query.feedable() { + let cache = &query.cache; + if query.feedable { // We should not compute queries that also got a value via feeding. // This can't happen, as query feeding adds the very dependencies to the fed query // as its feeding query had. So if the fed query is red, so is its feeder, which will // get evaluated first, and re-feed the query. - if let Some((cached_result, _)) = cache.lookup(&key) { - let Some(hasher) = query.hash_result() else { + if let Some((cached_value, _)) = cache.lookup(&key) { + let Some(hash_value_fn) = query.hash_value_fn else { panic!( "no_hash fed query later has its value computed.\n\ Remove `no_hash` modifier to allow recomputation.\n\ The already cached value: {}", - (query.format_value())(&cached_result) + (query.format_value)(&cached_value) ); }; - let (old_hash, new_hash) = qcx.dep_context().with_stable_hashing_context(|mut hcx| { - (hasher(&mut hcx, &cached_result), hasher(&mut hcx, &result)) + let (old_hash, new_hash) = tcx.with_stable_hashing_context(|mut hcx| { + (hash_value_fn(&mut hcx, &cached_value), hash_value_fn(&mut hcx, &value)) }); - let formatter = query.format_value(); + let formatter = query.format_value; if old_hash != new_hash { // We have an inconsistency. This can happen if one of the two // results is tainted by errors. assert!( - qcx.tcx.dcx().has_errors().is_some(), + tcx.dcx().has_errors().is_some(), "Computed query value for {:?}({:?}) is inconsistent with fed value,\n\ computed={:#?}\nfed={:#?}", - query.dep_kind(), + query.dep_kind, key, - formatter(&result), - formatter(&cached_result), + formatter(&value), + formatter(&cached_value), ); } } } // Tell the guard to perform completion bookkeeping, and also to not poison the query. - job_guard.complete(cache, result, dep_node_index); + job_guard.complete(cache, value, dep_node_index); - (result, Some(dep_node_index)) + (value, Some(dep_node_index)) } // Fast path for when incr. comp. is off. #[inline(always)] -fn execute_job_non_incr<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, +fn execute_job_non_incr<'tcx, C: QueryCache>( + query: &'tcx QueryVTable<'tcx, C>, + tcx: TyCtxt<'tcx>, key: C::Key, job_id: QueryJobId, ) -> (C::Value, DepNodeIndex) { - debug_assert!(!qcx.tcx.dep_graph.is_fully_enabled()); + debug_assert!(!tcx.dep_graph.is_fully_enabled()); - // Fingerprint the key, just to assert that it doesn't - // have anything we don't consider hashable - if cfg!(debug_assertions) { - let _ = key.to_fingerprint(qcx.tcx); - } - - let prof_timer = qcx.tcx.prof.query_provider(); + let prof_timer = tcx.prof.query_provider(); // Call the query provider. - let result = qcx.start_query(job_id, query.depth_limit(), || query.invoke_provider(qcx, key)); - let dep_node_index = qcx.tcx.dep_graph.next_virtual_depnode_index(); + let value = start_query(job_id, query.depth_limit, || (query.invoke_provider_fn)(tcx, key)); + let dep_node_index = tcx.dep_graph.next_virtual_depnode_index(); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - // Similarly, fingerprint the result to assert that - // it doesn't have anything not considered hashable. - if cfg!(debug_assertions) - && let Some(hash_result) = query.hash_result() - { - qcx.dep_context().with_stable_hashing_context(|mut hcx| { - hash_result(&mut hcx, &result); - }); + // Sanity: Fingerprint the key and the result to assert they don't contain anything unhashable. + if cfg!(debug_assertions) { + let _ = key.to_fingerprint(tcx); + if let Some(hash_value_fn) = query.hash_value_fn { + tcx.with_stable_hashing_context(|mut hcx| { + hash_value_fn(&mut hcx, &value); + }); + } } - (result, dep_node_index) + (value, dep_node_index) } #[inline(always)] -fn execute_job_incr<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, - dep_graph_data: &DepGraphData, +fn execute_job_incr<'tcx, C: QueryCache>( + query: &'tcx QueryVTable<'tcx, C>, + tcx: TyCtxt<'tcx>, key: C::Key, mut dep_node_opt: Option, job_id: QueryJobId, ) -> (C::Value, DepNodeIndex) { - if !query.anon() && !query.eval_always() { + let dep_graph_data = + tcx.dep_graph.data().expect("should always be present in incremental mode"); + + if !query.anon && !query.eval_always { // `to_dep_node` is expensive for some `DepKind`s. - let dep_node = dep_node_opt.get_or_insert_with(|| query.construct_dep_node(qcx.tcx, &key)); + let dep_node = + dep_node_opt.get_or_insert_with(|| DepNode::construct(tcx, query.dep_kind, &key)); // The diagnostics for this query will be promoted to the current session during // `try_mark_green()`, so we can ignore them here. - if let Some(ret) = qcx.start_query(job_id, false, || { - try_load_from_disk_and_cache_in_memory(query, dep_graph_data, qcx, &key, dep_node) + if let Some(ret) = start_query(job_id, false, || try { + let (prev_index, dep_node_index) = dep_graph_data.try_mark_green(tcx, dep_node)?; + let value = load_from_disk_or_invoke_provider_green( + tcx, + dep_graph_data, + query, + key, + dep_node, + prev_index, + dep_node_index, + ); + (value, dep_node_index) }) { return ret; } } - let prof_timer = qcx.tcx.prof.query_provider(); + let prof_timer = tcx.prof.query_provider(); - let (result, dep_node_index) = qcx.start_query(job_id, query.depth_limit(), || { - if query.anon() { + let (result, dep_node_index) = start_query(job_id, query.depth_limit, || { + if query.anon { // Call the query provider inside an anon task. - return dep_graph_data.with_anon_task_inner(qcx.tcx, query.dep_kind(), || { - query.invoke_provider(qcx, key) + return dep_graph_data.with_anon_task_inner(tcx, query.dep_kind, || { + (query.invoke_provider_fn)(tcx, key) }); } // `to_dep_node` is expensive for some `DepKind`s. - let dep_node = dep_node_opt.unwrap_or_else(|| query.construct_dep_node(qcx.tcx, &key)); + let dep_node = + dep_node_opt.unwrap_or_else(|| DepNode::construct(tcx, query.dep_kind, &key)); // Call the query provider. dep_graph_data.with_task( dep_node, - (qcx, query), - key, - |(qcx, query), key| query.invoke_provider(qcx, key), - query.hash_result(), + tcx, + (query, key), + |tcx, (query, key)| (query.invoke_provider_fn)(tcx, key), + query.hash_value_fn, ) }); @@ -484,29 +499,32 @@ fn execute_job_incr<'tcx, C: QueryCache, const FLAGS: QueryFlags>( (result, dep_node_index) } +/// Given that the dep node for this query+key is green, obtain a value for it +/// by loading one from disk if possible, or by invoking its query provider if +/// necessary. #[inline(always)] -fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - dep_graph_data: &DepGraphData, - qcx: QueryCtxt<'tcx>, - key: &C::Key, +fn load_from_disk_or_invoke_provider_green<'tcx, C: QueryCache>( + tcx: TyCtxt<'tcx>, + dep_graph_data: &DepGraphData, + query: &'tcx QueryVTable<'tcx, C>, + key: C::Key, dep_node: &DepNode, -) -> Option<(C::Value, DepNodeIndex)> { + prev_index: SerializedDepNodeIndex, + dep_node_index: DepNodeIndex, +) -> C::Value { // Note this function can be called concurrently from the same query // We must ensure that this is handled correctly. - let (prev_dep_node_index, dep_node_index) = dep_graph_data.try_mark_green(qcx, dep_node)?; - - debug_assert!(dep_graph_data.is_index_green(prev_dep_node_index)); + debug_assert!(dep_graph_data.is_index_green(prev_index)); // First we try to load the result from the on-disk cache. // Some things are never cached on disk. - if let Some(result) = query.try_load_from_disk(qcx, key, prev_dep_node_index, dep_node_index) { - if std::intrinsics::unlikely(qcx.tcx.sess.opts.unstable_opts.query_dep_graph) { + if let Some(value) = (query.try_load_from_disk_fn)(tcx, key, prev_index, dep_node_index) { + if std::intrinsics::unlikely(tcx.sess.opts.unstable_opts.query_dep_graph) { dep_graph_data.mark_debug_loaded_from_disk(*dep_node) } - let prev_fingerprint = dep_graph_data.prev_fingerprint_of(prev_dep_node_index); + let prev_fingerprint = dep_graph_data.prev_value_fingerprint_of(prev_index); // If `-Zincremental-verify-ich` is specified, re-hash results from // the cache and make sure that they have the expected fingerprint. // @@ -516,43 +534,43 @@ fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache, const FLAGS: Quer // give us some coverage of potential bugs though. let try_verify = prev_fingerprint.split().1.as_u64().is_multiple_of(32); if std::intrinsics::unlikely( - try_verify || qcx.tcx.sess.opts.unstable_opts.incremental_verify_ich, + try_verify || tcx.sess.opts.unstable_opts.incremental_verify_ich, ) { incremental_verify_ich( - qcx.tcx, + tcx, dep_graph_data, - &result, - prev_dep_node_index, - query.hash_result(), - query.format_value(), + &value, + prev_index, + query.hash_value_fn, + query.format_value, ); } - return Some((result, dep_node_index)); + return value; } // We always expect to find a cached result for things that // can be forced from `DepNode`. debug_assert!( - !query.will_cache_on_disk_for_key(qcx.tcx, key) - || !qcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(), + !(query.will_cache_on_disk_for_key_fn)(tcx, key) + || !tcx.key_fingerprint_style(dep_node.kind).is_maybe_recoverable(), "missing on-disk cache entry for {dep_node:?}" ); // Sanity check for the logic in `ensure`: if the node is green and the result loadable, // we should actually be able to load it. debug_assert!( - !query.is_loadable_from_disk(qcx, key, prev_dep_node_index), + !(query.is_loadable_from_disk_fn)(tcx, key, prev_index), "missing on-disk cache entry for loadable {dep_node:?}" ); // We could not load a result from the on-disk cache, so // recompute. - let prof_timer = qcx.tcx.prof.query_provider(); + let prof_timer = tcx.prof.query_provider(); // The dep-graph for this computation is already in-place. // Call the query provider. - let result = qcx.tcx.dep_graph.with_ignore(|| query.invoke_provider(qcx, *key)); + let value = tcx.dep_graph.with_ignore(|| (query.invoke_provider_fn)(tcx, key)); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -566,43 +584,51 @@ fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache, const FLAGS: Quer // See issue #82920 for an example of a miscompilation that would get turned into // an ICE by this check incremental_verify_ich( - qcx.tcx, + tcx, dep_graph_data, - &result, - prev_dep_node_index, - query.hash_result(), - query.format_value(), + &value, + prev_index, + query.hash_value_fn, + query.format_value, ); - Some((result, dep_node_index)) + value } -/// Ensure that either this query has all green inputs or been executed. -/// Executing `query::ensure(D)` is considered a read of the dep-node `D`. -/// Returns true if the query should still run. +/// Return value struct for [`check_if_ensure_can_skip_execution`]. +struct EnsureCanSkip { + /// If true, the current `tcx.ensure_ok()` or `tcx.ensure_done()` query + /// can return early without actually trying to execute. + skip_execution: bool, + /// A dep node that was prepared while checking whether execution can be + /// skipped, to be reused by execution itself if _not_ skipped. + dep_node: Option, +} + +/// Checks whether a `tcx.ensure_ok()` or `tcx.ensure_done()` query call can +/// return early without actually trying to execute. /// -/// This function is particularly useful when executing passes for their -/// side-effects -- e.g., in order to report errors for erroneous programs. -/// -/// Note: The optimization is only available during incr. comp. +/// This only makes sense during incremental compilation, because it relies +/// on having the dependency graph (and in some cases a disk-cached value) +/// from the previous incr-comp session. #[inline(never)] -fn ensure_must_run<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, - key: &C::Key, - check_cache: bool, -) -> (bool, Option) { - if query.eval_always() { - return (true, None); +fn check_if_ensure_can_skip_execution<'tcx, C: QueryCache>( + query: &'tcx QueryVTable<'tcx, C>, + tcx: TyCtxt<'tcx>, + key: C::Key, + ensure_mode: EnsureMode, +) -> EnsureCanSkip { + // Queries with `eval_always` should never skip execution. + if query.eval_always { + return EnsureCanSkip { skip_execution: false, dep_node: None }; } // Ensuring an anonymous query makes no sense - assert!(!query.anon()); + assert!(!query.anon); - let dep_node = query.construct_dep_node(qcx.tcx, key); + let dep_node = DepNode::construct(tcx, query.dep_kind, &key); - let dep_graph = &qcx.tcx.dep_graph; - let serialized_dep_node_index = match dep_graph.try_mark_green(qcx, &dep_node) { + let serialized_dep_node_index = match tcx.dep_graph.try_mark_green(tcx, &dep_node) { None => { // A None return from `try_mark_green` means that this is either // a new dep node or that the dep node has already been marked red. @@ -610,81 +636,98 @@ fn ensure_must_run<'tcx, C: QueryCache, const FLAGS: QueryFlags>( // DepNodeIndex. We must invoke the query itself. The performance cost // this introduces should be negligible as we'll immediately hit the // in-memory cache, or another query down the line will. - return (true, Some(dep_node)); + return EnsureCanSkip { skip_execution: false, dep_node: Some(dep_node) }; } Some((serialized_dep_node_index, dep_node_index)) => { - dep_graph.read_index(dep_node_index); - qcx.tcx.prof.query_cache_hit(dep_node_index.into()); + tcx.dep_graph.read_index(dep_node_index); + tcx.prof.query_cache_hit(dep_node_index.into()); serialized_dep_node_index } }; - // We do not need the value at all, so do not check the cache. - if !check_cache { - return (false, None); + match ensure_mode { + EnsureMode::Ok => { + // In ensure-ok mode, we can skip execution for this key if the node + // is green. It must have succeeded in the previous session, and + // therefore would succeed in the current session if executed. + EnsureCanSkip { skip_execution: true, dep_node: None } + } + EnsureMode::Done => { + // In ensure-done mode, we can only skip execution for this key if + // there's a disk-cached value available to load later if needed, + // which guarantees the query provider will never run for this key. + let is_loadable = (query.is_loadable_from_disk_fn)(tcx, key, serialized_dep_node_index); + EnsureCanSkip { skip_execution: is_loadable, dep_node: Some(dep_node) } + } } - - let loadable = query.is_loadable_from_disk(qcx, key, serialized_dep_node_index); - (!loadable, Some(dep_node)) } +/// Called by a macro-generated impl of [`QueryVTable::execute_query_fn`], +/// in non-incremental mode. #[inline(always)] -pub(super) fn get_query_non_incr<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, +pub(super) fn execute_query_non_incr_inner<'tcx, C: QueryCache>( + query: &'tcx QueryVTable<'tcx, C>, + tcx: TyCtxt<'tcx>, span: Span, key: C::Key, ) -> C::Value { - debug_assert!(!qcx.tcx.dep_graph.is_fully_enabled()); + debug_assert!(!tcx.dep_graph.is_fully_enabled()); - ensure_sufficient_stack(|| try_execute_query::(query, qcx, span, key, None).0) + ensure_sufficient_stack(|| try_execute_query::(query, tcx, span, key, None).0) } +/// Called by a macro-generated impl of [`QueryVTable::execute_query_fn`], +/// in incremental mode. #[inline(always)] -pub(super) fn get_query_incr<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, +pub(super) fn execute_query_incr_inner<'tcx, C: QueryCache>( + query: &'tcx QueryVTable<'tcx, C>, + tcx: TyCtxt<'tcx>, span: Span, key: C::Key, mode: QueryMode, ) -> Option { - debug_assert!(qcx.tcx.dep_graph.is_fully_enabled()); + debug_assert!(tcx.dep_graph.is_fully_enabled()); - let dep_node = if let QueryMode::Ensure { check_cache } = mode { - let (must_run, dep_node) = ensure_must_run(query, qcx, &key, check_cache); - if !must_run { - return None; + // Check if query execution can be skipped, for `ensure_ok` or `ensure_done`. + // This might have the side-effect of creating a suitable DepNode, which + // we should reuse for execution instead of creating a new one. + let dep_node: Option = match mode { + QueryMode::Ensure { ensure_mode } => { + let EnsureCanSkip { skip_execution, dep_node } = + check_if_ensure_can_skip_execution(query, tcx, key, ensure_mode); + if skip_execution { + // Return early to skip execution. + return None; + } + dep_node } - dep_node - } else { - None + QueryMode::Get => None, }; - let (result, dep_node_index) = ensure_sufficient_stack(|| { - try_execute_query::(query, qcx, span, key, dep_node) - }); + let (result, dep_node_index) = + ensure_sufficient_stack(|| try_execute_query::(query, tcx, span, key, dep_node)); if let Some(dep_node_index) = dep_node_index { - qcx.tcx.dep_graph.read_index(dep_node_index) + tcx.dep_graph.read_index(dep_node_index) } Some(result) } -pub(crate) fn force_query<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, +pub(crate) fn force_query<'tcx, C: QueryCache>( + query: &'tcx QueryVTable<'tcx, C>, + tcx: TyCtxt<'tcx>, key: C::Key, dep_node: DepNode, ) { // We may be concurrently trying both execute and force a query. // Ensure that only one of them runs the query. - if let Some((_, index)) = query.query_cache(qcx).lookup(&key) { - qcx.tcx.prof.query_cache_hit(index.into()); + if let Some((_, index)) = query.cache.lookup(&key) { + tcx.prof.query_cache_hit(index.into()); return; } - debug_assert!(!query.anon()); + debug_assert!(!query.anon); ensure_sufficient_stack(|| { - try_execute_query::(query, qcx, DUMMY_SP, key, Some(dep_node)) + try_execute_query::(query, tcx, DUMMY_SP, key, Some(dep_node)) }); } diff --git a/compiler/rustc_query_impl/src/values.rs b/compiler/rustc_query_impl/src/from_cycle_error.rs similarity index 84% rename from compiler/rustc_query_impl/src/values.rs rename to compiler/rustc_query_impl/src/from_cycle_error.rs index 67bc6893a320..eb6942ba491f 100644 --- a/compiler/rustc_query_impl/src/values.rs +++ b/compiler/rustc_query_impl/src/from_cycle_error.rs @@ -7,7 +7,7 @@ use rustc_errors::{Applicability, MultiSpan, pluralize, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_middle::dep_graph::dep_kinds; +use rustc_middle::dep_graph::DepKind; use rustc_middle::query::CycleError; use rustc_middle::query::plumbing::CyclePlaceholder; use rustc_middle::ty::{self, Representability, Ty, TyCtxt}; @@ -17,62 +17,51 @@ use crate::job::report_cycle; -pub(crate) trait Value<'tcx>: Sized { - fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) - -> Self; +pub(crate) trait FromCycleError<'tcx>: Sized { + /// Try to produce a `Self` value that represents an error form (e.g. `TyKind::Error`). + /// + /// Note: the default impl calls `raise_fatal`, ending compilation immediately! Only a few + /// types override this with a non-fatal impl. + fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self; } -impl<'tcx, T> Value<'tcx> for T { +impl<'tcx, T> FromCycleError<'tcx> for T { default fn from_cycle_error( tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, + cycle_error: CycleError, _guar: ErrorGuaranteed, ) -> T { - tcx.sess.dcx().abort_if_errors(); - bug!( - "<{} as Value>::from_cycle_error called without errors: {:#?}", - std::any::type_name::(), - cycle_error.cycle, - ); + let Some(guar) = tcx.sess.dcx().has_errors() else { + bug!( + "<{} as FromCycleError>::from_cycle_error called without errors: {:#?}", + std::any::type_name::(), + cycle_error.cycle, + ); + }; + guar.raise_fatal(); } } -impl<'tcx> Value<'tcx> for Ty<'_> { - fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self { +impl<'tcx> FromCycleError<'tcx> for Ty<'_> { + fn from_cycle_error(tcx: TyCtxt<'tcx>, _: CycleError, guar: ErrorGuaranteed) -> Self { // SAFETY: This is never called when `Self` is not `Ty<'tcx>`. // FIXME: Represent the above fact in the trait system somehow. unsafe { std::mem::transmute::, Ty<'_>>(Ty::new_error(tcx, guar)) } } } -impl<'tcx> Value<'tcx> for Result>, CyclePlaceholder> { - fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self { +impl<'tcx> FromCycleError<'tcx> for Result>, CyclePlaceholder> { + fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: CycleError, guar: ErrorGuaranteed) -> Self { Err(CyclePlaceholder(guar)) } } -impl<'tcx> Value<'tcx> for ty::SymbolName<'_> { - fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, _guar: ErrorGuaranteed) -> Self { - // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`. - // FIXME: Represent the above fact in the trait system somehow. - unsafe { - std::mem::transmute::, ty::SymbolName<'_>>(ty::SymbolName::new( - tcx, "", - )) - } - } -} - -impl<'tcx> Value<'tcx> for ty::Binder<'_, ty::FnSig<'_>> { - fn from_cycle_error( - tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, - guar: ErrorGuaranteed, - ) -> Self { +impl<'tcx> FromCycleError<'tcx> for ty::Binder<'_, ty::FnSig<'_>> { + fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self { let err = Ty::new_error(tcx, guar); let arity = if let Some(info) = cycle_error.cycle.get(0) - && info.frame.dep_kind == dep_kinds::fn_sig + && info.frame.dep_kind == DepKind::fn_sig && let Some(def_id) = info.frame.def_id && let Some(node) = tcx.hir_get_if_local(def_id) && let Some(sig) = node.fn_sig() @@ -97,16 +86,16 @@ fn from_cycle_error( } } -impl<'tcx> Value<'tcx> for Representability { +impl<'tcx> FromCycleError<'tcx> for Representability { fn from_cycle_error( tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, + cycle_error: CycleError, _guar: ErrorGuaranteed, ) -> Self { let mut item_and_field_ids = Vec::new(); let mut representable_ids = FxHashSet::default(); for info in &cycle_error.cycle { - if info.frame.dep_kind == dep_kinds::representability + if info.frame.dep_kind == DepKind::check_representability && let Some(field_id) = info.frame.def_id && let Some(field_id) = field_id.as_local() && let Some(DefKind::Field) = info.frame.info.def_kind @@ -120,7 +109,7 @@ fn from_cycle_error( } } for info in &cycle_error.cycle { - if info.frame.dep_kind == dep_kinds::representability_adt_ty + if info.frame.dep_kind == DepKind::check_representability_adt_ty && let Some(def_id) = info.frame.def_id_for_ty_in_cycle && let Some(def_id) = def_id.as_local() && !item_and_field_ids.iter().any(|&(id, _)| id == def_id) @@ -128,42 +117,36 @@ fn from_cycle_error( representable_ids.insert(def_id); } } + // We used to continue here, but the cycle error printed next is actually less useful than + // the error produced by `recursive_type_error`. let guar = recursive_type_error(tcx, item_and_field_ids, &representable_ids); - Representability::Infinite(guar) + guar.raise_fatal(); } } -impl<'tcx> Value<'tcx> for ty::EarlyBinder<'_, Ty<'_>> { - fn from_cycle_error( - tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, - guar: ErrorGuaranteed, - ) -> Self { +impl<'tcx> FromCycleError<'tcx> for ty::EarlyBinder<'_, Ty<'_>> { + fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self { ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle_error, guar)) } } -impl<'tcx> Value<'tcx> for ty::EarlyBinder<'_, ty::Binder<'_, ty::FnSig<'_>>> { - fn from_cycle_error( - tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, - guar: ErrorGuaranteed, - ) -> Self { +impl<'tcx> FromCycleError<'tcx> for ty::EarlyBinder<'_, ty::Binder<'_, ty::FnSig<'_>>> { + fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self { ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle_error, guar)) } } -impl<'tcx> Value<'tcx> for &[ty::Variance] { +impl<'tcx> FromCycleError<'tcx> for &[ty::Variance] { fn from_cycle_error( tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, + cycle_error: CycleError, _guar: ErrorGuaranteed, ) -> Self { search_for_cycle_permutation( &cycle_error.cycle, |cycle| { if let Some(info) = cycle.get(0) - && info.frame.dep_kind == dep_kinds::variances_of + && info.frame.dep_kind == DepKind::variances_of && let Some(def_id) = info.frame.def_id { let n = tcx.generics_of(def_id).own_params.len(); @@ -201,16 +184,16 @@ fn search_for_cycle_permutation( otherwise() } -impl<'tcx, T> Value<'tcx> for Result> { +impl<'tcx, T> FromCycleError<'tcx> for Result> { fn from_cycle_error( tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, + cycle_error: CycleError, _guar: ErrorGuaranteed, ) -> Self { let diag = search_for_cycle_permutation( &cycle_error.cycle, |cycle| { - if cycle[0].frame.dep_kind == dep_kinds::layout_of + if cycle[0].frame.dep_kind == DepKind::layout_of && let Some(def_id) = cycle[0].frame.def_id_for_ty_in_cycle && let Some(def_id) = def_id.as_local() && let def_kind = tcx.def_kind(def_id) @@ -235,7 +218,7 @@ fn from_cycle_error( tcx.def_kind_descr(def_kind, def_id.to_def_id()), ); for (i, info) in cycle.iter().enumerate() { - if info.frame.dep_kind != dep_kinds::layout_of { + if info.frame.dep_kind != DepKind::layout_of { continue; } let Some(frame_def_id) = info.frame.def_id_for_ty_in_cycle else { @@ -280,7 +263,7 @@ fn from_cycle_error( ControlFlow::Continue(()) } }, - || report_cycle(tcx.sess, cycle_error), + || report_cycle(tcx.sess, &cycle_error), ); let guar = diag.emit(); diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index 6db4998ffafb..2a7f7bf5efd4 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -10,11 +10,11 @@ CycleError, QueryInfo, QueryJob, QueryJobId, QueryLatch, QueryStackDeferred, QueryStackFrame, QueryWaiter, }; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::{DUMMY_SP, Span}; -use crate::QueryCtxt; -use crate::dep_graph::DepContext; +use crate::collect_active_jobs_from_all_queries; /// Map from query job IDs to job information collected by /// `collect_active_jobs_from_all_queries`. @@ -26,7 +26,7 @@ pub struct QueryJobMap<'tcx> { impl<'tcx> QueryJobMap<'tcx> { /// Adds information about a job ID to the job map. /// - /// Should only be called by `gather_active_jobs_inner`. + /// Should only be called by `gather_active_jobs`. pub(crate) fn insert(&mut self, id: QueryJobId, info: QueryJobInfo<'tcx>) { self.map.insert(id, info); } @@ -209,27 +209,6 @@ fn connected_to_root<'tcx>( visit_waiters(job_map, query, |_, successor| connected_to_root(job_map, successor, visited)) } -// Deterministically pick an query from a list -fn pick_query<'a, 'tcx, T, F>(job_map: &QueryJobMap<'tcx>, queries: &'a [T], f: F) -> &'a T -where - F: Fn(&T) -> (Span, QueryJobId), -{ - // Deterministically pick an entry point - // FIXME: Sort this instead - queries - .iter() - .min_by_key(|v| { - let (span, query) = f(v); - let hash = job_map.frame_of(query).hash; - // Prefer entry points which have valid spans for nicer error messages - // We add an integer to the tuple ensuring that entry points - // with valid spans are picked first - let span_cmp = if span == DUMMY_SP { 1 } else { 0 }; - (span_cmp, hash) - }) - .unwrap() -} - /// Looks for query cycles starting from the last query in `jobs`. /// If a cycle is found, all queries in the cycle is removed from `jobs` and /// the function return true. @@ -263,48 +242,56 @@ fn remove_cycle<'tcx>( } } + struct EntryPoint { + query_in_cycle: QueryJobId, + waiter: Option<(Span, QueryJobId)>, + } + // Find the queries in the cycle which are // connected to queries outside the cycle let entry_points = stack .iter() - .filter_map(|&(span, query)| { - if job_map.parent_of(query).is_none() { + .filter_map(|&(_, query_in_cycle)| { + if job_map.parent_of(query_in_cycle).is_none() { // This query is connected to the root (it has no query parent) - Some((span, query, None)) + Some(EntryPoint { query_in_cycle, waiter: None }) } else { - let mut waiters = Vec::new(); - // Find all the direct waiters who lead to the root - let _ = visit_waiters(job_map, query, |span, waiter| { + let mut waiter_on_cycle = None; + // Find a direct waiter who leads to the root + let _ = visit_waiters(job_map, query_in_cycle, |span, waiter| { // Mark all the other queries in the cycle as already visited let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); if connected_to_root(job_map, waiter, &mut visited).is_break() { - waiters.push((span, waiter)); + waiter_on_cycle = Some((span, waiter)); + ControlFlow::Break(None) + } else { + ControlFlow::Continue(()) } - - ControlFlow::Continue(()) }); - if waiters.is_empty() { - None - } else { - // Deterministically pick one of the waiters to show to the user - let waiter = *pick_query(job_map, &waiters, |s| *s); - Some((span, query, Some(waiter))) - } + + waiter_on_cycle.map(|waiter_on_cycle| EntryPoint { + query_in_cycle, + waiter: Some(waiter_on_cycle), + }) } }) - .collect::)>>(); + .collect::>(); - // Deterministically pick an entry point - let (_, entry_point, usage) = pick_query(job_map, &entry_points, |e| (e.0, e.1)); + // Pick an entry point, preferring ones with waiters + let entry_point = entry_points + .iter() + .find(|entry_point| entry_point.waiter.is_some()) + .unwrap_or(&entry_points[0]); // Shift the stack so that our entry point is first - let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point); + let entry_point_pos = + stack.iter().position(|(_, query)| *query == entry_point.query_in_cycle); if let Some(pos) = entry_point_pos { stack.rotate_left(pos); } - let usage = usage.map(|(span, job)| (span, job_map.frame_of(job).clone())); + let usage = entry_point.waiter.map(|(span, job)| (span, job_map.frame_of(job).clone())); // Create the cycle error let error = CycleError { @@ -385,7 +372,7 @@ pub fn break_query_cycles<'tcx>( } pub fn print_query_stack<'tcx>( - qcx: QueryCtxt<'tcx>, + tcx: TyCtxt<'tcx>, mut current_query: Option, dcx: DiagCtxtHandle<'_>, limit_frames: Option, @@ -398,8 +385,7 @@ pub fn print_query_stack<'tcx>( let mut count_total = 0; // Make use of a partial query job map if we fail to take locks collecting active queries. - let job_map: QueryJobMap<'_> = qcx - .collect_active_jobs_from_all_queries(false) + let job_map: QueryJobMap<'_> = collect_active_jobs_from_all_queries(tcx, false) .unwrap_or_else(|partial_job_map| partial_job_map); if let Some(ref mut file) = file { @@ -424,10 +410,8 @@ pub fn print_query_stack<'tcx>( if let Some(ref mut file) = file { let _ = writeln!( file, - "#{} [{}] {}", - count_total, - qcx.tcx.dep_kind_vtable(query_info.frame.dep_kind).name, - query_extra.description + "#{} [{:?}] {}", + count_total, query_info.frame.dep_kind, query_extra.description ); } @@ -454,7 +438,12 @@ pub(crate) fn report_cycle<'a>( let mut cycle_stack = Vec::new(); use crate::error::StackCount; - let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple }; + let stack_bottom = stack[0].frame.info.description.to_owned(); + let stack_count = if stack.len() == 1 { + StackCount::Single { stack_bottom: stack_bottom.clone() } + } else { + StackCount::Multiple { stack_bottom: stack_bottom.clone() } + }; for i in 1..stack.len() { let frame = &stack[i].frame; @@ -483,7 +472,7 @@ pub(crate) fn report_cycle<'a>( let cycle_diag = crate::error::Cycle { span, cycle_stack, - stack_bottom: stack[0].frame.info.description.to_owned(), + stack_bottom, alias, cycle_usage, stack_count, diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 6a4171afe4bc..d92d80dbfb86 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -2,35 +2,25 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![feature(adt_const_params)] #![feature(core_intrinsics)] #![feature(min_specialization)] #![feature(rustc_attrs)] #![feature(try_blocks)] // tidy-alphabetical-end -use std::marker::ConstParamTy; - use rustc_data_structures::sync::AtomicU64; -use rustc_middle::dep_graph::{self, DepKind, DepNode, DepNodeIndex, SerializedDepNodeIndex}; -use rustc_middle::queries::{ - self, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, -}; +use rustc_middle::dep_graph; +use rustc_middle::queries::{self, ExternProviders, Providers}; use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; -use rustc_middle::query::plumbing::{ - HashResult, QueryState, QuerySystem, QuerySystemFns, QueryVTable, -}; -use rustc_middle::query::{AsLocalKey, CycleError, CycleErrorHandling, QueryCache, QueryMode}; +use rustc_middle::query::plumbing::{QuerySystem, QueryVTable}; +use rustc_middle::query::{AsLocalQueryKey, QueryCache, QueryMode}; use rustc_middle::ty::TyCtxt; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::Span; pub use crate::dep_kind_vtables::make_dep_kind_vtables; +use crate::from_cycle_error::FromCycleError; pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack}; -pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all}; -use crate::plumbing::{encode_all_query_results, try_mark_green}; use crate::profiling_support::QueryKeyStringCache; -pub use crate::profiling_support::alloc_self_profile_query_strings; -use crate::values::Value; #[macro_use] mod plumbing; @@ -38,189 +28,22 @@ mod dep_kind_vtables; mod error; mod execution; +mod from_cycle_error; mod job; mod profiling_support; -mod values; -#[derive(ConstParamTy)] // Allow this struct to be used for const-generic values. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct QueryFlags { - /// True if this query has the `anon` modifier. - is_anon: bool, - /// True if this query has the `depth_limit` modifier. - is_depth_limit: bool, - /// True if this query has the `feedable` modifier. - is_feedable: bool, -} - -/// Combines a [`QueryVTable`] with some additional compile-time booleans. -/// "Dispatcher" should be understood as a near-synonym of "vtable". -/// -/// Baking these boolean flags into the type gives a modest but measurable -/// improvement to compiler perf and compiler code size; see -/// . -struct SemiDynamicQueryDispatcher<'tcx, C: QueryCache, const FLAGS: QueryFlags> { - vtable: &'tcx QueryVTable<'tcx, C>, -} - -// Manually implement Copy/Clone, because deriving would put trait bounds on the cache type. -impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Copy - for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> -{ -} -impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Clone - for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> -{ - fn clone(&self) -> Self { - *self - } -} - -impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> SemiDynamicQueryDispatcher<'tcx, C, FLAGS> { - #[inline(always)] - fn name(self) -> &'static str { - self.vtable.name - } - - #[inline(always)] - fn will_cache_on_disk_for_key(self, tcx: TyCtxt<'tcx>, key: &C::Key) -> bool { - self.vtable.will_cache_on_disk_for_key_fn.map_or(false, |f| f(tcx, key)) - } - - // Don't use this method to access query results, instead use the methods on TyCtxt. - #[inline(always)] - fn query_state(self, qcx: QueryCtxt<'tcx>) -> &'tcx QueryState<'tcx, C::Key> { - // Safety: - // This is just manually doing the subfield referencing through pointer math. - unsafe { - &*(&qcx.tcx.query_system.states as *const QueryStates<'tcx>) - .byte_add(self.vtable.query_state) - .cast::>() - } - } - - // Don't use this method to access query results, instead use the methods on TyCtxt. - #[inline(always)] - fn query_cache(self, qcx: QueryCtxt<'tcx>) -> &'tcx C { - // Safety: - // This is just manually doing the subfield referencing through pointer math. - unsafe { - &*(&qcx.tcx.query_system.caches as *const QueryCaches<'tcx>) - .byte_add(self.vtable.query_cache) - .cast::() - } - } - - /// Calls `tcx.$query(key)` for this query, and discards the returned value. - /// See [`QueryVTable::call_query_method_fn`] for details of this strange operation. - #[inline(always)] - fn call_query_method(self, tcx: TyCtxt<'tcx>, key: C::Key) { - (self.vtable.call_query_method_fn)(tcx, key) - } - - /// Calls the actual provider function for this query. - /// See [`QueryVTable::invoke_provider_fn`] for more details. - #[inline(always)] - fn invoke_provider(self, qcx: QueryCtxt<'tcx>, key: C::Key) -> C::Value { - (self.vtable.invoke_provider_fn)(qcx.tcx, key) - } - - #[inline(always)] - fn try_load_from_disk( - self, - qcx: QueryCtxt<'tcx>, - key: &C::Key, - prev_index: SerializedDepNodeIndex, - index: DepNodeIndex, - ) -> Option { - // `?` will return None immediately for queries that never cache to disk. - self.vtable.try_load_from_disk_fn?(qcx.tcx, key, prev_index, index) - } - - #[inline] - fn is_loadable_from_disk( - self, - qcx: QueryCtxt<'tcx>, - key: &C::Key, - index: SerializedDepNodeIndex, - ) -> bool { - self.vtable.is_loadable_from_disk_fn.map_or(false, |f| f(qcx.tcx, key, index)) - } - - /// Synthesize an error value to let compilation continue after a cycle. - fn value_from_cycle_error( - self, - tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, - guar: ErrorGuaranteed, - ) -> C::Value { - (self.vtable.value_from_cycle_error)(tcx, cycle_error, guar) - } - - #[inline(always)] - fn format_value(self) -> fn(&C::Value) -> String { - self.vtable.format_value - } - - #[inline(always)] - fn anon(self) -> bool { - FLAGS.is_anon - } - - #[inline(always)] - fn eval_always(self) -> bool { - self.vtable.eval_always - } - - #[inline(always)] - fn depth_limit(self) -> bool { - FLAGS.is_depth_limit - } - - #[inline(always)] - fn feedable(self) -> bool { - FLAGS.is_feedable - } - - #[inline(always)] - fn dep_kind(self) -> DepKind { - self.vtable.dep_kind - } - - #[inline(always)] - fn cycle_error_handling(self) -> CycleErrorHandling { - self.vtable.cycle_error_handling - } - - #[inline(always)] - fn hash_result(self) -> HashResult { - self.vtable.hash_result - } - - fn construct_dep_node(self, tcx: TyCtxt<'tcx>, key: &C::Key) -> DepNode { - DepNode::construct(tcx, self.dep_kind(), key) - } -} - -/// Provides access to vtable-like operations for a query -/// (by creating a [`SemiDynamicQueryDispatcher`]), -/// but also keeps track of the "unerased" value type of the query -/// (i.e. the actual result type in the query declaration). +/// Trait that knows how to look up the [`QueryVTable`] for a particular query. /// /// This trait allows some per-query code to be defined in generic functions /// with a trait bound, instead of having to be defined inline within a macro /// expansion. /// /// There is one macro-generated implementation of this trait for each query, -/// on the type `rustc_query_impl::query_impl::$name::QueryType`. -trait QueryDispatcherUnerased<'tcx, C: QueryCache, const FLAGS: QueryFlags> { - type UnerasedValue; +/// on the type `rustc_query_impl::query_impl::$name::VTableGetter`. +trait GetQueryVTable<'tcx> { + type Cache: QueryCache + 'tcx; - const NAME: &'static &'static str; - - fn query_dispatcher(tcx: TyCtxt<'tcx>) -> SemiDynamicQueryDispatcher<'tcx, C, FLAGS>; - - fn restore_val(value: C::Value) -> Self::UnerasedValue; + fn query_vtable(tcx: TyCtxt<'tcx>) -> &'tcx QueryVTable<'tcx, Self::Cache>; } pub fn query_system<'tcx>( @@ -230,18 +53,11 @@ pub fn query_system<'tcx>( incremental: bool, ) -> QuerySystem<'tcx> { QuerySystem { - states: Default::default(), arenas: Default::default(), - caches: Default::default(), - query_vtables: make_query_vtables(), + query_vtables: make_query_vtables(incremental), on_disk_cache, - fns: QuerySystemFns { - engine: engine(incremental), - local_providers, - extern_providers, - encode_query_results: encode_all_query_results, - try_mark_green, - }, + local_providers, + extern_providers, jobs: AtomicU64::new(1), } } @@ -251,4 +67,5 @@ pub fn query_system<'tcx>( pub fn provide(providers: &mut rustc_middle::util::Providers) { providers.hooks.alloc_self_profile_query_strings = alloc_self_profile_query_strings; providers.hooks.query_key_hash_verify_all = query_key_hash_verify_all; + providers.hooks.encode_all_query_results = encode_all_query_results; } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index ad782b3150b0..f4649b2403a6 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -4,332 +4,90 @@ use std::num::NonZero; -use rustc_data_structures::jobserver::Proxy; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_data_structures::unord::UnordMap; -use rustc_hashes::Hash64; use rustc_hir::def_id::DefId; use rustc_hir::limit::Limit; use rustc_index::Idx; use rustc_middle::bug; #[expect(unused_imports, reason = "used by doc comments")] use rustc_middle::dep_graph::DepKindVTable; -use rustc_middle::dep_graph::{ - self, DepContext, DepNode, DepNodeIndex, DepNodeKey, DepsType, HasDepContext, - SerializedDepNodeIndex, dep_kinds, -}; +use rustc_middle::dep_graph::{DepKind, DepNode, DepNodeIndex, DepNodeKey, SerializedDepNodeIndex}; +use rustc_middle::query::erase::{Erasable, Erased}; use rustc_middle::query::on_disk_cache::{ AbsoluteBytePos, CacheDecoder, CacheEncoder, EncodedDepNodeIndex, }; use rustc_middle::query::plumbing::QueryVTable; use rustc_middle::query::{ - Key, QueryCache, QueryContext, QueryJobId, QueryStackDeferred, QueryStackFrame, - QueryStackFrameExtra, + QueryCache, QueryJobId, QueryKey, QueryMode, QueryStackDeferred, QueryStackFrame, + QueryStackFrameExtra, erase, }; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::print::with_reduced_queries; use rustc_middle::ty::tls::{self, ImplicitCtxt}; use rustc_middle::ty::{self, TyCtxt}; -use rustc_query_system::query::QuerySideEffect; use rustc_serialize::{Decodable, Encodable}; +use rustc_span::DUMMY_SP; use rustc_span::def_id::LOCAL_CRATE; use crate::error::{QueryOverflow, QueryOverflowNote}; use crate::execution::{all_inactive, force_query}; -use crate::job::{QueryJobMap, find_dep_kind_root}; -use crate::{QueryDispatcherUnerased, QueryFlags, SemiDynamicQueryDispatcher}; +use crate::job::find_dep_kind_root; +use crate::{GetQueryVTable, collect_active_jobs_from_all_queries}; -/// Implements [`QueryContext`] for use by [`rustc_query_system`], since that -/// crate does not have direct access to [`TyCtxt`]. -#[derive(Copy, Clone)] -pub struct QueryCtxt<'tcx> { - pub tcx: TyCtxt<'tcx>, +fn depth_limit_error<'tcx>(tcx: TyCtxt<'tcx>, job: QueryJobId) { + let job_map = + collect_active_jobs_from_all_queries(tcx, true).expect("failed to collect active queries"); + let (info, depth) = find_dep_kind_root(job, job_map); + + let suggested_limit = match tcx.recursion_limit() { + Limit(0) => Limit(2), + limit => limit * 2, + }; + + tcx.sess.dcx().emit_fatal(QueryOverflow { + span: info.job.span, + note: QueryOverflowNote { desc: info.frame.info.extract().description, depth }, + suggested_limit, + crate_name: tcx.crate_name(LOCAL_CRATE), + }); } -impl<'tcx> QueryCtxt<'tcx> { - #[inline] - pub fn new(tcx: TyCtxt<'tcx>) -> Self { - QueryCtxt { tcx } - } +#[inline] +pub(crate) fn next_job_id<'tcx>(tcx: TyCtxt<'tcx>) -> QueryJobId { + QueryJobId( + NonZero::new(tcx.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed)) + .unwrap(), + ) +} - fn depth_limit_error(self, job: QueryJobId) { - let job_map = self - .collect_active_jobs_from_all_queries(true) - .expect("failed to collect active queries"); - let (info, depth) = find_dep_kind_root(job, job_map); +#[inline] +pub(crate) fn current_query_job() -> Option { + tls::with_context(|icx| icx.query) +} - let suggested_limit = match self.tcx.recursion_limit() { - Limit(0) => Limit(2), - limit => limit * 2, +/// Executes a job by changing the `ImplicitCtxt` to point to the new query job while it executes. +#[inline(always)] +pub(crate) fn start_query( + job_id: QueryJobId, + depth_limit: bool, + compute: impl FnOnce() -> R, +) -> R { + tls::with_context(move |icx| { + if depth_limit && !icx.tcx.recursion_limit().value_within_limit(icx.query_depth) { + depth_limit_error(icx.tcx, job_id); + } + + // Update the `ImplicitCtxt` to point to our new query job. + let icx = ImplicitCtxt { + query: Some(job_id), + query_depth: icx.query_depth + if depth_limit { 1 } else { 0 }, + ..*icx }; - self.tcx.sess.dcx().emit_fatal(QueryOverflow { - span: info.job.span, - note: QueryOverflowNote { desc: info.frame.info.extract().description, depth }, - suggested_limit, - crate_name: self.tcx.crate_name(LOCAL_CRATE), - }); - } - - #[inline] - pub(crate) fn next_job_id(self) -> QueryJobId { - QueryJobId( - NonZero::new( - self.tcx.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed), - ) - .unwrap(), - ) - } - - #[inline] - pub(crate) fn current_query_job(self) -> Option { - tls::with_related_context(self.tcx, |icx| icx.query) - } - - /// Executes a job by changing the `ImplicitCtxt` to point to the - /// new query job while it executes. - #[inline(always)] - pub(crate) fn start_query( - self, - token: QueryJobId, - depth_limit: bool, - compute: impl FnOnce() -> R, - ) -> R { - // The `TyCtxt` stored in TLS has the same global interner lifetime - // as `self`, so we use `with_related_context` to relate the 'tcx lifetimes - // when accessing the `ImplicitCtxt`. - tls::with_related_context(self.tcx, move |current_icx| { - if depth_limit - && !self.tcx.recursion_limit().value_within_limit(current_icx.query_depth) - { - self.depth_limit_error(token); - } - - // Update the `ImplicitCtxt` to point to our new query job. - let new_icx = ImplicitCtxt { - tcx: self.tcx, - query: Some(token), - query_depth: current_icx.query_depth + depth_limit as usize, - task_deps: current_icx.task_deps, - }; - - // Use the `ImplicitCtxt` while we execute the query. - tls::enter_context(&new_icx, compute) - }) - } - - /// Returns a map of currently active query jobs, collected from all queries. - /// - /// If `require_complete` is `true`, this function locks all shards of the - /// query results to produce a complete map, which always returns `Ok`. - /// Otherwise, it may return an incomplete map as an error if any shard - /// lock cannot be acquired. - /// - /// Prefer passing `false` to `require_complete` to avoid potential deadlocks, - /// especially when called from within a deadlock handler, unless a - /// complete map is needed and no deadlock is possible at this call site. - pub fn collect_active_jobs_from_all_queries( - self, - require_complete: bool, - ) -> Result, QueryJobMap<'tcx>> { - let mut job_map_out = QueryJobMap::default(); - let mut complete = true; - - for gather_fn in crate::PER_QUERY_GATHER_ACTIVE_JOBS_FNS.iter() { - if gather_fn(self.tcx, require_complete, &mut job_map_out).is_none() { - complete = false; - } - } - - if complete { Ok(job_map_out) } else { Err(job_map_out) } - } -} - -impl<'tcx> HasDepContext for QueryCtxt<'tcx> { - type Deps = DepsType; - type DepContext = TyCtxt<'tcx>; - - #[inline] - fn dep_context(&self) -> &Self::DepContext { - &self.tcx - } -} - -impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { - #[inline] - fn jobserver_proxy(&self) -> &Proxy { - &self.tcx.jobserver_proxy - } - - // Interactions with on_disk_cache - fn load_side_effect( - self, - prev_dep_node_index: SerializedDepNodeIndex, - ) -> Option { - self.tcx - .query_system - .on_disk_cache - .as_ref() - .and_then(|c| c.load_side_effect(self.tcx, prev_dep_node_index)) - } - - #[inline(never)] - #[cold] - fn store_side_effect(self, dep_node_index: DepNodeIndex, side_effect: QuerySideEffect) { - if let Some(c) = self.tcx.query_system.on_disk_cache.as_ref() { - c.store_side_effect(dep_node_index, side_effect) - } - } -} - -pub(super) fn try_mark_green<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool { - tcx.dep_graph.try_mark_green(QueryCtxt::new(tcx), dep_node).is_some() -} - -pub(super) fn encode_all_query_results<'tcx>( - tcx: TyCtxt<'tcx>, - encoder: &mut CacheEncoder<'_, 'tcx>, - query_result_index: &mut EncodedDepNodeIndex, -) { - for encode in super::ENCODE_QUERY_RESULTS.iter().copied().flatten() { - encode(tcx, encoder, query_result_index); - } -} - -pub fn query_key_hash_verify_all<'tcx>(tcx: TyCtxt<'tcx>) { - if tcx.sess().opts.unstable_opts.incremental_verify_ich || cfg!(debug_assertions) { - tcx.sess.time("query_key_hash_verify_all", || { - for verify in super::QUERY_KEY_HASH_VERIFY.iter() { - verify(tcx); - } - }) - } -} - -macro_rules! cycle_error_handling { - ([]) => {{ - rustc_middle::query::CycleErrorHandling::Error - }}; - ([(cycle_fatal) $($rest:tt)*]) => {{ - rustc_middle::query::CycleErrorHandling::Fatal - }}; - ([(cycle_stash) $($rest:tt)*]) => {{ - rustc_middle::query::CycleErrorHandling::Stash - }}; - ([(cycle_delay_bug) $($rest:tt)*]) => {{ - rustc_middle::query::CycleErrorHandling::DelayBug - }}; - ([$other:tt $($modifiers:tt)*]) => { - cycle_error_handling!([$($modifiers)*]) - }; -} - -macro_rules! is_anon { - ([]) => {{ - false - }}; - ([(anon) $($rest:tt)*]) => {{ - true - }}; - ([$other:tt $($modifiers:tt)*]) => { - is_anon!([$($modifiers)*]) - }; -} - -macro_rules! is_eval_always { - ([]) => {{ - false - }}; - ([(eval_always) $($rest:tt)*]) => {{ - true - }}; - ([$other:tt $($modifiers:tt)*]) => { - is_eval_always!([$($modifiers)*]) - }; -} - -macro_rules! depth_limit { - ([]) => {{ - false - }}; - ([(depth_limit) $($rest:tt)*]) => {{ - true - }}; - ([$other:tt $($modifiers:tt)*]) => { - depth_limit!([$($modifiers)*]) - }; -} - -macro_rules! feedable { - ([]) => {{ - false - }}; - ([(feedable) $($rest:tt)*]) => {{ - true - }}; - ([$other:tt $($modifiers:tt)*]) => { - feedable!([$($modifiers)*]) - }; -} - -macro_rules! hash_result { - ([][$V:ty]) => {{ - Some(|hcx, result| { - let result = rustc_middle::query::erase::restore_val::<$V>(*result); - rustc_middle::dep_graph::hash_result(hcx, &result) - }) - }}; - ([(no_hash) $($rest:tt)*][$V:ty]) => {{ - None - }}; - ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { - hash_result!([$($modifiers)*][$($args)*]) - }; -} - -macro_rules! call_provider { - ([][$tcx:expr, $name:ident, $key:expr]) => {{ - ($tcx.query_system.fns.local_providers.$name)($tcx, $key) - }}; - ([(separate_provide_extern) $($rest:tt)*][$tcx:expr, $name:ident, $key:expr]) => {{ - if let Some(key) = $key.as_local_key() { - ($tcx.query_system.fns.local_providers.$name)($tcx, key) - } else { - ($tcx.query_system.fns.extern_providers.$name)($tcx, $key) - } - }}; - ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { - call_provider!([$($modifiers)*][$($args)*]) - }; -} - -/// Expands to one of two token trees, depending on whether the current query -/// has the `cache_on_disk_if` modifier. -macro_rules! if_cache_on_disk { - ([] $yes:tt $no:tt) => { - $no - }; - // The `cache_on_disk_if` modifier generates a synthetic `(cache_on_disk)`, - // modifier, for use by this macro and similar macros. - ([(cache_on_disk) $($rest:tt)*] $yes:tt $no:tt) => { - $yes - }; - ([$other:tt $($modifiers:tt)*] $yes:tt $no:tt) => { - if_cache_on_disk!([$($modifiers)*] $yes $no) - }; -} - -/// Conditionally expands to some token trees, if the current query has the -/// `cache_on_disk_if` modifier. -macro_rules! item_if_cache_on_disk { - ([] $($item:tt)*) => {}; - ([(cache_on_disk) $($rest:tt)*] $($item:tt)*) => { - $($item)* - }; - ([$other:tt $($modifiers:tt)*] $($item:tt)*) => { - item_if_cache_on_disk! { [$($modifiers)*] $($item)* } - }; + // Use the `ImplicitCtxt` while we execute the query. + tls::enter_context(&icx, compute) + }) } /// The deferred part of a deferred query stack frame. @@ -338,7 +96,7 @@ fn mk_query_stack_frame_extra<'tcx, Cache>( ) -> QueryStackFrameExtra where Cache: QueryCache, - Cache::Key: Key, + Cache::Key: QueryKey, { let def_id = key.key_as_def_id(); @@ -353,7 +111,7 @@ fn mk_query_stack_frame_extra<'tcx, Cache>( } else { description }; - let span = if vtable.dep_kind == dep_graph::dep_kinds::def_span || reduce_queries { + let span = if vtable.dep_kind == DepKind::def_span || reduce_queries { // The `def_span` query is used to calculate `default_span`, // so exit to avoid infinite recursion. None @@ -361,7 +119,7 @@ fn mk_query_stack_frame_extra<'tcx, Cache>( Some(key.default_span(tcx)) }; - let def_kind = if vtable.dep_kind == dep_graph::dep_kinds::def_kind || reduce_queries { + let def_kind = if vtable.dep_kind == DepKind::def_kind || reduce_queries { // Try to avoid infinite recursion. None } else { @@ -370,46 +128,38 @@ fn mk_query_stack_frame_extra<'tcx, Cache>( QueryStackFrameExtra::new(description, span, def_kind) } -pub(crate) fn create_deferred_query_stack_frame<'tcx, Cache>( +pub(crate) fn create_deferred_query_stack_frame<'tcx, C>( tcx: TyCtxt<'tcx>, - vtable: &'tcx QueryVTable<'tcx, Cache>, - key: Cache::Key, + vtable: &'tcx QueryVTable<'tcx, C>, + key: C::Key, ) -> QueryStackFrame> where - Cache: QueryCache, - Cache::Key: Key + DynSend + DynSync, + C: QueryCache, + QueryVTable<'tcx, C>: DynSync, { let kind = vtable.dep_kind; - let hash = tcx.with_stable_hashing_context(|mut hcx| { - let mut hasher = StableHasher::new(); - kind.as_usize().hash_stable(&mut hcx, &mut hasher); - key.hash_stable(&mut hcx, &mut hasher); - hasher.finish::() - }); - let def_id: Option = key.key_as_def_id(); let def_id_for_ty_in_cycle: Option = key.def_id_for_ty_in_cycle(); let info = QueryStackDeferred::new((tcx, vtable, key), mk_query_stack_frame_extra); - QueryStackFrame::new(info, kind, hash, def_id, def_id_for_ty_in_cycle) + QueryStackFrame::new(info, kind, def_id, def_id_for_ty_in_cycle) } -pub(crate) fn encode_query_results<'a, 'tcx, Q, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, +pub(crate) fn encode_query_results<'a, 'tcx, C, V>( + tcx: TyCtxt<'tcx>, + query: &'tcx QueryVTable<'tcx, C>, encoder: &mut CacheEncoder<'a, 'tcx>, query_result_index: &mut EncodedDepNodeIndex, ) where - Q: QueryDispatcherUnerased<'tcx, C, FLAGS>, - Q::UnerasedValue: Encodable>, + C: QueryCache>, + V: Erasable + Encodable>, { - let _timer = qcx.tcx.prof.generic_activity_with_arg("encode_query_results_for", query.name()); + let _timer = tcx.prof.generic_activity_with_arg("encode_query_results_for", query.name); - assert!(all_inactive(query.query_state(qcx))); - let cache = query.query_cache(qcx); - cache.iter(&mut |key, value, dep_node| { - if query.will_cache_on_disk_for_key(qcx.tcx, key) { + assert!(all_inactive(&query.state)); + query.cache.for_each(&mut |key, value, dep_node| { + if (query.will_cache_on_disk_for_key_fn)(tcx, *key) { let dep_node = SerializedDepNodeIndex::new(dep_node.index()); // Record position of the cache entry. @@ -417,21 +167,21 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q, C: QueryCache, const FLAGS: Quer // Encode the type check tables with the `SerializedDepNodeIndex` // as tag. - encoder.encode_tagged(dep_node, &Q::restore_val(*value)); + encoder.encode_tagged(dep_node, &erase::restore_val::(*value)); } }); } -pub(crate) fn query_key_hash_verify<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, - qcx: QueryCtxt<'tcx>, +pub(crate) fn query_key_hash_verify<'tcx, C: QueryCache>( + query: &'tcx QueryVTable<'tcx, C>, + tcx: TyCtxt<'tcx>, ) { - let _timer = qcx.tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name()); + let _timer = tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name); - let cache = query.query_cache(qcx); + let cache = &query.cache; let mut map = UnordMap::with_capacity(cache.len()); - cache.iter(&mut |key, _, _| { - let node = DepNode::construct(qcx.tcx, query.dep_kind(), key); + cache.for_each(&mut |key, _, _| { + let node = DepNode::construct(tcx, query.dep_kind, key); if let Some(other_key) = map.insert(node, *key) { bug!( "query key:\n\ @@ -448,21 +198,41 @@ pub(crate) fn query_key_hash_verify<'tcx, C: QueryCache, const FLAGS: QueryFlags }); } -/// Implementation of [`DepKindVTable::try_load_from_on_disk_cache`] for queries. -pub(crate) fn try_load_from_on_disk_cache_inner<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, +/// Implementation of [`DepKindVTable::promote_from_disk_fn`] for queries. +pub(crate) fn promote_from_disk_inner<'tcx, Q: GetQueryVTable<'tcx>>( tcx: TyCtxt<'tcx>, dep_node: DepNode, ) { + let query = Q::query_vtable(tcx); debug_assert!(tcx.dep_graph.is_green(&dep_node)); - let key = C::Key::recover(tcx, &dep_node).unwrap_or_else(|| { - panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash) + let key = ::Key::try_recover_key(tcx, &dep_node).unwrap_or_else(|| { + panic!( + "Failed to recover key for {dep_node:?} with key fingerprint {}", + dep_node.key_fingerprint + ) }); - if query.will_cache_on_disk_for_key(tcx, &key) { - // Call `tcx.$query(key)` for its side-effect of loading the disk-cached - // value into memory. - query.call_query_method(tcx, key); + + // If the recovered key isn't eligible for cache-on-disk, then there's no + // value on disk to promote. + if !(query.will_cache_on_disk_for_key_fn)(tcx, key) { + return; + } + + match query.cache.lookup(&key) { + // If the value is already in memory, then promotion isn't needed. + Some(_) => {} + + // "Execute" the query to load its disk-cached value into memory. + // + // We know that the key is cache-on-disk and its node is green, + // so there _must_ be a value on disk to load. + // + // FIXME(Zalathar): Is there a reasonable way to skip more of the + // query bookkeeping when doing this? + None => { + (query.execute_query_fn)(tcx, DUMMY_SP, key, QueryMode::Get); + } } } @@ -498,12 +268,15 @@ pub(crate) fn try_load_from_disk<'tcx, V>( value } -/// Implementation of [`DepKindVTable::force_from_dep_node`] for queries. -pub(crate) fn force_from_dep_node_inner<'tcx, C: QueryCache, const FLAGS: QueryFlags>( - query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, +/// Implementation of [`DepKindVTable::force_from_dep_node_fn`] for queries. +pub(crate) fn force_from_dep_node_inner<'tcx, Q: GetQueryVTable<'tcx>>( tcx: TyCtxt<'tcx>, dep_node: DepNode, + // Needed by the vtable function signature, but not used when forcing queries. + _prev_index: SerializedDepNodeIndex, ) -> bool { + let query = Q::query_vtable(tcx); + // We must avoid ever having to call `force_from_dep_node()` for a // `DepNode::codegen_unit`: // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we @@ -518,34 +291,54 @@ pub(crate) fn force_from_dep_node_inner<'tcx, C: QueryCache, const FLAGS: QueryF // hit the cache instead of having to go through `force_from_dep_node`. // This assertion makes sure, we actually keep applying the solution above. debug_assert!( - dep_node.kind != dep_kinds::codegen_unit, + dep_node.kind != DepKind::codegen_unit, "calling force_from_dep_node() on dep_kinds::codegen_unit" ); - if let Some(key) = C::Key::recover(tcx, &dep_node) { - force_query(query, QueryCtxt::new(tcx), key, dep_node); + if let Some(key) = ::Key::try_recover_key(tcx, &dep_node) { + force_query(query, tcx, key, dep_node); true } else { false } } -// NOTE: `$V` isn't used here, but we still need to match on it so it can be passed to other macros -// invoked by `rustc_with_all_queries`. macro_rules! define_queries { ( - $( - $(#[$attr:meta])* - [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty, - )* + // Note: `$K` and `$V` are unused but present so this can be called by + // `rustc_with_all_queries`. + queries { + $( + $(#[$attr:meta])* + fn $name:ident($K:ty) -> $V:ty + { + // Search for (QMODLIST) to find all occurrences of this query modifier list. + anon: $anon:literal, + arena_cache: $arena_cache:literal, + cache_on_disk: $cache_on_disk:literal, + cycle_error_handling: $cycle_error_handling:ident, + depth_limit: $depth_limit:literal, + eval_always: $eval_always:literal, + feedable: $feedable:literal, + no_hash: $no_hash:literal, + returns_error_guaranteed: $returns_error_guaranteed:literal, + separate_provide_extern: $separate_provide_extern:literal, + } + )* + } + // Non-queries are unused here. + non_queries { $($_:tt)* } ) => { - pub(crate) mod query_impl { $(pub(crate) mod $name { use super::super::*; - use std::marker::PhantomData; use ::rustc_middle::query::erase::{self, Erased}; - pub(crate) mod get_query_incr { + // It seems to be important that every query has its own monomorphic + // copy of `execute_query_incr` and `execute_query_non_incr`. + // Trying to inline these wrapper functions into their generic + // "inner" helpers tends to break `tests/run-make/short-ice`. + + pub(crate) mod execute_query_incr { use super::*; // Adding `__rust_end_short_backtrace` marker to backtraces so that we emit the frames @@ -559,9 +352,9 @@ pub(crate) fn __rust_end_short_backtrace<'tcx>( ) -> Option>> { #[cfg(debug_assertions)] let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); - execution::get_query_incr( - QueryType::query_dispatcher(tcx), - QueryCtxt::new(tcx), + execution::execute_query_incr_inner( + &tcx.query_system.query_vtables.$name, + tcx, span, key, mode @@ -569,7 +362,7 @@ pub(crate) fn __rust_end_short_backtrace<'tcx>( } } - pub(crate) mod get_query_non_incr { + pub(crate) mod execute_query_non_incr { use super::*; #[inline(never)] @@ -579,9 +372,9 @@ pub(crate) fn __rust_end_short_backtrace<'tcx>( key: queries::$name::Key<'tcx>, __mode: QueryMode, ) -> Option>> { - Some(execution::get_query_non_incr( - QueryType::query_dispatcher(tcx), - QueryCtxt::new(tcx), + Some(execution::execute_query_non_incr_inner( + &tcx.query_system.query_vtables.$name, + tcx, span, key, )) @@ -606,7 +399,16 @@ pub(crate) fn __rust_begin_short_backtrace<'tcx>( let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); // Call the actual provider function for this query. - let provided_value = call_provider!([$($modifiers)*][tcx, $name, key]); + + #[cfg($separate_provide_extern)] + let provided_value = if let Some(local_key) = key.as_local_key() { + (tcx.query_system.local_providers.$name)(tcx, local_key) + } else { + (tcx.query_system.extern_providers.$name)(tcx, key) + }; + + #[cfg(not($separate_provide_extern))] + let provided_value = (tcx.query_system.local_providers.$name)(tcx, key); rustc_middle::ty::print::with_reduced_queries!({ tracing::trace!(?provided_value); @@ -618,244 +420,187 @@ pub(crate) fn __rust_begin_short_backtrace<'tcx>( } } - pub(crate) fn make_query_vtable<'tcx>() - -> QueryVTable<'tcx, queries::$name::Storage<'tcx>> + pub(crate) fn make_query_vtable<'tcx>(incremental: bool) + -> QueryVTable<'tcx, queries::$name::Cache<'tcx>> { QueryVTable { name: stringify!($name), - eval_always: is_eval_always!([$($modifiers)*]), - dep_kind: dep_graph::dep_kinds::$name, - cycle_error_handling: cycle_error_handling!([$($modifiers)*]), - query_state: std::mem::offset_of!(QueryStates<'tcx>, $name), - query_cache: std::mem::offset_of!(QueryCaches<'tcx>, $name), - will_cache_on_disk_for_key_fn: if_cache_on_disk!([$($modifiers)*] { - Some(::rustc_middle::queries::_cache_on_disk_if_fns::$name) - } { - None - }), - call_query_method_fn: |tcx, key| { - // Call the query method for its side-effect of loading a value - // from disk-cache; the caller doesn't need the value. - let _ = tcx.$name(key); - }, + anon: $anon, + eval_always: $eval_always, + depth_limit: $depth_limit, + feedable: $feedable, + dep_kind: dep_graph::DepKind::$name, + cycle_error_handling: + rustc_middle::query::CycleErrorHandling::$cycle_error_handling, + state: Default::default(), + cache: Default::default(), + invoke_provider_fn: self::invoke_provider_fn::__rust_begin_short_backtrace, - try_load_from_disk_fn: if_cache_on_disk!([$($modifiers)*] { - Some(|tcx, key, prev_index, index| { - // Check the `cache_on_disk_if` condition for this key. - if !::rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) { - return None; - } - let value: queries::$name::ProvidedValue<'tcx> = - $crate::plumbing::try_load_from_disk(tcx, prev_index, index)?; + #[cfg($cache_on_disk)] + will_cache_on_disk_for_key_fn: + rustc_middle::queries::_cache_on_disk_if_fns::$name, + #[cfg(not($cache_on_disk))] + will_cache_on_disk_for_key_fn: |_, _| false, + + #[cfg($cache_on_disk)] + try_load_from_disk_fn: |tcx, key, prev_index, index| { + // Check the `cache_on_disk_if` condition for this key. + if !rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) { + return None; + } + + let value: queries::$name::ProvidedValue<'tcx> = + $crate::plumbing::try_load_from_disk(tcx, prev_index, index)?; + + // Arena-alloc the value if appropriate, and erase it. + Some(queries::$name::provided_to_erased(tcx, value)) + }, + #[cfg(not($cache_on_disk))] + try_load_from_disk_fn: |_tcx, _key, _prev_index, _index| None, + + #[cfg($cache_on_disk)] + is_loadable_from_disk_fn: |tcx, key, index| -> bool { + rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) && + $crate::plumbing::loadable_from_disk(tcx, index) + }, + #[cfg(not($cache_on_disk))] + is_loadable_from_disk_fn: |_tcx, _key, _index| false, - // Arena-alloc the value if appropriate, and erase it. - Some(queries::$name::provided_to_erased(tcx, value)) - }) - } { - None - }), - is_loadable_from_disk_fn: if_cache_on_disk!([$($modifiers)*] { - Some(|tcx, key, index| -> bool { - ::rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) && - $crate::plumbing::loadable_from_disk(tcx, index) - }) - } { - None - }), value_from_cycle_error: |tcx, cycle, guar| { - let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle, guar); + let result: queries::$name::Value<'tcx> = + FromCycleError::from_cycle_error(tcx, cycle, guar); erase::erase_val(result) }, - hash_result: hash_result!([$($modifiers)*][queries::$name::Value<'tcx>]), + + #[cfg($no_hash)] + hash_value_fn: None, + #[cfg(not($no_hash))] + hash_value_fn: Some(|hcx, erased_value: &erase::Erased>| { + let value = erase::restore_val(*erased_value); + rustc_middle::dep_graph::hash_result(hcx, &value) + }), + format_value: |value| format!("{:?}", erase::restore_val::>(*value)), description_fn: $crate::queries::_description_fns::$name, + execute_query_fn: if incremental { + query_impl::$name::execute_query_incr::__rust_end_short_backtrace + } else { + query_impl::$name::execute_query_non_incr::__rust_end_short_backtrace + }, } } - #[derive(Copy, Clone, Default)] - pub(crate) struct QueryType<'tcx> { - data: PhantomData<&'tcx ()> - } + /// Marker type that implements [`GetQueryVTable`] for this query. + pub(crate) enum VTableGetter {} - const FLAGS: QueryFlags = QueryFlags { - is_anon: is_anon!([$($modifiers)*]), - is_depth_limit: depth_limit!([$($modifiers)*]), - is_feedable: feedable!([$($modifiers)*]), - }; - - impl<'tcx> QueryDispatcherUnerased<'tcx, queries::$name::Storage<'tcx>, FLAGS> - for QueryType<'tcx> - { - type UnerasedValue = queries::$name::Value<'tcx>; - - const NAME: &'static &'static str = &stringify!($name); + impl<'tcx> GetQueryVTable<'tcx> for VTableGetter { + type Cache = rustc_middle::queries::$name::Cache<'tcx>; #[inline(always)] - fn query_dispatcher(tcx: TyCtxt<'tcx>) - -> SemiDynamicQueryDispatcher<'tcx, queries::$name::Storage<'tcx>, FLAGS> - { - SemiDynamicQueryDispatcher { - vtable: &tcx.query_system.query_vtables.$name, - } + fn query_vtable(tcx: TyCtxt<'tcx>) -> &'tcx QueryVTable<'tcx, Self::Cache> { + &tcx.query_system.query_vtables.$name } - - #[inline(always)] - fn restore_val(value: as QueryCache>::Value) - -> Self::UnerasedValue - { - erase::restore_val::>(value) - } - } - - /// Internal per-query plumbing for collecting the set of active jobs for this query. - /// - /// Should only be called through `PER_QUERY_GATHER_ACTIVE_JOBS_FNS`. - pub(crate) fn gather_active_jobs<'tcx>( - tcx: TyCtxt<'tcx>, - require_complete: bool, - job_map_out: &mut QueryJobMap<'tcx>, - ) -> Option<()> { - let make_frame = |tcx: TyCtxt<'tcx>, key| { - let vtable = &tcx.query_system.query_vtables.$name; - $crate::plumbing::create_deferred_query_stack_frame(tcx, vtable, key) - }; - - // Call `gather_active_jobs_inner` to do the actual work. - let res = crate::execution::gather_active_jobs_inner(&tcx.query_system.states.$name, - tcx, - make_frame, - require_complete, - job_map_out, - ); - - // this can be called during unwinding, and the function has a `try_`-prefix, so - // don't `unwrap()` here, just manually check for `None` and do best-effort error - // reporting. - if res.is_none() { - tracing::warn!( - "Failed to collect active jobs for query with name `{}`!", - stringify!($name) - ); - } - res - } - - pub(crate) fn alloc_self_profile_query_strings<'tcx>( - tcx: TyCtxt<'tcx>, - string_cache: &mut QueryKeyStringCache - ) { - $crate::profiling_support::alloc_self_profile_query_strings_for_query_cache( - tcx, - stringify!($name), - &tcx.query_system.caches.$name, - string_cache, - ) - } - - item_if_cache_on_disk! { [$($modifiers)*] - pub(crate) fn encode_query_results<'tcx>( - tcx: TyCtxt<'tcx>, - encoder: &mut CacheEncoder<'_, 'tcx>, - query_result_index: &mut EncodedDepNodeIndex - ) { - $crate::plumbing::encode_query_results::< - query_impl::$name::QueryType<'tcx>, - _, - _ - > ( - query_impl::$name::QueryType::query_dispatcher(tcx), - QueryCtxt::new(tcx), - encoder, - query_result_index, - ) - } - } - - pub(crate) fn query_key_hash_verify<'tcx>(tcx: TyCtxt<'tcx>) { - $crate::plumbing::query_key_hash_verify( - query_impl::$name::QueryType::query_dispatcher(tcx), - QueryCtxt::new(tcx), - ) } })*} - pub(crate) fn engine(incremental: bool) -> QueryEngine { - if incremental { - QueryEngine { - $($name: query_impl::$name::get_query_incr::__rust_end_short_backtrace,)* - } - } else { - QueryEngine { - $($name: query_impl::$name::get_query_non_incr::__rust_end_short_backtrace,)* - } - } - } - - pub fn make_query_vtables<'tcx>() -> queries::PerQueryVTables<'tcx> { - queries::PerQueryVTables { + pub fn make_query_vtables<'tcx>(incremental: bool) -> queries::QueryVTables<'tcx> { + queries::QueryVTables { $( - $name: query_impl::$name::make_query_vtable(), + $name: query_impl::$name::make_query_vtable(incremental), )* } } - // These arrays are used for iteration and can't be indexed by `DepKind`. - - /// Used by `collect_active_jobs_from_all_queries` to iterate over all - /// queries, and gather the active jobs for each query. + /// Returns a map of currently active query jobs, collected from all queries. /// - /// (We arbitrarily use the word "gather" when collecting the jobs for - /// each individual query, so that we have distinct function names to - /// grep for.) - const PER_QUERY_GATHER_ACTIVE_JOBS_FNS: &[ - for<'tcx> fn( - tcx: TyCtxt<'tcx>, - require_complete: bool, - job_map_out: &mut QueryJobMap<'tcx>, - ) -> Option<()> - ] = &[ - $( $crate::query_impl::$name::gather_active_jobs ),* - ]; - - const ALLOC_SELF_PROFILE_QUERY_STRINGS: &[ - for<'tcx> fn(TyCtxt<'tcx>, &mut QueryKeyStringCache) - ] = &[$(query_impl::$name::alloc_self_profile_query_strings),*]; - - const ENCODE_QUERY_RESULTS: &[ - Option fn( - TyCtxt<'tcx>, - &mut CacheEncoder<'_, 'tcx>, - &mut EncodedDepNodeIndex) - > - ] = &[ - $( - if_cache_on_disk!([$($modifiers)*] { - Some(query_impl::$name::encode_query_results) - } { - None - }) - ),* - ]; - - const QUERY_KEY_HASH_VERIFY: &[ - for<'tcx> fn(TyCtxt<'tcx>) - ] = &[$(query_impl::$name::query_key_hash_verify),*]; - - /// Declares a dep-kind vtable constructor for each query. - mod _dep_kind_vtable_ctors_for_queries { - use ::rustc_middle::dep_graph::DepKindVTable; - use $crate::dep_kind_vtables::make_dep_kind_vtable_for_query; + /// If `require_complete` is `true`, this function locks all shards of the + /// query results to produce a complete map, which always returns `Ok`. + /// Otherwise, it may return an incomplete map as an error if any shard + /// lock cannot be acquired. + /// + /// Prefer passing `false` to `require_complete` to avoid potential deadlocks, + /// especially when called from within a deadlock handler, unless a + /// complete map is needed and no deadlock is possible at this call site. + pub fn collect_active_jobs_from_all_queries<'tcx>( + tcx: TyCtxt<'tcx>, + require_complete: bool, + ) -> Result, QueryJobMap<'tcx>> { + let mut job_map_out = QueryJobMap::default(); + let mut complete = true; $( - /// `DepKindVTable` constructor for this query. - pub(crate) fn $name<'tcx>() -> DepKindVTable<'tcx> { - use $crate::query_impl::$name::QueryType; - make_dep_kind_vtable_for_query::, _, _>( - is_eval_always!([$($modifiers)*]), + let res = crate::execution::gather_active_jobs( + &tcx.query_system.query_vtables.$name, + tcx, + require_complete, + &mut job_map_out, + ); + if res.is_none() { + complete = false; + } + )* + + if complete { Ok(job_map_out) } else { Err(job_map_out) } + } + + /// All self-profiling events generated by the query engine use + /// virtual `StringId`s for their `event_id`. This method makes all + /// those virtual `StringId`s point to actual strings. + /// + /// If we are recording only summary data, the ids will point to + /// just the query names. If we are recording query keys too, we + /// allocate the corresponding strings here. + pub fn alloc_self_profile_query_strings(tcx: TyCtxt<'_>) { + if !tcx.prof.enabled() { + return; + } + + let _prof_timer = tcx.sess.prof.generic_activity("self_profile_alloc_query_strings"); + + let mut string_cache = QueryKeyStringCache::new(); + + $( + $crate::profiling_support::alloc_self_profile_query_strings_for_query_cache( + tcx, + stringify!($name), + &tcx.query_system.query_vtables.$name.cache, + &mut string_cache, + ); + )* + + tcx.sess.prof.store_query_cache_hits(); + } + + fn encode_all_query_results<'tcx>( + tcx: TyCtxt<'tcx>, + encoder: &mut CacheEncoder<'_, 'tcx>, + query_result_index: &mut EncodedDepNodeIndex, + ) { + $( + #[cfg($cache_on_disk)] + { + $crate::plumbing::encode_query_results( + tcx, + &tcx.query_system.query_vtables.$name, + encoder, + query_result_index, ) } )* } + + pub fn query_key_hash_verify_all<'tcx>(tcx: TyCtxt<'tcx>) { + if tcx.sess.opts.unstable_opts.incremental_verify_ich || cfg!(debug_assertions) { + tcx.sess.time("query_key_hash_verify_all", || { + $( + $crate::plumbing::query_key_hash_verify( + &tcx.query_system.query_vtables.$name, + tcx + ); + )* + }) + } + } } } diff --git a/compiler/rustc_query_impl/src/profiling_support.rs b/compiler/rustc_query_impl/src/profiling_support.rs index 679fee49b6c6..0c0e966f1fa4 100644 --- a/compiler/rustc_query_impl/src/profiling_support.rs +++ b/compiler/rustc_query_impl/src/profiling_support.rs @@ -14,7 +14,7 @@ pub(crate) struct QueryKeyStringCache { } impl QueryKeyStringCache { - fn new() -> QueryKeyStringCache { + pub(crate) fn new() -> QueryKeyStringCache { QueryKeyStringCache { def_id_cache: Default::default() } } } @@ -200,7 +200,7 @@ pub(crate) fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>( // locked while doing so. Instead we copy out the // `(query_key, dep_node_index)` pairs and release the lock again. let mut query_keys_and_indices = Vec::new(); - query_cache.iter(&mut |k, _, i| query_keys_and_indices.push((*k, i))); + query_cache.for_each(&mut |k, _, i| query_keys_and_indices.push((*k, i))); // Now actually allocate the strings. If allocating the strings // generates new entries in the query cache, we'll miss them but @@ -228,7 +228,7 @@ pub(crate) fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>( // instead of passing the `DepNodeIndex` to `finish_with_query_invocation_id`, // when recording the event in the first place. let mut query_invocation_ids = Vec::new(); - query_cache.iter(&mut |_, _, i| { + query_cache.for_each(&mut |_, _, i| { query_invocation_ids.push(i.into()); }); @@ -239,25 +239,3 @@ pub(crate) fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>( } }); } - -/// All self-profiling events generated by the query engine use -/// virtual `StringId`s for their `event_id`. This method makes all -/// those virtual `StringId`s point to actual strings. -/// -/// If we are recording only summary data, the ids will point to -/// just the query names. If we are recording query keys too, we -/// allocate the corresponding strings here. -pub fn alloc_self_profile_query_strings(tcx: TyCtxt<'_>) { - if !tcx.prof.enabled() { - return; - } - - let _prof_timer = tcx.sess.prof.generic_activity("self_profile_alloc_query_strings"); - - let mut string_cache = QueryKeyStringCache::new(); - - for alloc in super::ALLOC_SELF_PROFILE_QUERY_STRINGS.iter() { - alloc(tcx, &mut string_cache) - } - tcx.sess.prof.store_query_cache_hits(); -} diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml deleted file mode 100644 index bd12dcbfe0d1..000000000000 --- a/compiler/rustc_query_system/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "rustc_query_system" -version = "0.0.0" -edition = "2024" - -[dependencies] -# tidy-alphabetical-start -rustc_abi = { path = "../rustc_abi" } -rustc_ast = { path = "../rustc_ast" } -rustc_data_structures = { path = "../rustc_data_structures" } -rustc_errors = { path = "../rustc_errors" } -rustc_feature = { path = "../rustc_feature" } -rustc_hir = { path = "../rustc_hir" } -rustc_macros = { path = "../rustc_macros" } -rustc_serialize = { path = "../rustc_serialize" } -rustc_session = { path = "../rustc_session" } -rustc_span = { path = "../rustc_span" } -smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -# tidy-alphabetical-end diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs deleted file mode 100644 index bb077d02422b..000000000000 --- a/compiler/rustc_query_system/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -// tidy-alphabetical-start -#![allow(internal_features)] -#![cfg_attr(bootstrap, feature(assert_matches))] -#![feature(min_specialization)] -// tidy-alphabetical-end - -pub mod ich; -pub mod query; diff --git a/compiler/rustc_query_system/src/query/README.md b/compiler/rustc_query_system/src/query/README.md deleted file mode 100644 index 8ec07b9fdeb7..000000000000 --- a/compiler/rustc_query_system/src/query/README.md +++ /dev/null @@ -1,3 +0,0 @@ -For more information about how the query system works, see the [rustc dev guide]. - -[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/query.html diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs deleted file mode 100644 index 87be4358fb8b..000000000000 --- a/compiler/rustc_query_system/src/query/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::fmt::Debug; - -use rustc_errors::DiagInner; -use rustc_macros::{Decodable, Encodable}; - -/// Tracks 'side effects' for a particular query. -/// This struct is saved to disk along with the query result, -/// and loaded from disk if we mark the query as green. -/// This allows us to 'replay' changes to global state -/// that would otherwise only occur if we actually -/// executed the query method. -/// -/// Each side effect gets an unique dep node index which is added -/// as a dependency of the query which had the effect. -#[derive(Debug, Encodable, Decodable)] -pub enum QuerySideEffect { - /// Stores a diagnostic emitted during query execution. - /// This diagnostic will be re-emitted if we mark - /// the query as green, as that query will have the side - /// effect dep node as a dependency. - Diagnostic(DiagInner), -} diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index f0dffd8829da..a280acc0d51d 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -331,8 +331,8 @@ fn build_reduced_graph_for_external_crate_res( DefKind::Fn | DefKind::AssocFn | DefKind::Static { .. } - | DefKind::Const - | DefKind::AssocConst + | DefKind::Const { .. } + | DefKind::AssocConst { .. } | DefKind::Ctor(..), _, ) => define_extern(ValueNS), @@ -469,9 +469,15 @@ fn try_resolve_visibility<'ast>( PathResult::NonModule(partial_res) => { expected_found_error(partial_res.expect_full_res()) } - PathResult::Failed { span, label, suggestion, .. } => { - Err(VisResolutionError::FailedToResolve(span, label, suggestion)) - } + PathResult::Failed { + span, label, suggestion, message, segment_name, .. + } => Err(VisResolutionError::FailedToResolve( + span, + segment_name, + label, + suggestion, + message, + )), PathResult::Indeterminate => Err(VisResolutionError::Indeterminate(path.span)), } } @@ -614,97 +620,101 @@ fn build_reduced_graph_for_use_tree( let prefix = crate_root.into_iter().chain(prefix_iter).collect::>(); debug!("build_reduced_graph_for_use_tree: prefix={:?}", prefix); - let empty_for_self = |prefix: &[Segment]| { - prefix.is_empty() || prefix.len() == 1 && prefix[0].ident.name == kw::PathRoot - }; match use_tree.kind { ast::UseTreeKind::Simple(rename) => { let mut ident = use_tree.ident(); let mut module_path = prefix; let mut source = module_path.pop().unwrap(); - let mut type_ns_only = false; - if nested { - // Correctly handle `self` - if source.ident.name == kw::SelfLower { - type_ns_only = true; + // `true` for `...::{self [as target]}` imports, `false` otherwise. + let type_ns_only = nested && source.ident.name == kw::SelfLower; - if empty_for_self(&module_path) { - self.r.report_error( - use_tree.span, - ResolutionError::SelfImportOnlyInImportListWithNonEmptyPrefix, - ); - return; - } - - // Replace `use foo::{ self };` with `use foo;` - let self_span = source.ident.span; - source = module_path.pop().unwrap(); - if rename.is_none() { - // Keep the span of `self`, but the name of `foo` - ident = Ident::new(source.ident.name, self_span); - } - } - } else { - // Disallow `self` - if source.ident.name == kw::SelfLower { - let parent = module_path.last(); - - let span = match parent { - // only `::self` from `use foo::self as bar` - Some(seg) => seg.ident.span.shrink_to_hi().to(source.ident.span), - None => source.ident.span, - }; + if source.ident.name == kw::SelfLower + && let Some(parent) = module_path.pop() + { + // Suggest `use prefix::{self};` for `use prefix::self;` + if !type_ns_only + && (parent.ident.name != kw::PathRoot + || self.r.path_root_is_crate_root(parent.ident)) + { let span_with_rename = match rename { - // only `self as bar` from `use foo::self as bar` Some(rename) => source.ident.span.to(rename.span), None => source.ident.span, }; + self.r.report_error( - span, + parent.ident.span.shrink_to_hi().to(source.ident.span), ResolutionError::SelfImportsOnlyAllowedWithin { - root: parent.is_none(), + root: parent.ident.name == kw::PathRoot, span_with_rename, }, ); - - // Error recovery: replace `use foo::self;` with `use foo;` - if let Some(parent) = module_path.pop() { - source = parent; - if rename.is_none() { - ident = source.ident; - } - } } - // Disallow `use $crate;` - if source.ident.name == kw::DollarCrate && module_path.is_empty() { - let crate_root = self.r.resolve_crate_root(source.ident); - let crate_name = match crate_root.kind { - ModuleKind::Def(.., name) => name, - ModuleKind::Block => unreachable!(), - }; - // HACK(eddyb) unclear how good this is, but keeping `$crate` - // in `source` breaks `tests/ui/imports/import-crate-var.rs`, - // while the current crate doesn't have a valid `crate_name`. - if let Some(crate_name) = crate_name { - // `crate_name` should not be interpreted as relative. - module_path.push(Segment::from_ident_and_id( - Ident::new(kw::PathRoot, source.ident.span), - self.r.next_node_id(), - )); - source.ident.name = crate_name; - } - if rename.is_none() { - ident.name = sym::dummy; - } - - self.r.dcx().emit_err(errors::CrateImported { span: item.span }); + let self_span = source.ident.span; + source = parent; + if rename.is_none() { + ident = Ident::new(source.ident.name, self_span); } } - if ident.name == kw::Crate { - self.r.dcx().emit_err(errors::UnnamedCrateRootImport { span: ident.span }); + match source.ident.name { + kw::DollarCrate => { + if !module_path.is_empty() { + self.r.dcx().span_err( + source.ident.span, + "`$crate` in paths can only be used in start position", + ); + return; + } + } + kw::Crate => { + if !module_path.is_empty() { + self.r.dcx().span_err( + source.ident.span, + "`crate` in paths can only be used in start position", + ); + return; + } + } + kw::Super => { + // Allow `self::super` as a valid prefix - `self` at position 0 + // followed by any number of `super` segments. + let valid_prefix = module_path.iter().enumerate().all(|(i, seg)| { + let name = seg.ident.name; + name == kw::Super || (name == kw::SelfLower && i == 0) + }); + + if !valid_prefix { + self.r.dcx().span_err( + source.ident.span, + "`super` in paths can only be used in start position, after `self`, or after another `super`", + ); + return; + } + } + // Deny `use ::{self};` after edition 2015 + kw::PathRoot if !self.r.path_root_is_crate_root(source.ident) => { + self.r.dcx().span_err(use_tree.span, "extern prelude cannot be imported"); + return; + } + _ => {} + } + + // Deny importing path-kw without renaming + if rename.is_none() && ident.is_path_segment_keyword() { + let ident = use_tree.ident(); + + // Don't suggest `use xx::self as name;` for `use xx::self;` + // But it's OK to suggest `use xx::{self as name};` for `use xx::{self};` + let sugg = if !type_ns_only && ident.name == kw::SelfLower { + None + } else { + Some(errors::UnnamedImportSugg { span: ident.span, ident }) + }; + + self.r.dcx().emit_err(errors::UnnamedImport { span: ident.span, sugg }); + return; } let kind = ImportKind::Single { @@ -734,32 +744,6 @@ fn build_reduced_graph_for_use_tree( } } ast::UseTreeKind::Nested { ref items, .. } => { - // Ensure there is at most one `self` in the list - let self_spans = items - .iter() - .filter_map(|(use_tree, _)| { - if let ast::UseTreeKind::Simple(..) = use_tree.kind - && use_tree.ident().name == kw::SelfLower - { - return Some(use_tree.span); - } - - None - }) - .collect::>(); - if self_spans.len() > 1 { - let mut e = self.r.into_struct_error( - self_spans[0], - ResolutionError::SelfImportCanOnlyAppearOnceInTheList, - ); - - for other_span in self_spans.iter().skip(1) { - e.span_label(*other_span, "another `self` import appears here"); - } - - e.emit(); - } - for &(ref tree, id) in items { self.build_reduced_graph_for_use_tree( // This particular use tree @@ -771,7 +755,10 @@ fn build_reduced_graph_for_use_tree( // Empty groups `a::b::{}` are turned into synthetic `self` imports // `a::b::c::{self as _}`, so that their prefixes are correctly // resolved and checked for privacy/stability/etc. - if items.is_empty() && !empty_for_self(&prefix) { + if items.is_empty() + && !prefix.is_empty() + && (prefix.len() > 1 || prefix[0].ident.name != kw::PathRoot) + { let new_span = prefix[prefix.len() - 1].ident.span; let tree = ast::UseTree { prefix: ast::Path::from_ident(Ident::new(kw::SelfLower, new_span)), diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index e2fea9146c79..81820e049716 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -131,7 +131,11 @@ fn visit_item(&mut self, i: &'a Item) { mutability: s.mutability, nested: false, }, - ItemKind::Const(..) | ItemKind::ConstBlock(..) => DefKind::Const, + ItemKind::Const(citem) => { + let is_type_const = matches!(citem.rhs_kind, ConstItemRhsKind::TypeConst { .. }); + DefKind::Const { is_type_const } + } + ItemKind::ConstBlock(..) => DefKind::Const { is_type_const: false }, ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn, ItemKind::MacroDef(ident, def) => { let edition = i.span.edition(); @@ -347,7 +351,12 @@ fn visit_assoc_item(&mut self, i: &'a AssocItem, ctxt: visit::AssocCtxt) { let (ident, def_kind) = match &i.kind { AssocItemKind::Fn(box Fn { ident, .. }) | AssocItemKind::Delegation(box Delegation { ident, .. }) => (*ident, DefKind::AssocFn), - AssocItemKind::Const(box ConstItem { ident, .. }) => (*ident, DefKind::AssocConst), + AssocItemKind::Const(box ConstItem { ident, rhs_kind, .. }) => ( + *ident, + DefKind::AssocConst { + is_type_const: matches!(rhs_kind, ConstItemRhsKind::TypeConst { .. }), + }, + ), AssocItemKind::Type(box TyAlias { ident, .. }) => (*ident, DefKind::AssocTy), AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => { return self.visit_macro_invoc(i.id); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index ac6188c2c152..1b1198af41c0 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength use std::ops::ControlFlow; use itertools::Itertools as _; @@ -14,7 +15,7 @@ struct_span_code_err, }; use rustc_feature::BUILTIN_ATTRIBUTES; -use rustc_hir::attrs::{AttributeKind, CfgEntry, StrippedCfgItem}; +use rustc_hir::attrs::{CfgEntry, StrippedCfgItem}; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, MacroKinds, NonMacroAttrKind, PerNS}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; @@ -32,7 +33,7 @@ use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::{SourceMap, Spanned}; -use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym}; +use rustc_span::{BytePos, Ident, RemapPathScopeComponents, Span, Symbol, SyntaxContext, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, instrument}; @@ -136,7 +137,7 @@ pub(crate) fn report_errors(&mut self, krate: &Crate) { } for ambiguity_error in &self.ambiguity_errors { - let diag = self.ambiguity_diagnostic(ambiguity_error); + let mut diag = self.ambiguity_diagnostic(ambiguity_error); if let Some(ambiguity_warning) = ambiguity_error.warning { let node_id = match ambiguity_error.b1.0.kind { @@ -152,6 +153,7 @@ pub(crate) fn report_errors(&mut self, krate: &Crate) { self.lint_buffer.buffer_lint(lint, node_id, diag.ident.span, diag); } else { + diag.is_error = true; self.dcx().emit_err(diag); } } @@ -249,20 +251,23 @@ pub(crate) fn report_conflict( }; let label = match new_binding.is_import_user_facing() { - true => errors::NameDefinedMultipleTimeLabel::Reimported { span }, - false => errors::NameDefinedMultipleTimeLabel::Redefined { span }, + true => errors::NameDefinedMultipleTimeLabel::Reimported { span, name }, + false => errors::NameDefinedMultipleTimeLabel::Redefined { span, name }, }; let old_binding_label = (!old_binding.span.is_dummy() && old_binding.span != span).then(|| { let span = self.tcx.sess.source_map().guess_head_span(old_binding.span); match old_binding.is_import_user_facing() { - true => { - errors::NameDefinedMultipleTimeOldBindingLabel::Import { span, old_kind } - } + true => errors::NameDefinedMultipleTimeOldBindingLabel::Import { + span, + old_kind, + name, + }, false => errors::NameDefinedMultipleTimeOldBindingLabel::Definition { span, old_kind, + name, }, } }); @@ -557,7 +562,9 @@ pub(crate) fn into_struct_error( DefKind::Static { .. } => { Some(errs::GenericParamsFromOuterItemStaticOrConst::Static) } - DefKind::Const => Some(errs::GenericParamsFromOuterItemStaticOrConst::Const), + DefKind::Const { .. } => { + Some(errs::GenericParamsFromOuterItemStaticOrConst::Const) + } _ => None, }; let is_self = @@ -573,6 +580,7 @@ pub(crate) fn into_struct_error( errs::GenericParamsFromOuterItemInnerItem { span: *span, descr: kind.descr().to_string(), + is_self, } }), }; @@ -720,8 +728,8 @@ pub(crate) fn into_struct_error( Res::Def( DefKind::Ctor(CtorOf::Variant, CtorKind::Const) | DefKind::Ctor(CtorOf::Struct, CtorKind::Const) - | DefKind::Const - | DefKind::AssocConst, + | DefKind::Const { .. } + | DefKind::AssocConst { .. }, _, ) ) @@ -729,11 +737,11 @@ pub(crate) fn into_struct_error( ); if import_suggestions.is_empty() && !suggested_typo { - let kinds = [ - DefKind::Ctor(CtorOf::Variant, CtorKind::Const), - DefKind::Ctor(CtorOf::Struct, CtorKind::Const), - DefKind::Const, - DefKind::AssocConst, + let kind_matches: [fn(DefKind) -> bool; 4] = [ + |kind| matches!(kind, DefKind::Ctor(CtorOf::Variant, CtorKind::Const)), + |kind| matches!(kind, DefKind::Ctor(CtorOf::Struct, CtorKind::Const)), + |kind| matches!(kind, DefKind::Const { .. }), + |kind| matches!(kind, DefKind::AssocConst { .. }), ]; let mut local_names = vec![]; self.add_module_candidates( @@ -752,13 +760,13 @@ pub(crate) fn into_struct_error( let mut local_suggestions = vec![]; let mut suggestions = vec![]; - for kind in kinds { + for matches_kind in kind_matches { if let Some(suggestion) = self.early_lookup_typo_candidate( ScopeSet::All(Namespace::ValueNS), &parent_scope, name, &|res: Res| match res { - Res::Def(k, _) => k == kind, + Res::Def(k, _) => matches_kind(k), _ => false, }, ) && let Res::Def(kind, mut def_id) = suggestion.res @@ -893,15 +901,8 @@ pub(crate) fn into_struct_error( mpart_suggestion, }) } - ResolutionError::SelfImportCanOnlyAppearOnceInTheList => { - self.dcx().create_err(errs::SelfImportCanOnlyAppearOnceInTheList { span }) - } - ResolutionError::SelfImportOnlyInImportListWithNonEmptyPrefix => { - self.dcx().create_err(errs::SelfImportOnlyInImportListWithNonEmptyPrefix { span }) - } - ResolutionError::FailedToResolve { segment, label, suggestion, module } => { - let mut err = - struct_span_code_err!(self.dcx(), span, E0433, "failed to resolve: {label}"); + ResolutionError::FailedToResolve { segment, label, suggestion, module, message } => { + let mut err = struct_span_code_err!(self.dcx(), span, E0433, "{message}"); err.span_label(span, label); if let Some((suggestions, msg, applicability)) = suggestion { @@ -912,13 +913,11 @@ pub(crate) fn into_struct_error( err.multipart_suggestion(msg, suggestions, applicability); } - if let Some(segment) = segment { - let module = match module { - Some(ModuleOrUniformRoot::Module(m)) if let Some(id) = m.opt_def_id() => id, - _ => CRATE_DEF_ID.to_def_id(), - }; - self.find_cfg_stripped(&mut err, &segment, module); - } + let module = match module { + Some(ModuleOrUniformRoot::Module(m)) if let Some(id) = m.opt_def_id() => id, + _ => CRATE_DEF_ID.to_def_id(), + }; + self.find_cfg_stripped(&mut err, &segment, module); err } @@ -1010,12 +1009,14 @@ pub(crate) fn into_struct_error( ResolutionError::ParamInTyOfConstParam { name } => { self.dcx().create_err(errs::ParamInTyOfConstParam { span, name }) } - ResolutionError::ParamInNonTrivialAnonConst { name, param_kind: is_type } => { + ResolutionError::ParamInNonTrivialAnonConst { is_ogca, name, param_kind: is_type } => { self.dcx().create_err(errs::ParamInNonTrivialAnonConst { span, name, param_kind: is_type, help: self.tcx.sess.is_nightly_build(), + is_ogca, + help_ogca: is_ogca, }) } ResolutionError::ParamInEnumDiscriminant { name, param_kind: is_type } => self @@ -1108,10 +1109,17 @@ pub(crate) fn report_vis_error( VisResolutionError::AncestorOnly(span) => { self.dcx().create_err(errs::AncestorOnly(span)) } - VisResolutionError::FailedToResolve(span, label, suggestion) => self.into_struct_error( - span, - ResolutionError::FailedToResolve { segment: None, label, suggestion, module: None }, - ), + VisResolutionError::FailedToResolve(span, segment, label, suggestion, message) => self + .into_struct_error( + span, + ResolutionError::FailedToResolve { + segment, + label, + suggestion, + module: None, + message, + }, + ), VisResolutionError::ExpectedFound(span, path_str, res) => { self.dcx().create_err(errs::ExpectedModuleFound { span, res, path_str }) } @@ -1426,8 +1434,9 @@ fn lookup_import_candidates_from_module( let note = if let Some(did) = did { let requires_note = !did.is_local() && find_attr!( - this.tcx.get_all_attrs(did), - AttributeKind::RustcDiagnosticItem( + this.tcx, + did, + RustcDiagnosticItem( sym::TryInto | sym::TryFrom | sym::FromIterator ) ); @@ -2087,6 +2096,7 @@ fn ambiguity_diagnostic(&self, ambiguity_error: &AmbiguityError<'ra>) -> errors: b1_help_msgs, b2_note, b2_help_msgs, + is_error: false, } } @@ -2166,7 +2176,7 @@ fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { // Otherwise, point out if the struct has any private fields. if let Some(def_id) = res.opt_def_id() && !def_id.is_local() - && let Some(attr_span) = find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::NonExhaustive(span) => *span) + && let Some(attr_span) = find_attr!(self.tcx, def_id, NonExhaustive(span) => *span) { non_exhaustive = Some(attr_span); } else if let Some(span) = ctor_fields_span { @@ -2208,7 +2218,7 @@ fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { }) }) .collect(); - if let Some(def_id) = path.get(0) + if let Some(&def_id) = path.get(0) && let Some(path) = path_names { if let Some(def_id) = def_id.as_local() { @@ -2375,7 +2385,7 @@ fn mention_default_field_values( (last_span.shrink_to_hi(), ", ..".to_string()), ] }; - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!( "the type `{ident}` of field `{}` is private, but you can construct \ the default value defined for it in `{}` using `..` in the struct \ @@ -2438,13 +2448,25 @@ pub(crate) fn report_path_resolution_error( failed_segment_idx: usize, ident: Ident, diag_metadata: Option<&DiagMetadata<'_>>, - ) -> (String, Option) { + ) -> (String, String, Option) { let is_last = failed_segment_idx == path.len() - 1; let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; let module_res = match module { Some(ModuleOrUniformRoot::Module(module)) => module.res(), _ => None, }; + let scope = match &path[..failed_segment_idx] { + [.., prev] => { + if prev.ident.name == kw::PathRoot { + format!("the crate root") + } else { + format!("`{}`", prev.ident) + } + } + _ => format!("this scope"), + }; + let message = format!("cannot find `{ident}` in {scope}"); + if module_res == self.graph_root.res() { let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); @@ -2462,6 +2484,7 @@ pub(crate) fn report_path_resolution_error( Path { segments, span: Span::default(), tokens: None } }; ( + message, String::from("unresolved import"), Some(( vec![(ident.span, pprust::path_to_string(&path))], @@ -2471,6 +2494,7 @@ pub(crate) fn report_path_resolution_error( ) } else if ident.name == sym::core { ( + message, format!("you might be missing crate `{ident}`"), Some(( vec![(ident.span, "std".to_string())], @@ -2479,9 +2503,14 @@ pub(crate) fn report_path_resolution_error( )), ) } else if ident.name == kw::Underscore { - (format!("`_` is not a valid crate or module name"), None) + ( + "invalid crate or module name `_`".to_string(), + "`_` is not a valid crate or module name".to_string(), + None, + ) } else if self.tcx.sess.is_rust_2015() { ( + format!("cannot find module or crate `{ident}` in {scope}"), format!("use of unresolved module or unlinked crate `{ident}`"), Some(( vec![( @@ -2490,8 +2519,9 @@ pub(crate) fn report_path_resolution_error( )], if was_invoked_from_cargo() { format!( - "if you wanted to use a crate named `{ident}`, use `cargo add {ident}` \ - to add it to your `Cargo.toml` and import it in your code", + "if you wanted to use a crate named `{ident}`, use `cargo add \ + {ident}` to add it to your `Cargo.toml` and import it in your \ + code", ) } else { format!( @@ -2503,7 +2533,7 @@ pub(crate) fn report_path_resolution_error( )), ) } else { - (format!("could not find `{ident}` in the crate root"), None) + (message, format!("could not find `{ident}` in the crate root"), None) } } else if failed_segment_idx > 0 { let parent = path[failed_segment_idx - 1].ident.name; @@ -2569,15 +2599,16 @@ pub(crate) fn report_path_resolution_error( ); }; } - (msg, None) + (message, msg, None) } else if ident.name == kw::SelfUpper { // As mentioned above, `opt_ns` being `None` indicates a module path in import. // We can use this to improve a confusing error for, e.g. `use Self::Variant` in an // impl if opt_ns.is_none() { - ("`Self` cannot be used in imports".to_string(), None) + (message, "`Self` cannot be used in imports".to_string(), None) } else { ( + message, "`Self` is only available in impls, traits, and type definitions".to_string(), None, ) @@ -2608,12 +2639,12 @@ pub(crate) fn report_path_resolution_error( // } // ``` Some(LateDecl::RibDef(Res::Local(id))) => { - Some(*self.pat_span_map.get(&id).unwrap()) + Some((*self.pat_span_map.get(&id).unwrap(), "a", "local binding")) } // Name matches item from a local name binding // created by `use` declaration. For example: // ``` - // pub Foo: &str = ""; + // pub const Foo: &str = ""; // // mod submod { // use super::Foo; @@ -2621,18 +2652,27 @@ pub(crate) fn report_path_resolution_error( // // binding `Foo`. // } // ``` - Some(LateDecl::Decl(name_binding)) => Some(name_binding.span), + Some(LateDecl::Decl(name_binding)) => Some(( + name_binding.span, + name_binding.res().article(), + name_binding.res().descr(), + )), _ => None, }; - let suggestion = match_span.map(|span| { - ( - vec![(span, String::from(""))], - format!("`{ident}` is defined here, but is not a type"), - Applicability::MaybeIncorrect, - ) - }); - (format!("use of undeclared type `{ident}`"), suggestion) + let message = format!("cannot find type `{ident}` in {scope}"); + let label = if let Some((span, article, descr)) = match_span { + format!( + "`{ident}` is declared as {article} {descr} at `{}`, not a type", + self.tcx + .sess + .source_map() + .span_to_short_string(span, RemapPathScopeComponents::DIAGNOSTICS) + ) + } else { + format!("use of undeclared type `{ident}`") + }; + (message, label, None) } else { let mut suggestion = None; if ident.name == sym::alloc { @@ -2663,7 +2703,8 @@ pub(crate) fn report_path_resolution_error( ignore_import, ) { let descr = binding.res().descr(); - (format!("{descr} `{ident}` is not a crate or module"), suggestion) + let message = format!("cannot find module or crate `{ident}` in {scope}"); + (message, format!("{descr} `{ident}` is not a crate or module"), suggestion) } else { let suggestion = if suggestion.is_some() { suggestion @@ -2685,7 +2726,12 @@ pub(crate) fn report_path_resolution_error( Applicability::MaybeIncorrect, )) }; - (format!("use of unresolved module or unlinked crate `{ident}`"), suggestion) + let message = format!("cannot find module or crate `{ident}` in {scope}"); + ( + message, + format!("use of unresolved module or unlinked crate `{ident}`"), + suggestion, + ) } } } @@ -2934,7 +2980,7 @@ pub(crate) fn check_for_module_export_macro( corrections.push((import.span, format!("{module_name}::{import_snippet}"))); } else { // Find the binding span (and any trailing commas and spaces). - // ie. `use a::b::{c, d, e};` + // i.e. `use a::b::{c, d, e};` // ^^^ let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( self.tcx.sess, @@ -2946,11 +2992,11 @@ pub(crate) fn check_for_module_export_macro( let mut removal_span = binding_span; // If the binding span ended with a closing brace, as in the below example: - // ie. `use a::b::{c, d};` + // i.e. `use a::b::{c, d};` // ^ // Then expand the span of characters to remove to include the previous // binding's trailing comma. - // ie. `use a::b::{c, d};` + // i.e. `use a::b::{c, d};` // ^^^ if found_closing_brace && let Some(previous_span) = @@ -2966,7 +3012,7 @@ pub(crate) fn check_for_module_export_macro( // Find the span after the crate name and if it has nested imports immediately // after the crate name already. - // ie. `use a::b::{c, d};` + // i.e. `use a::b::{c, d};` // ^^^^^^^^^ // or `use a::{b, c, d}};` // ^^^^^^^^^^^ @@ -3130,16 +3176,16 @@ fn find_span_of_binding_until_next_binding( let source_map = sess.source_map(); // Find the span of everything after the binding. - // ie. `a, e};` or `a};` + // i.e. `a, e};` or `a};` let binding_until_end = binding_span.with_hi(use_span.hi()); // Find everything after the binding but not including the binding. - // ie. `, e};` or `};` + // i.e. `, e};` or `};` let after_binding_until_end = binding_until_end.with_lo(binding_span.hi()); // Keep characters in the span until we encounter something that isn't a comma or // whitespace. - // ie. `, ` or ``. + // i.e. `, ` or ``. // // Also note whether a closing brace character was encountered. If there // was, then later go backwards to remove any trailing commas that are left. @@ -3153,7 +3199,7 @@ fn find_span_of_binding_until_next_binding( }); // Combine the two spans. - // ie. `a, ` or `a`. + // i.e. `a, ` or `a`. // // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };` let span = binding_span.with_hi(after_binding_until_next_binding.hi()); @@ -3177,7 +3223,7 @@ fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option let source_map = sess.source_map(); // `prev_source` will contain all of the source that came before the span. - // Then split based on a command and take the first (ie. closest to our span) + // Then split based on a command and take the first (i.e. closest to our span) // snippet. In the example, this is a space. let prev_source = source_map.span_to_prev_source(binding_span).ok()?; diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index a9b2dabc8ebe..63ffdbc37f7d 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1,9 +1,10 @@ use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagMessage, Diagnostic, ElidedLifetimeInPathSubdiag, - EmissionGuarantee, IntoDiagArg, Level, LintDiagnostic, MultiSpan, Subdiagnostic, msg, + EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, msg, }; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::source_map::Spanned; use rustc_span::{Ident, Span, Symbol}; @@ -51,6 +52,7 @@ pub(crate) struct GenericParamsFromOuterItemInnerItem { #[primary_span] pub(crate) span: Span, pub(crate) descr: String, + pub(crate) is_self: bool, } #[derive(Subdiagnostic)] @@ -248,22 +250,6 @@ pub(crate) struct UnreachableLabelWithSimilarNameExists { pub(crate) ident_span: Span, } -#[derive(Diagnostic)] -#[diag("`self` import can only appear once in an import list", code = E0430)] -pub(crate) struct SelfImportCanOnlyAppearOnceInTheList { - #[primary_span] - #[label("can only appear once in an import list")] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("`self` import can only appear in an import list with a non-empty prefix", code = E0431)] -pub(crate) struct SelfImportOnlyInImportListWithNonEmptyPrefix { - #[primary_span] - #[label("can only appear in an import list with a non-empty prefix")] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag("can't capture dynamic environment in a fn item", code = E0434)] #[help("use the `|| {\"{\"} ... {\"}\"}` closure form instead")] @@ -421,7 +407,12 @@ pub(crate) struct SelfInConstGenericTy { } #[derive(Diagnostic)] -#[diag("generic parameters may not be used in const operations")] +#[diag( + "{$is_ogca -> + [true] generic parameters in const blocks are only allowed as the direct value of a `type const` + *[false] generic parameters may not be used in const operations +}" +)] pub(crate) struct ParamInNonTrivialAnonConst { #[primary_span] #[label("cannot perform const operation using `{$name}`")] @@ -431,6 +422,11 @@ pub(crate) struct ParamInNonTrivialAnonConst { pub(crate) param_kind: ParamKindInNonTrivialAnonConst, #[help("add `#![feature(generic_const_exprs)]` to allow generic const expressions")] pub(crate) help: bool, + pub(crate) is_ogca: bool, + #[help( + "consider factoring the expression into a `type const` item and use it as the const argument instead" + )] + pub(crate) help_ogca: bool, } #[derive(Debug)] @@ -620,7 +616,7 @@ pub(crate) struct ProcMacroSameCrate { pub(crate) is_test: bool, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("cannot find {$ns_descr} `{$ident}` in this scope")] pub(crate) struct ProcMacroDeriveResolutionFallback { #[label("names from parent modules are not accessible without an explicit import")] @@ -629,7 +625,7 @@ pub(crate) struct ProcMacroDeriveResolutionFallback { pub ident: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths" )] @@ -638,13 +634,6 @@ pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { pub definition: Span, } -#[derive(Diagnostic)] -#[diag("`$crate` may not be imported")] -pub(crate) struct CrateImported { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag("`#[macro_use]` is not supported on `extern crate self`")] pub(crate) struct MacroUseExternCrateSelf { @@ -836,7 +825,7 @@ pub(crate) struct CannotBeReexportedCratePublicNS { pub(crate) ident: Ident, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("extern crate `{$ident}` is private and cannot be re-exported", code = E0365)] pub(crate) struct PrivateExternCrateReexport { pub ident: Ident, @@ -973,11 +962,25 @@ pub(crate) struct ArgumentsMacroUseNotAllowed { pub(crate) span: Span, } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "try renaming it with a name", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct UnnamedImportSugg { + #[suggestion_part(code = "{ident} as name")] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + #[derive(Diagnostic)] -#[diag("crate root imports need to be explicitly named: `use crate as name;`")] -pub(crate) struct UnnamedCrateRootImport { +#[diag("imports need to be explicitly named")] +pub(crate) struct UnnamedImport { #[primary_span] pub(crate) span: Span, + #[subdiagnostic] + pub(crate) sugg: Option, } #[derive(Diagnostic)] @@ -1125,11 +1128,13 @@ pub(crate) enum NameDefinedMultipleTimeLabel { Reimported { #[primary_span] span: Span, + name: Symbol, }, #[label("`{$name}` redefined here")] Redefined { #[primary_span] span: Span, + name: Symbol, }, } @@ -1140,12 +1145,14 @@ pub(crate) enum NameDefinedMultipleTimeOldBindingLabel { #[primary_span] span: Span, old_kind: &'static str, + name: Symbol, }, #[label("previous definition of the {$old_kind} `{$name}` here")] Definition { #[primary_span] span: Span, old_kind: &'static str, + name: Symbol, }, } @@ -1210,15 +1217,6 @@ pub(crate) struct ToolWasAlreadyRegistered { pub(crate) old_ident_span: Span, } -#[derive(Diagnostic)] -#[diag("`{$tool}` only accepts identifiers")] -pub(crate) struct ToolOnlyAcceptsIdentifiers { - #[primary_span] - #[label("not an identifier")] - pub(crate) span: Span, - pub(crate) tool: Symbol, -} - #[derive(Subdiagnostic)] pub(crate) enum DefinedHere { #[label("similarly named {$candidate_descr} `{$candidate}` defined here")] @@ -1368,12 +1366,10 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { let mut multispan: MultiSpan = self.span.into(); match self.item_was { ItemWas::BehindFeature { feature, span } => { - let key = "feature".into(); let value = feature.into_diag_arg(&mut None); - let msg = diag.dcx.eagerly_translate_to_string( - msg!("the item is gated behind the `{$feature}` feature"), - [(&key, &value)].into_iter(), - ); + let msg = msg!("the item is gated behind the `{$feature}` feature") + .arg("feature", value) + .format(); multispan.push_span_label(span, msg); } ItemWas::CfgOut { span } => { @@ -1397,14 +1393,14 @@ pub(crate) struct TraitImplMismatch { pub(crate) trait_item_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("derive helper attribute is used before it is introduced")] pub(crate) struct LegacyDeriveHelpers { #[label("the attribute is introduced here")] pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused extern crate")] pub(crate) struct UnusedExternCrate { #[label("unused")] @@ -1418,7 +1414,7 @@ pub(crate) struct UnusedExternCrate { pub removal_span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("{$kind} `{$name}` from private dependency '{$krate}' is re-exported")] pub(crate) struct ReexportPrivateDependency { pub name: Symbol, @@ -1426,32 +1422,32 @@ pub(crate) struct ReexportPrivateDependency { pub krate: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused label")] pub(crate) struct UnusedLabel; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused `#[macro_use]` import")] pub(crate) struct UnusedMacroUse; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("applying the `#[macro_use]` attribute to an `extern crate` item is deprecated")] #[help("remove it and import macros at use sites with a `use` item instead")] pub(crate) struct MacroUseDeprecated; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("macro `{$ident}` is private")] pub(crate) struct MacroIsPrivate { pub ident: Ident, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unused macro definition: `{$name}`")] pub(crate) struct UnusedMacroDefinition { pub name: Symbol, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("rule #{$n} of macro `{$name}` is never used")] pub(crate) struct MacroRuleNeverUsed { pub n: usize, @@ -1462,13 +1458,14 @@ pub(crate) struct UnstableFeature { pub msg: DiagMessage, } -impl<'a> LintDiagnostic<'a, ()> for UnstableFeature { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(self.msg); +impl<'a> Diagnostic<'a, ()> for UnstableFeature { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { msg } = self; + Diag::new(dcx, level, msg) } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("`extern crate` is not idiomatic in the new edition")] pub(crate) struct ExternCrateNotIdiomatic { #[suggestion( @@ -1481,7 +1478,7 @@ pub(crate) struct ExternCrateNotIdiomatic { pub code: &'static str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("cannot find macro `{$path}` in the current scope when looking from {$location}")] #[help("import `macro_rules` with `use` to make it callable above its definition")] pub(crate) struct OutOfScopeMacroCalls { @@ -1491,7 +1488,7 @@ pub(crate) struct OutOfScopeMacroCalls { pub location: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "glob import doesn't reexport anything with visibility `{$import_vis}` because no imported item is public enough" )] @@ -1504,7 +1501,7 @@ pub(crate) struct RedundantImportVisibility { pub max_vis: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unknown diagnostic attribute")] pub(crate) struct UnknownDiagnosticAttribute { #[subdiagnostic] @@ -1534,43 +1531,48 @@ pub(crate) struct Ambiguity { pub b1_help_msgs: Vec, pub b2_note: Spanned, pub b2_help_msgs: Vec, -} - -impl Ambiguity { - fn decorate<'a>(self, diag: &mut Diag<'a, impl EmissionGuarantee>) { - if let Some(ambig_vis) = self.ambig_vis { - diag.primary_message(format!("ambiguous import visibility: {ambig_vis}")); - } else { - diag.primary_message(format!("`{}` is ambiguous", self.ident)); - diag.span_label(self.ident.span, "ambiguous name"); - } - diag.note(format!("ambiguous because of {}", self.kind)); - diag.span_note(self.b1_note.span, self.b1_note.node); - if let Some(help) = self.help { - for help in help { - diag.help(*help); - } - } - for help_msg in self.b1_help_msgs { - diag.help(help_msg); - } - diag.span_note(self.b2_note.span, self.b2_note.node); - for help_msg in self.b2_help_msgs { - diag.help(help_msg); - } - } + /// If false, then it's a lint, if true, then it's an error with the `E0659` error code. + pub is_error: bool, } impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for Ambiguity { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - let mut diag = Diag::new(dcx, level, "").with_span(self.ident.span).with_code(E0659); - self.decorate(&mut diag); + let Self { + ident, + ambig_vis, + kind, + help, + b1_note, + b1_help_msgs, + b2_note, + b2_help_msgs, + is_error, + } = self; + + let mut diag = Diag::new(dcx, level, "").with_span(ident.span); + if is_error { + diag.code(E0659); + } + if let Some(ambig_vis) = ambig_vis { + diag.primary_message(format!("ambiguous import visibility: {ambig_vis}")); + } else { + diag.primary_message(format!("`{}` is ambiguous", ident)); + diag.span_label(ident.span, "ambiguous name"); + } + diag.note(format!("ambiguous because of {}", kind)); + diag.span_note(b1_note.span, b1_note.node); + if let Some(help) = help { + for help in help { + diag.help(*help); + } + } + for help_msg in b1_help_msgs { + diag.help(help_msg); + } + diag.span_note(b2_note.span, b2_note.node); + for help_msg in b2_help_msgs { + diag.help(help_msg); + } diag } } - -impl<'a> LintDiagnostic<'a, ()> for Ambiguity { - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - self.decorate(diag); - } -} diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index d4d373d82064..7cfd5b5f861a 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -923,6 +923,23 @@ pub(crate) fn maybe_resolve_ident_in_module<'r>( self.resolve_ident_in_module(module, ident, ns, parent_scope, None, None, ignore_import) } + fn resolve_super_in_module( + &self, + ident: Ident, + module: Option>, + parent_scope: &ParentScope<'ra>, + ) -> Option> { + let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); + module + .unwrap_or_else(|| self.resolve_self(&mut ctxt, parent_scope.module)) + .parent + .map(|parent| self.resolve_self(&mut ctxt, parent)) + } + + pub(crate) fn path_root_is_crate_root(&self, ident: Ident) -> bool { + ident.name == kw::PathRoot && ident.span.is_rust_2015() && self.tcx.sess.is_rust_2015() + } + #[instrument(level = "debug", skip(self))] pub(crate) fn resolve_ident_in_module<'r>( self: CmResolver<'r, 'ra, 'tcx>, @@ -936,6 +953,14 @@ pub(crate) fn resolve_ident_in_module<'r>( ) -> Result, Determinacy> { match module { ModuleOrUniformRoot::Module(module) => { + if ns == TypeNS + && ident.name == kw::Super + && let Some(module) = + self.resolve_super_in_module(ident, Some(module), parent_scope) + { + return Ok(module.self_decl.unwrap()); + } + let (ident_key, def) = IdentKey::new_adjusted(ident, module.expansion); let adjusted_parent_scope = match def { Some(def) => ParentScope { module: self.expn_def_scope(def), ..*parent_scope }, @@ -976,7 +1001,21 @@ pub(crate) fn resolve_ident_in_module<'r>( } ModuleOrUniformRoot::CurrentScope => { if ns == TypeNS { - if ident.name == kw::Crate || ident.name == kw::DollarCrate { + if ident.name == kw::SelfLower { + let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); + let module = self.resolve_self(&mut ctxt, parent_scope.module); + return Ok(module.self_decl.unwrap()); + } + if ident.name == kw::Super + && let Some(module) = + self.resolve_super_in_module(ident, None, parent_scope) + { + return Ok(module.self_decl.unwrap()); + } + if ident.name == kw::Crate + || ident.name == kw::DollarCrate + || self.path_root_is_crate_root(ident) + { let module = self.resolve_crate_root(ident); return Ok(module.self_decl.unwrap()); } else if ident.name == kw::Super || ident.name == kw::SelfLower { @@ -1256,7 +1295,7 @@ fn finalize_module_binding( .tcx .associated_item_def_ids(def_id) .iter() - .map(|field_id| self.tcx.visibility(field_id)) + .map(|&field_id| self.tcx.visibility(field_id)) .collect(); (ctor_res, ctor_vis, field_visibilities) }) @@ -1515,6 +1554,10 @@ fn validate_res_from_ribs( } NoConstantGenericsReason::NonTrivialConstArg => { ResolutionError::ParamInNonTrivialAnonConst { + is_ogca: self + .tcx + .features() + .opaque_generic_const_args(), name: rib_ident.name, param_kind: ParamKindInNonTrivialAnonConst::Type, } @@ -1606,6 +1649,10 @@ fn validate_res_from_ribs( } NoConstantGenericsReason::NonTrivialConstArg => { ResolutionError::ParamInNonTrivialAnonConst { + is_ogca: self + .tcx + .features() + .opaque_generic_const_args(), name: rib_ident.name, param_kind: ParamKindInNonTrivialAnonConst::Const { name: rib_ident.name, @@ -1754,19 +1801,15 @@ fn record_segment_res<'r, 'ra, 'tcx>( if ns == TypeNS { if allow_super && name == kw::Super { - let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); - let self_module = match segment_idx { - 0 => Some(self.resolve_self(&mut ctxt, parent_scope.module)), - _ => match module { - Some(ModuleOrUniformRoot::Module(module)) => Some(module), - _ => None, - }, + let parent = if segment_idx == 0 { + self.resolve_super_in_module(ident, None, parent_scope) + } else if let Some(ModuleOrUniformRoot::Module(module)) = module { + self.resolve_super_in_module(ident, Some(module), parent_scope) + } else { + None }; - if let Some(self_module) = self_module - && let Some(parent) = self_module.parent - { - module = - Some(ModuleOrUniformRoot::Module(self.resolve_self(&mut ctxt, parent))); + if let Some(parent) = parent { + module = Some(ModuleOrUniformRoot::Module(parent)); continue; } return PathResult::failed( @@ -1775,7 +1818,13 @@ fn record_segment_res<'r, 'ra, 'tcx>( finalize.is_some(), module_had_parse_errors, module, - || ("there are too many leading `super` keywords".to_string(), None), + || { + ( + "too many leading `super` keywords".to_string(), + "there are too many leading `super` keywords".to_string(), + None, + ) + }, ); } if segment_idx == 0 { @@ -1823,16 +1872,24 @@ fn record_segment_res<'r, 'ra, 'tcx>( module, || { let name_str = if name == kw::PathRoot { - "crate root".to_string() + "the crate root".to_string() } else { format!("`{name}`") }; - let label = if segment_idx == 1 && path[0].ident.name == kw::PathRoot { - format!("global paths cannot start with {name_str}") + let (message, label) = if segment_idx == 1 + && path[0].ident.name == kw::PathRoot + { + ( + format!("global paths cannot start with {name_str}"), + "cannot start with this".to_string(), + ) } else { - format!("{name_str} in paths can only be used in start position") + ( + format!("{name_str} in paths can only be used in start position"), + "can only be used in path start position".to_string(), + ) }; - (label, None) + (message, label, None) }, ); } @@ -1948,7 +2005,20 @@ fn record_segment_res<'r, 'ra, 'tcx>( res.article(), res.descr() ); - (label, None) + let scope = match &path[..segment_idx] { + [.., prev] => { + if prev.ident.name == kw::PathRoot { + format!("the crate root") + } else { + format!("`{}`", prev.ident) + } + } + _ => format!("this scope"), + }; + // FIXME: reword, as the reason we expected a module is because of + // the following path segment. + let message = format!("cannot find module `{ident}` in {scope}"); + (message, label, None) }, ); } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 4e7622d08462..7696b4b220d6 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -70,7 +70,7 @@ pub(crate) enum ImportKind<'ra> { decls: PerNS>>, /// `true` for `...::{self [as target]}` imports, `false` otherwise. type_ns_only: bool, - /// Did this import result from a nested import? ie. `use foo::{bar, baz};` + /// Did this import result from a nested import? i.e. `use foo::{bar, baz};` nested: bool, /// The ID of the `UseTree` that imported this `Import`. /// @@ -371,6 +371,8 @@ fn select_glob_decl( // - A glob decl is overwritten by its clone after setting ambiguity in it. // FIXME: avoid this by removing `warn_ambiguity`, or by triggering glob re-fetch // with the same decl in some way. + // - A glob decl is overwritten by a glob decl with larger visibility. + // FIXME: avoid this by updating this visibility in place. // - A glob decl is overwritten by a glob decl re-fetching an // overwritten decl from other module (the recursive case). // Here we are detecting all such re-fetches and overwrite old decls @@ -384,7 +386,8 @@ fn select_glob_decl( // FIXME: reenable the asserts when `warn_ambiguity` is removed (#149195). // assert_ne!(old_deep_decl, deep_decl); // assert!(old_deep_decl.is_glob_import()); - assert!(!deep_decl.is_glob_import()); + // FIXME: reenable the assert when visibility is updated in place. + // assert!(!deep_decl.is_glob_import()); if old_glob_decl.ambiguity.get().is_some() && glob_decl.ambiguity.get().is_none() { // Do not lose glob ambiguities when re-fetching the glob. glob_decl.ambiguity.set_unchecked(old_glob_decl.ambiguity.get()); @@ -1042,16 +1045,20 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option { if no_ambiguity { - assert!(import.imported_module.get().is_none()); + if !self.issue_145575_hack_applied { + assert!(import.imported_module.get().is_none()); + } self.report_error( span, ResolutionError::FailedToResolve { - segment: Some(segment_name), + segment: segment_name, label, suggestion, module, + message, }, ); } @@ -1067,7 +1074,9 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option { if no_ambiguity { - assert!(import.imported_module.get().is_none()); + if !self.issue_145575_hack_applied { + assert!(import.imported_module.get().is_none()); + } let module = if let Some(ModuleOrUniformRoot::Module(m)) = module { m.opt_def_id() } else { @@ -1277,6 +1286,9 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option bool { res, Res::Def( DefKind::Ctor(_, CtorKind::Const | CtorKind::Fn) - | DefKind::Const + | DefKind::Const { .. } | DefKind::Static { .. } | DefKind::Fn | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } | DefKind::ConstParam, _, ) | Res::Local(..) @@ -589,7 +589,10 @@ pub(crate) fn is_expected(self, res: Res) -> bool { ), PathSource::Pat => { res.expected_in_unit_struct_pat() - || matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _)) + || matches!( + res, + Res::Def(DefKind::Const { .. } | DefKind::AssocConst { .. }, _) + ) } PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(), PathSource::Struct(_) => matches!( @@ -605,7 +608,7 @@ pub(crate) fn is_expected(self, res: Res) -> bool { | Res::SelfTyAlias { .. } ), PathSource::TraitItem(ns, _) => match res { - Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) if ns == ValueNS => true, + Res::Def(DefKind::AssocConst { .. } | DefKind::AssocFn, _) if ns == ValueNS => true, Res::Def(DefKind::AssocTy, _) if ns == TypeNS => true, _ => false, }, @@ -2045,7 +2048,7 @@ fn visit_lifetime( if let Some(ty) = &self.diag_metadata.current_self_type && let ControlFlow::Break(sp) = AnonRefFinder.visit_ty(ty) { - err.multipart_suggestion_verbose( + err.multipart_suggestion( "add a lifetime to the impl block and use it in the self type and associated \ type", vec![ @@ -2060,7 +2063,7 @@ fn visit_lifetime( && let Some(of_trait) = &impl_.of_trait && let ControlFlow::Break(sp) = AnonRefFinder.visit_trait_ref(&of_trait.trait_ref) { - err.multipart_suggestion_verbose( + err.multipart_suggestion( "add a lifetime to the impl block and use it in the trait and associated type", vec![ (span, "<'a>".to_string()), @@ -3739,7 +3742,7 @@ fn check_trait_item( match (def_kind, kind) { (DefKind::AssocTy, AssocItemKind::Type(..)) | (DefKind::AssocFn, AssocItemKind::Fn(..)) - | (DefKind::AssocConst, AssocItemKind::Const(..)) + | (DefKind::AssocConst { .. }, AssocItemKind::Const(..)) | (DefKind::AssocFn, AssocItemKind::Delegation(..)) => { self.r.record_partial_res(id, PartialRes::new(res)); return; @@ -4321,7 +4324,7 @@ fn try_resolve_as_non_binding( match res { Res::SelfCtor(_) // See #70549. | Res::Def( - DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst | DefKind::ConstParam, + DefKind::Ctor(_, CtorKind::Const) | DefKind::Const { .. } | DefKind::AssocConst { .. } | DefKind::ConstParam, _, ) if is_syntactic_ambiguity => { // Disambiguate in favor of a unit struct/variant or constant pattern. @@ -4330,7 +4333,7 @@ fn try_resolve_as_non_binding( } Some(res) } - Res::Def(DefKind::Ctor(..) | DefKind::Const | DefKind::AssocConst | DefKind::Static { .. }, _) => { + Res::Def(DefKind::Ctor(..) | DefKind::Const { .. } | DefKind::AssocConst { .. } | DefKind::Static { .. }, _) => { // This is unambiguously a fresh binding, either syntactically // (e.g., `IDENT @ PAT` or `ref IDENT`) or because `IDENT` resolves // to something unusable as a pattern (e.g., constructor function), @@ -4351,7 +4354,7 @@ fn try_resolve_as_non_binding( None } Res::Def(DefKind::ConstParam, def_id) => { - // Same as for DefKind::Const above, but here, `binding` is `None`, so we + // Same as for DefKind::Const { .. } above, but here, `binding` is `None`, so we // have to construct the error differently self.report_error( ident.span, @@ -4890,14 +4893,16 @@ fn resolve_qpath( module, segment_name, error_implied_by_parse_error: _, + message, } => { return Err(respan( span, ResolutionError::FailedToResolve { - segment: Some(segment_name), + segment: segment_name, label, suggestion, module, + message, }, )); } @@ -5061,7 +5066,7 @@ fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) { match &se.rest { StructRest::Base(expr) => self.visit_expr(expr), StructRest::Rest(_span) => {} - StructRest::None => {} + StructRest::None | StructRest::NoneWithError(_) => {} } } @@ -5253,7 +5258,7 @@ fn record_candidate_traits_for_expr_if_necessary(&mut self, expr: &'ast Expr) { } } - fn traits_in_scope(&mut self, ident: Ident, ns: Namespace) -> Vec { + fn traits_in_scope(&mut self, ident: Ident, ns: Namespace) -> &'tcx [TraitCandidate<'tcx>] { self.r.traits_in_scope( self.current_trait_ref.as_ref().map(|(module, _)| *module), &self.parent_scope, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 94f8444db142..95a6b25d54e6 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -17,7 +17,6 @@ struct_span_code_err, }; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, MacroKinds}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; @@ -415,7 +414,10 @@ fn make_base_error( .is_ok_and(|snippet| snippet.ends_with(')')) } Res::Def( - DefKind::Ctor(..) | DefKind::AssocFn | DefKind::Const | DefKind::AssocConst, + DefKind::Ctor(..) + | DefKind::AssocFn + | DefKind::Const { .. } + | DefKind::AssocConst { .. }, _, ) | Res::SelfCtor(_) @@ -1115,8 +1117,7 @@ fn lookup_doc_alias_name(&mut self, path: &[Segment], ns: Namespace) -> Option<( // confused by them. continue; } - if let Some(d) = - hir::find_attr!(r.tcx.get_all_attrs(did), AttributeKind::Doc(d) => d) + if let Some(d) = hir::find_attr!(r.tcx, did, Doc(d) => d) && d.aliases.contains_key(&item_name) { return Some(did); @@ -2218,7 +2219,7 @@ fn smart_resolve_context_dependent_help( "}".to_owned(), )); - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!("use struct {descr} syntax instead of calling"), parts, applicability, @@ -2361,7 +2362,7 @@ fn smart_resolve_context_dependent_help( .tcx .associated_item_def_ids(def_id) .iter() - .map(|field_id| self.r.tcx.visibility(field_id)) + .map(|&field_id| self.r.tcx.visibility(field_id)) .collect(); (ctor_res, ctor_vis, field_visibilities) }) @@ -2430,7 +2431,7 @@ fn smart_resolve_context_dependent_help( if non_visible_spans.len() > 0 { if let Some(fields) = self.r.field_visibility_spans.get(&def_id) { - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!( "consider making the field{} publicly accessible", pluralize!(fields.len()) @@ -2596,7 +2597,7 @@ fn suggest_alternative_construction_methods( .tcx .inherent_impls(def_id) .iter() - .flat_map(|i| self.r.tcx.associated_items(i).in_definition_order()) + .flat_map(|&i| self.r.tcx.associated_items(i).in_definition_order()) // Only assoc fn with no receivers. .filter(|item| item.is_fn() && !item.is_method()) .filter_map(|item| { @@ -2665,12 +2666,7 @@ fn suggest_alternative_construction_methods( ) .iter() .filter_map(|candidate| candidate.did) - .find(|did| { - find_attr!( - self.r.tcx.get_all_attrs(*did), - AttributeKind::RustcDiagnosticItem(sym::Default) - ) - }); + .find(|did| find_attr!(self.r.tcx, *did, RustcDiagnosticItem(sym::Default))); let Some(default_trait) = default_trait else { return; }; @@ -2709,7 +2705,7 @@ fn has_private_fields(&self, def_id: DefId) -> bool { .tcx .associated_item_def_ids(def_id) .iter() - .map(|field_id| self.r.tcx.visibility(field_id)) + .map(|&field_id| self.r.tcx.visibility(field_id)) .collect(), ), }; @@ -2739,7 +2735,7 @@ pub(crate) fn find_similarly_named_assoc_item( .iter() .filter_map(|(key, res)| res.borrow().best_decl().map(|binding| (key, binding.res()))) .filter(|(_, res)| match (kind, res) { - (AssocItemKind::Const(..), Res::Def(DefKind::AssocConst, _)) => true, + (AssocItemKind::Const(..), Res::Def(DefKind::AssocConst { .. }, _)) => true, (AssocItemKind::Fn(_), Res::Def(DefKind::AssocFn, _)) => true, (AssocItemKind::Type(..), Res::Def(DefKind::AssocTy, _)) => true, (AssocItemKind::Delegation(_), Res::Def(DefKind::AssocFn, _)) => true, @@ -2847,7 +2843,7 @@ fn extract_node_id(t: &Ty) -> Option { return Some(AssocSuggestion::AssocFn { called }); } } - Res::Def(DefKind::AssocConst, _) => { + Res::Def(DefKind::AssocConst { .. }, _) => { return Some(AssocSuggestion::AssocConst); } Res::Def(DefKind::AssocTy, _) => { @@ -3529,7 +3525,7 @@ pub(crate) fn emit_undeclared_lifetime_error( &mut err, Some(lifetime_ref.ident), |err, _, span, message, suggestion, span_suggs| { - err.multipart_suggestion_verbose( + err.multipart_suggestion( message, std::iter::once((span, suggestion)).chain(span_suggs).collect(), Applicability::MaybeIncorrect, @@ -3733,6 +3729,8 @@ pub(crate) fn emit_forbidden_non_static_lifetime_error( name: lifetime_ref.ident.name, param_kind: errors::ParamKindInNonTrivialAnonConst::Lifetime, help: self.r.tcx.sess.is_nightly_build(), + is_ogca: self.r.tcx.features().opaque_generic_const_args(), + help_ogca: self.r.tcx.features().opaque_generic_const_args(), }) .emit() } @@ -3912,7 +3910,7 @@ fn add_missing_lifetime_specifiers_label<'a>( err, None, |err, higher_ranked, span, message, intro_sugg, _| { - err.multipart_suggestion_verbose( + err.multipart_suggestion( message, std::iter::once((span, intro_sugg)) .chain(spans_suggs.clone()) @@ -3941,7 +3939,7 @@ fn add_missing_lifetime_specifiers_label<'a>( } else { String::new() }; - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!("consider using the `{existing_name}` lifetime{post}"), spans_suggs, Applicability::MaybeIncorrect, @@ -3990,7 +3988,7 @@ fn add_missing_lifetime_specifiers_label<'a>( }; let dotdotdot = if lt.kind == MissingLifetimeKind::Ampersand { "..." } else { "" }; - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!( "instead, you are more likely to want to change {the} \ argument{s} to be borrowed{dotdotdot}", @@ -4045,7 +4043,7 @@ fn add_missing_lifetime_specifiers_label<'a>( err, None, |err, higher_ranked, span, message, intro_sugg, _| { - err.multipart_suggestion_verbose( + err.multipart_suggestion( message, std::iter::once((span, intro_sugg)) .chain(spans_suggs.clone()) @@ -4060,25 +4058,32 @@ fn add_missing_lifetime_specifiers_label<'a>( "instead, you are more likely to want" }; let mut owned_sugg = lt.kind == MissingLifetimeKind::Ampersand; + let mut sugg_is_str_to_string = false; let mut sugg = vec![(lt.span, String::new())]; if let Some((kind, _span)) = self.diag_metadata.current_function && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = kind - && let ast::FnRetTy::Ty(ty) = &sig.decl.output { let mut lt_finder = LifetimeFinder { lifetime: lt.span, found: None, seen: vec![] }; - lt_finder.visit_ty(&ty); - - if let [Ty { span, kind: TyKind::Ref(_, mut_ty), .. }] = - <_finder.seen[..] - { - // We might have a situation like - // fn g(mut x: impl Iterator) -> Option<&'_ ()> - // but `lt.span` only points at `'_`, so to suggest `-> Option<()>` - // we need to find a more accurate span to end up with - // fn g<'a>(mut x: impl Iterator) -> Option<()> - sugg = vec![(span.with_hi(mut_ty.ty.span.lo()), String::new())]; - owned_sugg = true; + for param in &sig.decl.inputs { + lt_finder.visit_ty(¶m.ty); + } + if let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output { + lt_finder.visit_ty(ret_ty); + let mut ret_lt_finder = + LifetimeFinder { lifetime: lt.span, found: None, seen: vec![] }; + ret_lt_finder.visit_ty(ret_ty); + if let [Ty { span, kind: TyKind::Ref(_, mut_ty), .. }] = + &ret_lt_finder.seen[..] + { + // We might have a situation like + // fn g(mut x: impl Iterator) -> Option<&'_ ()> + // but `lt.span` only points at `'_`, so to suggest `-> Option<()>` + // we need to find a more accurate span to end up with + // fn g<'a>(mut x: impl Iterator) -> Option<()> + sugg = vec![(span.with_hi(mut_ty.ty.span.lo()), String::new())]; + owned_sugg = true; + } } if let Some(ty) = lt_finder.found { if let TyKind::Path(None, path) = &ty.kind { @@ -4098,6 +4103,7 @@ fn add_missing_lifetime_specifiers_label<'a>( lt.span.with_hi(ty.span.hi()), "String".to_string(), )]; + sugg_is_str_to_string = true; } Some(Res::PrimTy(..)) => {} Some(Res::Def( @@ -4124,6 +4130,7 @@ fn add_missing_lifetime_specifiers_label<'a>( lt.span.with_hi(ty.span.hi()), "String".to_string(), )]; + sugg_is_str_to_string = true; } Res::PrimTy(..) => {} Res::Def( @@ -4158,7 +4165,13 @@ fn add_missing_lifetime_specifiers_label<'a>( } } if owned_sugg { - err.multipart_suggestion_verbose( + if let Some(span) = + self.find_ref_prefix_span_for_owned_suggestion(lt.span) + && !sugg_is_str_to_string + { + sugg = vec![(span, String::new())]; + } + err.multipart_suggestion( format!("{pre} to return an owned value"), sugg, Applicability::MaybeIncorrect, @@ -4175,7 +4188,7 @@ fn add_missing_lifetime_specifiers_label<'a>( if spans_suggs.len() > 0 { // This happens when we have `Foo` where we point at the space before `T`, // but this can be confusing so we give a suggestion with placeholders. - err.multipart_suggestion_verbose( + err.multipart_suggestion( "consider using one of the available lifetimes here", spans_suggs, Applicability::HasPlaceholders, @@ -4184,6 +4197,23 @@ fn add_missing_lifetime_specifiers_label<'a>( } } } + + fn find_ref_prefix_span_for_owned_suggestion(&self, lifetime: Span) -> Option { + let mut finder = RefPrefixSpanFinder { lifetime, span: None }; + if let Some(item) = self.diag_metadata.current_item { + finder.visit_item(item); + } else if let Some((kind, _span)) = self.diag_metadata.current_function + && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = kind + { + for param in &sig.decl.inputs { + finder.visit_ty(¶m.ty); + } + if let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output { + finder.visit_ty(ret_ty); + } + } + finder.span + } } fn mk_where_bound_predicate( @@ -4285,6 +4315,26 @@ fn visit_ty(&mut self, t: &'ast Ty) { } } +struct RefPrefixSpanFinder { + lifetime: Span, + span: Option, +} + +impl<'ast> Visitor<'ast> for RefPrefixSpanFinder { + fn visit_ty(&mut self, t: &'ast Ty) { + if self.span.is_some() { + return; + } + if let TyKind::Ref(_, mut_ty) | TyKind::PinnedRef(_, mut_ty) = &t.kind + && t.span.lo() == self.lifetime.lo() + { + self.span = Some(t.span.with_hi(mut_ty.ty.span.lo())); + return; + } + walk_ty(self, t); + } +} + /// Shadowing involving a label is only a warning for historical reasons. //FIXME: make this a proper lint. pub(super) fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index ff1c30458a98..ead69473b00d 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -8,15 +8,12 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![cfg_attr(bootstrap, feature(assert_matches))] -#![cfg_attr(bootstrap, feature(ptr_as_ref_unchecked))] #![feature(arbitrary_self_types)] #![feature(box_patterns)] #![feature(const_default)] #![feature(const_trait_impl)] #![feature(control_flow_into_value)] #![feature(default_field_values)] -#![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(rustc_attrs)] #![feature(trim_prefix_suffix)] @@ -25,7 +22,7 @@ use std::cell::Ref; use std::collections::BTreeSet; -use std::fmt::{self}; +use std::fmt; use std::ops::ControlFlow; use std::sync::Arc; @@ -53,7 +50,7 @@ use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed, LintBuffer}; use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind}; use rustc_feature::BUILTIN_ATTRIBUTES; -use rustc_hir::attrs::{AttributeKind, StrippedCfgItem}; +use rustc_hir::attrs::StrippedCfgItem; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{ self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, MacroKinds, NonMacroAttrKind, PartialRes, @@ -274,16 +271,13 @@ enum ResolutionError<'ra> { UndeclaredLabel { name: Symbol, suggestion: Option }, /// Error E0429: `self` imports are only allowed within a `{ }` list. SelfImportsOnlyAllowedWithin { root: bool, span_with_rename: Span }, - /// Error E0430: `self` import can only appear once in the list. - SelfImportCanOnlyAppearOnceInTheList, - /// Error E0431: `self` import can only appear in an import list with a non-empty prefix. - SelfImportOnlyInImportListWithNonEmptyPrefix, /// Error E0433: failed to resolve. FailedToResolve { - segment: Option, + segment: Symbol, label: String, suggestion: Option, module: Option>, + message: String, }, /// Error E0434: can't capture dynamic environment in a fn item. CannotCaptureDynamicEnvironmentInFnItem, @@ -312,7 +306,11 @@ enum ResolutionError<'ra> { /// generic parameters must not be used inside const evaluations. /// /// This error is only emitted when using `min_const_generics`. - ParamInNonTrivialAnonConst { name: Symbol, param_kind: ParamKindInNonTrivialAnonConst }, + ParamInNonTrivialAnonConst { + is_ogca: bool, + name: Symbol, + param_kind: ParamKindInNonTrivialAnonConst, + }, /// generic parameters must not be used inside enum discriminants. /// /// This error is emitted even with `generic_const_exprs`. @@ -342,7 +340,7 @@ enum ResolutionError<'ra> { enum VisResolutionError<'a> { Relative2018(Span, &'a ast::Path), AncestorOnly(Span), - FailedToResolve(Span, String, Option), + FailedToResolve(Span, Symbol, String, Option, String), ExpectedFound(Span, String, Res), Indeterminate(Span), ModuleOnly(Span), @@ -377,16 +375,6 @@ fn from_ident(ident: Ident) -> Segment { } } - fn from_ident_and_id(ident: Ident, id: NodeId) -> Segment { - Segment { - ident, - id: Some(id), - has_generic_args: false, - has_lifetime_args: false, - args_span: DUMMY_SP, - } - } - fn names_to_string(segments: &[Segment]) -> String { names_to_string(segments.iter().map(|seg| seg.ident.name)) } @@ -486,6 +474,7 @@ enum PathResult<'ra> { /// The segment name of target segment_name: Symbol, error_implied_by_parse_error: bool, + message: String, }, } @@ -496,10 +485,14 @@ fn failed( finalize: bool, error_implied_by_parse_error: bool, module: Option>, - label_and_suggestion: impl FnOnce() -> (String, Option), + label_and_suggestion: impl FnOnce() -> (String, String, Option), ) -> PathResult<'ra> { - let (label, suggestion) = - if finalize { label_and_suggestion() } else { (String::new(), None) }; + let (message, label, suggestion) = if finalize { + label_and_suggestion() + } else { + // FIXME: this output isn't actually present in the test suite. + (format!("cannot find `{ident}` in this scope"), String::new(), None) + }; PathResult::Failed { span: ident.span, segment_name: ident.name, @@ -508,6 +501,7 @@ fn failed( is_error_from_last_segment, module, error_implied_by_parse_error, + message, } } } @@ -1058,7 +1052,10 @@ fn is_glob_import(&self) -> bool { } fn is_assoc_item(&self) -> bool { - matches!(self.res(), Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _)) + matches!( + self.res(), + Res::Def(DefKind::AssocConst { .. } | DefKind::AssocFn | DefKind::AssocTy, _) + ) } fn macro_kinds(&self) -> Option { @@ -1155,9 +1152,9 @@ fn new(ext: Arc) -> MacroData { } } -pub struct ResolverOutputs { +pub struct ResolverOutputs<'tcx> { pub global_ctxt: ResolverGlobalCtxt, - pub ast_lowering: ResolverAstLowering, + pub ast_lowering: ResolverAstLowering<'tcx>, } /// The main resolver class. @@ -1212,7 +1209,7 @@ pub struct Resolver<'ra, 'tcx> { extern_crate_map: UnordMap = Default::default(), module_children: LocalDefIdMap> = Default::default(), ambig_module_children: LocalDefIdMap> = Default::default(), - trait_map: NodeMap> = Default::default(), + trait_map: NodeMap<&'tcx [TraitCandidate<'tcx>]> = Default::default(), /// A map from nodes to anonymous modules. /// Anonymous modules are pseudo-modules that are implicitly created around items @@ -1806,7 +1803,7 @@ fn feed_visibility(&mut self, feed: Feed<'tcx, LocalDefId>, vis: Visibility) { self.visibilities_for_hashing.push((feed.def_id(), vis)); } - pub fn into_outputs(self) -> ResolverOutputs { + pub fn into_outputs(self) -> ResolverOutputs<'tcx> { let proc_macros = self.proc_macros; let expn_that_defined = self.expn_that_defined; let extern_crate_map = self.extern_crate_map; @@ -1952,7 +1949,7 @@ fn traits_in_scope( parent_scope: &ParentScope<'ra>, sp: Span, assoc_item: Option<(Symbol, Namespace)>, - ) -> Vec { + ) -> &'tcx [TraitCandidate<'tcx>] { let mut found_traits = Vec::new(); if let Some(module) = current_trait { @@ -1960,7 +1957,7 @@ fn traits_in_scope( let def_id = module.def_id(); found_traits.push(TraitCandidate { def_id, - import_ids: smallvec![], + import_ids: &[], lint_ambiguous: false, }); } @@ -1990,14 +1987,14 @@ fn traits_in_scope( ControlFlow::<()>::Continue(()) }); - found_traits + self.tcx.hir_arena.alloc_slice(&found_traits) } fn traits_in_module( &mut self, module: Module<'ra>, assoc_item: Option<(Symbol, Namespace)>, - found_traits: &mut Vec, + found_traits: &mut Vec>, ) { module.ensure_traits(self); let traits = module.traits.borrow(); @@ -2036,8 +2033,8 @@ fn find_transitive_imports( &mut self, mut kind: &DeclKind<'_>, trait_name: Symbol, - ) -> SmallVec<[LocalDefId; 1]> { - let mut import_ids = smallvec![]; + ) -> &'tcx [LocalDefId] { + let mut import_ids: SmallVec<[LocalDefId; 1]> = smallvec![]; while let DeclKind::Import { import, source_decl, .. } = kind { if let Some(node_id) = import.id() { let def_id = self.local_def_id(node_id); @@ -2047,7 +2044,8 @@ fn find_transitive_imports( self.add_to_glob_map(*import, trait_name); kind = &source_decl.kind; } - import_ids + + self.tcx.hir_arena.alloc_slice(&import_ids) } fn resolutions(&self, module: Module<'ra>) -> &'ra Resolutions<'ra> { @@ -2468,8 +2466,8 @@ fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option> { find_attr!( // we can use parsed attrs here since for other crates they're already available - self.tcx.get_all_attrs(def_id), - AttributeKind::RustcLegacyConstGenerics{fn_indexes,..} => fn_indexes + self.tcx, def_id, + RustcLegacyConstGenerics{fn_indexes,..} => fn_indexes ) .map(|fn_indexes| fn_indexes.iter().map(|(num, _)| *num).collect()) } @@ -2507,7 +2505,7 @@ fn resolve_main(&mut self) { fn names_to_string(names: impl Iterator) -> String { let mut result = String::new(); - for (i, name) in names.filter(|name| *name != kw::PathRoot).enumerate() { + for (i, name) in names.enumerate().filter(|(_, name)| *name != kw::PathRoot) { if i > 0 { result.push_str("::"); } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index e0973271da52..551d89ee6022 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -4,8 +4,9 @@ use std::mem; use std::sync::Arc; -use rustc_ast::{self as ast, Crate, NodeId, attr}; +use rustc_ast::{self as ast, Crate, DUMMY_NODE_ID, NodeId}; use rustc_ast_pretty::pprust; +use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, DiagCtxtHandle, StashKey}; use rustc_expand::base::{ Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, @@ -15,12 +16,14 @@ use rustc_expand::expand::{ AstFragment, AstFragmentKind, Invocation, InvocationKind, SupportsMacroExpansion, }; -use rustc_hir::StabilityLevel; -use rustc_hir::attrs::{CfgEntry, StrippedCfgItem}; +use rustc_feature::Features; +use rustc_hir::attrs::{AttributeKind, CfgEntry, StrippedCfgItem}; use rustc_hir::def::{self, DefKind, MacroKinds, Namespace, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; +use rustc_hir::{Attribute, StabilityLevel}; use rustc_middle::middle::stability; use rustc_middle::ty::{RegisteredTools, TyCtxt}; +use rustc_session::Session; use rustc_session::lint::builtin::{ LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, UNKNOWN_DIAGNOSTIC_ATTRIBUTES, UNUSED_MACRO_RULES, UNUSED_MACROS, @@ -122,35 +125,38 @@ fn fast_print_path(path: &ast::Path) -> Symbol { pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools { let (_, pre_configured_attrs) = &*tcx.crate_for_resolver(()).borrow(); - registered_tools_ast(tcx.dcx(), pre_configured_attrs) + registered_tools_ast(tcx.dcx(), pre_configured_attrs, tcx.sess, tcx.features()) } pub fn registered_tools_ast( dcx: DiagCtxtHandle<'_>, pre_configured_attrs: &[ast::Attribute], + sess: &Session, + features: &Features, ) -> RegisteredTools { let mut registered_tools = RegisteredTools::default(); - for attr in attr::filter_by_name(pre_configured_attrs, sym::register_tool) { - for meta_item_inner in attr.meta_item_list().unwrap_or_default() { - match meta_item_inner.ident() { - Some(ident) => { - if let Some(old_ident) = registered_tools.replace(ident) { - dcx.emit_err(errors::ToolWasAlreadyRegistered { - span: ident.span, - tool: ident, - old_ident_span: old_ident.span, - }); - } - } - None => { - dcx.emit_err(errors::ToolOnlyAcceptsIdentifiers { - span: meta_item_inner.span(), - tool: sym::register_tool, - }); - } + + if let Some(Attribute::Parsed(AttributeKind::RegisterTool(tools, _))) = + AttributeParser::parse_limited( + sess, + pre_configured_attrs, + sym::register_tool, + DUMMY_SP, + DUMMY_NODE_ID, + Some(features), + ) + { + for tool in tools { + if let Some(old_tool) = registered_tools.replace(tool) { + dcx.emit_err(errors::ToolWasAlreadyRegistered { + span: tool.span, + tool, + old_ident_span: old_tool.span, + }); } } } + // We implicitly add `rustfmt`, `clippy`, `diagnostic`, `miri` and `rust_analyzer` to known // tools, but it's not an error to register them explicitly. let predefined_tools = @@ -908,10 +914,10 @@ pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) { ), path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => { let mut suggestion = None; - let (span, label, module, segment) = - if let PathResult::Failed { span, label, module, segment_name, .. } = - path_res - { + let (span, message, label, module, segment) = match path_res { + PathResult::Failed { + span, label, module, segment_name, message, .. + } => { // try to suggest if it's not a macro, maybe a function if let PathResult::NonModule(partial_res) = self .cm() @@ -930,26 +936,52 @@ pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) { Applicability::MaybeIncorrect, )); } - (span, label, module, segment_name) - } else { + (span, message, label, module, segment_name) + } + PathResult::NonModule(partial_res) => { + let found_an = partial_res.base_res().article(); + let found_descr = partial_res.base_res().descr(); + let scope = match &path[..partial_res.unresolved_segments()] { + [.., prev] => { + format!("{found_descr} `{}`", prev.ident) + } + _ => found_descr.to_string(), + }; + let expected_an = kind.article(); + let expected_descr = kind.descr(); + let expected_name = path[partial_res.unresolved_segments()].ident; + ( path_span, format!( - "partially resolved path in {} {}", - kind.article(), - kind.descr() + "cannot find {expected_descr} `{expected_name}` in {scope}" ), + match partial_res.base_res() { + Res::Def( + DefKind::Mod | DefKind::Macro(..) | DefKind::ExternCrate, + _, + ) => format!( + "partially resolved path in {expected_an} {expected_descr}", + ), + _ => format!( + "{expected_an} {expected_descr} can't exist within \ + {found_an} {found_descr}" + ), + }, None, path.last().map(|segment| segment.ident.name).unwrap(), ) - }; + } + _ => unreachable!(), + }; self.report_error( span, ResolutionError::FailedToResolve { - segment: Some(segment), + segment, label, suggestion, module, + message, }, ); } diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 9f74a7801d2e..89b561bd2e90 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -367,7 +367,7 @@ pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool { /// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]` or `#[doc(attribute)]`. pub fn has_primitive_or_keyword_or_attribute_docs(attrs: &[impl AttributeExt]) -> bool { for attr in attrs { - if attr.has_name(sym::rustc_doc_primitive) || attr.is_doc_keyword_or_attribute() { + if attr.is_rustc_doc_primitive() || attr.is_doc_keyword_or_attribute() { return true; } } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 5505fe82cea6..26979c24bdb6 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -11,7 +11,6 @@ use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::find_attr; use rustc_middle::bug; use rustc_middle::ty::layout::IntegerExt; @@ -447,8 +446,7 @@ pub(crate) fn encode_ty<'tcx>( ty::Adt(adt_def, args) => { let mut s = String::new(); let def_id = adt_def.did(); - if let Some(encoding) = find_attr!(tcx.get_all_attrs(def_id), AttributeKind::CfiEncoding { encoding } => encoding) - { + if let Some(encoding) = find_attr!(tcx, def_id, CfiEncoding { encoding } => encoding) { let encoding = encoding.as_str().trim(); // Use user-defined CFI encoding for type s.push_str(&encoding); @@ -494,8 +492,7 @@ pub(crate) fn encode_ty<'tcx>( // , where is let mut s = String::new(); - if let Some(encoding) = find_attr!(tcx.get_all_attrs(*def_id), AttributeKind::CfiEncoding {encoding} => encoding) - { + if let Some(encoding) = find_attr!(tcx, *def_id, CfiEncoding {encoding} => encoding) { // Use user-defined CFI encoding for type s.push_str(encoding.as_str().trim()); } else { diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 971ac9348fc4..90b489258360 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -6,7 +6,6 @@ use std::iter; -use rustc_hir::attrs::AttributeKind; use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_middle::bug; use rustc_middle::ty::{ @@ -138,10 +137,7 @@ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { { // Don't transform repr(transparent) types with an user-defined CFI encoding to // preserve the user-defined CFI encoding. - if find_attr!( - self.tcx.get_all_attrs(adt_def.did()), - AttributeKind::CfiEncoding { .. } - ) { + if find_attr!(self.tcx, adt_def.did(), CfiEncoding { .. }) { return t; } let variant = adt_def.non_enum_variant(); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b278a6179fe7..0677ca0fddba 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1615,7 +1615,7 @@ pub fn build_target_config( let mut err = early_dcx.early_struct_fatal(format!("error loading target specification: {e}")); err.help("run `rustc --print target-list` for a list of built-in targets"); - err.emit(); + err.emit() } } } diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index cd0556de5c22..ebbbe36878da 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -328,7 +328,7 @@ pub fn fill_well_known(&mut self, current_target: &Target) { return; } - // for `#[cfg(foo)]` (ie. cfg value is none) + // for `#[cfg(foo)]` (i.e. cfg value is none) let no_values = || { let mut values = FxHashSet::default(); values.insert(None); diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 04a32c20a791..cb3f7363957e 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -7,7 +7,7 @@ Diag, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, ErrorGuaranteed, Level, MultiSpan, }; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTuple}; @@ -528,7 +528,7 @@ pub(crate) struct FailedToCreateProfiler { #[note("see issue #129893 for more information")] pub(crate) struct SoftFloatDeprecated; -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unexpected `--cfg {$cfg}` flag")] #[note("config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}`")] #[note("manually setting a built-in cfg can and does create incoherent behaviors")] @@ -539,5 +539,5 @@ pub(crate) struct UnexpectedBuiltinCfg { } #[derive(Diagnostic)] -#[diag("ThinLTO is not supported by the codegen backend")] +#[diag("ThinLTO is not supported by the codegen backend, using fat LTO instead")] pub(crate) struct ThinLtoNotSupportedByBackend; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 9219b5a7e8ac..74b3aa11d0d8 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -7,7 +7,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::profiling::TimePassesFormat; use rustc_data_structures::stable_hasher::StableHasher; -use rustc_errors::{ColorConfig, LanguageIdentifier, TerminalUrl}; +use rustc_errors::{ColorConfig, TerminalUrl}; use rustc_feature::UnstableFeatures; use rustc_hashes::Hash64; use rustc_hir::attrs::CollapseMacroDebuginfo; @@ -611,7 +611,7 @@ macro_rules! options { $parse:ident, [$dep_tracking_marker:ident $( $tmod:ident )?], $desc:expr - $(, deprecated_do_nothing: $dnn:literal )?) + $(, is_deprecated_and_do_nothing: $dnn:literal )?) ),* ,) => ( #[derive(Clone)] @@ -760,7 +760,7 @@ fn build_options( match value { None => early_dcx.early_fatal( format!( - "{outputname} option `{key}` requires {type_desc} ({prefix} {key}=)" + "{outputname} option `{key}` requires {type_desc} (`-{prefix} {key}=`)" ), ), Some(value) => early_dcx.early_fatal( @@ -790,7 +790,6 @@ mod desc { pub(crate) const parse_string: &str = "a string"; pub(crate) const parse_opt_string: &str = parse_string; pub(crate) const parse_string_push: &str = parse_string; - pub(crate) const parse_opt_langid: &str = "a language identifier"; pub(crate) const parse_opt_pathbuf: &str = "a path"; pub(crate) const parse_list: &str = "a space-separated list of strings"; pub(crate) const parse_list_with_polarity: &str = @@ -998,17 +997,6 @@ pub(crate) fn parse_opt_string(slot: &mut Option, v: Option<&str>) -> bo } } - /// Parse an optional language identifier, e.g. `en-US` or `zh-CN`. - pub(crate) fn parse_opt_langid(slot: &mut Option, v: Option<&str>) -> bool { - match v { - Some(s) => { - *slot = rustc_errors::LanguageIdentifier::from_str(s).ok(); - true - } - None => false, - } - } - pub(crate) fn parse_opt_pathbuf(slot: &mut Option, v: Option<&str>) -> bool { match v { Some(s) => { @@ -2071,7 +2059,7 @@ pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { #[rustc_lint_opt_deny_field_access("documented to do nothing")] ar: String = (String::new(), parse_string, [UNTRACKED], "this option is deprecated and does nothing", - deprecated_do_nothing: true), + is_deprecated_and_do_nothing: true), #[rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field")] code_model: Option = (None, parse_code_model, [TRACKED], "choose the code model to use (`rustc --print code-models` for details)"), @@ -2110,7 +2098,7 @@ pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { inline_threshold: Option = (None, parse_opt_number, [UNTRACKED], "this option is deprecated and does nothing \ (consider using `-Cllvm-args=--inline-threshold=...`)", - deprecated_do_nothing: true), + is_deprecated_and_do_nothing: true), #[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")] instrument_coverage: InstrumentCoverage = (InstrumentCoverage::No, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage reports \ @@ -2151,7 +2139,7 @@ pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { #[rustc_lint_opt_deny_field_access("documented to do nothing")] no_stack_check: bool = (false, parse_no_value, [UNTRACKED], "this option is deprecated and does nothing", - deprecated_do_nothing: true), + is_deprecated_and_do_nothing: true), no_vectorize_loops: bool = (false, parse_no_value, [TRACKED], "disable loop vectorization optimization passes"), no_vectorize_slp: bool = (false, parse_no_value, [TRACKED], @@ -2335,8 +2323,6 @@ pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { "embed source text in DWARF debug sections (default: no)"), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], "emit a section containing stack size metadata (default: no)"), - emit_thin_lto: bool = (true, parse_bool, [TRACKED], - "emit the bc module with thin LTO info (default: yes)"), emscripten_wasm_eh: bool = (true, parse_bool, [TRACKED], "Use WebAssembly error handling for wasm32-unknown-emscripten"), enforce_type_length_limit: bool = (false, parse_bool, [TRACKED], @@ -2481,6 +2467,9 @@ pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { mir_include_spans: MirIncludeSpans = (MirIncludeSpans::default(), parse_mir_include_spans, [UNTRACKED], "include extra comments in mir pretty printing, like line numbers and statement indices, \ details about types, etc. (boolean for all passes, 'nll' to enable in NLL MIR only, default: 'nll')"), + mir_opt_bisect_limit: Option = (None, parse_opt_number, [TRACKED], + "limit the number of MIR optimization pass executions (global across all bodies). \ + Pass executions after this limit are skipped and reported. (default: no limit)"), #[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")] mir_opt_level: Option = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), @@ -2710,15 +2699,6 @@ pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { "for every macro invocation, print its name and arguments (default: no)"), track_diagnostics: bool = (false, parse_bool, [UNTRACKED], "tracks where in rustc a diagnostic was emitted"), - // Diagnostics are considered side-effects of a query (see `QuerySideEffect`) and are saved - // alongside query results and changes to translation options can affect diagnostics - so - // translation options should be tracked. - translate_additional_ftl: Option = (None, parse_opt_pathbuf, [TRACKED], - "additional fluent translation to preferentially use (for testing translation)"), - translate_directionality_markers: bool = (false, parse_bool, [TRACKED], - "emit directionality isolation markers in translated diagnostics"), - translate_lang: Option = (None, parse_opt_langid, [TRACKED], - "language identifier for diagnostic output"), translate_remapped_path_to_local_path: bool = (true, parse_bool, [TRACKED], "translate remapped paths into local paths when possible (default: yes)"), trap_unreachable: Option = (None, parse_opt_bool, [TRACKED], diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 62c23424f371..9c12c480cacd 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -10,7 +10,6 @@ use rustc_data_structures::sync::{AppendOnlyVec, Lock}; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; use rustc_errors::emitter::{EmitterWithNote, stderr_destination}; -use rustc_errors::translation::Translator; use rustc_errors::{ BufferedEarlyLint, ColorConfig, DecorateDiagCompat, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan, StashKey, @@ -281,10 +280,9 @@ pub struct ParseSess { impl ParseSess { /// Used for testing. pub fn new() -> Self { - let translator = Translator::new(); let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let emitter = Box::new( - AnnotateSnippetEmitter::new(stderr_destination(ColorConfig::Auto), translator) + AnnotateSnippetEmitter::new(stderr_destination(ColorConfig::Auto)) .sm(Some(Arc::clone(&sm))), ); let dcx = DiagCtxt::new(emitter); @@ -314,12 +312,8 @@ pub fn with_dcx(dcx: DiagCtxt, source_map: Arc) -> Self { } pub fn emitter_with_note(note: String) -> Self { - let translator = Translator::new(); let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); - let emitter = Box::new(AnnotateSnippetEmitter::new( - stderr_destination(ColorConfig::Auto), - translator, - )); + let emitter = Box::new(AnnotateSnippetEmitter::new(stderr_destination(ColorConfig::Auto))); let dcx = DiagCtxt::new(Box::new(EmitterWithNote { emitter, note })); ParseSess::with_dcx(dcx, sm) } diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs index 3e1fb4df4c3a..c9c7b8a4d782 100644 --- a/compiler/rustc_session/src/search_paths.rs +++ b/compiler/rustc_session/src/search_paths.rs @@ -16,7 +16,7 @@ pub struct SearchPath { /// [FilesIndex] contains paths that can be efficiently looked up with (prefix, suffix) pairs. #[derive(Clone, Debug)] -pub struct FilesIndex(Vec<(Arc, SearchPathFile)>); +pub struct FilesIndex(Vec); impl FilesIndex { /// Look up [SearchPathFile] by (prefix, suffix) pair. @@ -25,15 +25,15 @@ pub fn query<'s>( prefix: &str, suffix: &str, ) -> Option> { - let start = self.0.partition_point(|(k, _)| **k < *prefix); + let start = self.0.partition_point(|v| *v.file_name_str < *prefix); if start == self.0.len() { return None; } - let end = self.0[start..].partition_point(|(k, _)| k.starts_with(prefix)); + let end = self.0[start..].partition_point(|v| v.file_name_str.starts_with(prefix)); let prefixed_items = &self.0[start..][..end]; - let ret = prefixed_items.into_iter().filter_map(move |(k, v)| { - k.ends_with(suffix).then(|| { + let ret = prefixed_items.into_iter().filter_map(move |v| { + v.file_name_str.ends_with(suffix).then(|| { ( String::from( &v.file_name_str[prefix.len()..v.file_name_str.len() - suffix.len()], @@ -45,7 +45,7 @@ pub fn query<'s>( Some(ret) } pub fn retain(&mut self, prefixes: &[&str]) { - self.0.retain(|(k, _)| prefixes.iter().any(|prefix| k.starts_with(prefix))); + self.0.retain(|v| prefixes.iter().any(|prefix| v.file_name_str.starts_with(prefix))); } } /// The obvious implementation of `SearchPath::files` is a `Vec`. But @@ -61,8 +61,14 @@ pub fn retain(&mut self, prefixes: &[&str]) { /// UTF-8, and so a non-UTF-8 filename couldn't be one we're looking for.) #[derive(Clone, Debug)] pub struct SearchPathFile { - pub path: Arc, - pub file_name_str: Arc, + file_name_str: Arc, +} + +impl SearchPathFile { + /// Constructs the full path to the file. + pub fn path(&self, dir: &Path) -> PathBuf { + dir.join(&*self.file_name_str) + } } #[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, Encodable, Decodable, HashStable_Generic)] @@ -134,20 +140,14 @@ pub fn new(kind: PathKind, dir: PathBuf) -> Self { Ok(files) => files .filter_map(|e| { e.ok().and_then(|e| { - e.file_name().to_str().map(|s| { - let file_name_str: Arc = s.into(); - ( - Arc::clone(&file_name_str), - SearchPathFile { path: e.path().into(), file_name_str }, - ) - }) + e.file_name().to_str().map(|s| SearchPathFile { file_name_str: s.into() }) }) }) - .collect::>(), + .collect::>(), Err(..) => Default::default(), }; - files.sort_by(|(lhs, _), (rhs, _)| lhs.cmp(rhs)); + files.sort_unstable_by(|lhs, rhs| lhs.file_name_str.cmp(&rhs.file_name_str)); let files = FilesIndex(files); SearchPath { kind, dir, files } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index bb22e4a8db04..0548380331be 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; -use std::sync::atomic::AtomicBool; +use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::{env, io}; use rand::{RngCore, rng}; @@ -16,7 +16,6 @@ use rustc_errors::emitter::{DynEmitter, HumanReadableErrorType, OutputTheme, stderr_destination}; use rustc_errors::json::JsonEmitter; use rustc_errors::timings::TimingSectionHandler; -use rustc_errors::translation::Translator; use rustc_errors::{ Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort, TerminalUrl, @@ -161,6 +160,17 @@ pub struct Session { /// Does the codegen backend support ThinLTO? pub thin_lto_supported: bool, + + /// Global per-session counter for MIR optimization pass applications. + /// + /// Used by `-Zmir-opt-bisect-limit` to assign an index to each + /// optimization-pass execution candidate during this compilation. + pub mir_opt_bisect_eval_count: AtomicUsize, + + /// Enabled features that are used in the current compilation. + /// + /// The value is the `DepNodeIndex` of the node encodes the used feature. + pub used_features: Lock>, } #[derive(Clone, Copy)] @@ -610,9 +620,9 @@ pub fn lto(&self) -> config::Lto { config::LtoCli::Thin => { // The user explicitly asked for ThinLTO if !self.thin_lto_supported { - // Backend doesn't support ThinLTO, disable LTO. + // Backend doesn't support ThinLTO, fallback to fat LTO. self.dcx().emit_warn(errors::ThinLtoNotSupportedByBackend); - return config::Lto::No; + return config::Lto::Fat; } return config::Lto::Thin; } @@ -908,11 +918,7 @@ pub fn sanitizers(&self) -> SanitizerSet { // JUSTIFICATION: part of session construction #[allow(rustc::bad_opt_access)] -fn default_emitter( - sopts: &config::Options, - source_map: Arc, - translator: Translator, -) -> Box { +fn default_emitter(sopts: &config::Options, source_map: Arc) -> Box { let macro_backtrace = sopts.unstable_opts.macro_backtrace; let track_diagnostics = sopts.unstable_opts.track_diagnostics; let terminal_url = match sopts.unstable_opts.terminal_urls { @@ -934,21 +940,17 @@ fn default_emitter( match sopts.error_format { config::ErrorOutputType::HumanReadable { kind, color_config } => match kind { HumanReadableErrorType { short, unicode } => { - let emitter = - AnnotateSnippetEmitter::new(stderr_destination(color_config), translator) - .sm(source_map) - .short_message(short) - .diagnostic_width(sopts.diagnostic_width) - .macro_backtrace(macro_backtrace) - .track_diagnostics(track_diagnostics) - .terminal_url(terminal_url) - .theme(if unicode { OutputTheme::Unicode } else { OutputTheme::Ascii }) - .ignored_directories_in_source_blocks( - sopts - .unstable_opts - .ignore_directory_in_diagnostics_source_blocks - .clone(), - ); + let emitter = AnnotateSnippetEmitter::new(stderr_destination(color_config)) + .sm(source_map) + .short_message(short) + .diagnostic_width(sopts.diagnostic_width) + .macro_backtrace(macro_backtrace) + .track_diagnostics(track_diagnostics) + .terminal_url(terminal_url) + .theme(if unicode { OutputTheme::Unicode } else { OutputTheme::Ascii }) + .ignored_directories_in_source_blocks( + sopts.unstable_opts.ignore_directory_in_diagnostics_source_blocks.clone(), + ); Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } }, @@ -956,7 +958,6 @@ fn default_emitter( JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), source_map, - translator, pretty, json_rendered, color_config, @@ -978,7 +979,6 @@ fn default_emitter( pub fn build_session( sopts: config::Options, io: CompilerIO, - fluent_bundle: Option>, driver_lint_caps: FxHashMap, target: Target, cfg_version: &'static str, @@ -996,9 +996,8 @@ pub fn build_session( let cap_lints_allow = sopts.lint_cap.is_some_and(|cap| cap == lint::Allow); let can_emit_warnings = !(warnings_allow || cap_lints_allow); - let translator = Translator { fluent_bundle }; let source_map = rustc_span::source_map::get_source_map().unwrap(); - let emitter = default_emitter(&sopts, Arc::clone(&source_map), translator); + let emitter = default_emitter(&sopts, Arc::clone(&source_map)); let mut dcx = DiagCtxt::new(emitter).with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)); @@ -1101,6 +1100,8 @@ pub fn build_session( invocation_temp, replaced_intrinsics: FxHashSet::default(), // filled by `run_compiler` thin_lto_supported: true, // filled by `run_compiler` + mir_opt_bisect_eval_count: AtomicUsize::new(0), + used_features: Lock::default(), }; validate_commandline_args_with_session_available(&sess); @@ -1434,13 +1435,10 @@ pub fn early_struct_warn(&self, msg: impl Into) -> Diag<'_, ()> { } fn mk_emitter(output: ErrorOutputType) -> Box { - // FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will - // need to reference every crate that might emit an early error for translation to work. - let translator = Translator::new(); let emitter: Box = match output { config::ErrorOutputType::HumanReadable { kind, color_config } => match kind { HumanReadableErrorType { short, unicode } => Box::new( - AnnotateSnippetEmitter::new(stderr_destination(color_config), translator) + AnnotateSnippetEmitter::new(stderr_destination(color_config)) .theme(if unicode { OutputTheme::Unicode } else { OutputTheme::Ascii }) .short_message(short), ), @@ -1449,7 +1447,6 @@ fn mk_emitter(output: ErrorOutputType) -> Box { Box::new(JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), Some(Arc::new(SourceMap::new(FilePathMapping::empty()))), - translator, pretty, json_rendered, color_config, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 00646dc7bcf4..a1d43d986384 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -18,9 +18,7 @@ // tidy-alphabetical-start #![allow(internal_features)] #![cfg_attr(target_arch = "loongarch64", feature(stdarch_loongarch))] -#![feature(cfg_select)] #![feature(core_io_borrowed_buf)] -#![feature(if_let_guard)] #![feature(map_try_insert)] #![feature(negative_impls)] #![feature(read_buf)] @@ -2653,7 +2651,7 @@ fn sub(self, rhs: $ident) -> $ident { pub struct BytePos(pub u32); /// A byte offset relative to file beginning. - #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, HashStable_Generic)] pub struct RelativeBytePos(pub u32); /// A character offset. @@ -2677,12 +2675,6 @@ fn decode(d: &mut D) -> BytePos { } } -impl HashStable for RelativeBytePos { - fn hash_stable(&self, hcx: &mut H, hasher: &mut StableHasher) { - self.0.hash_stable(hcx, hasher); - } -} - impl Encodable for RelativeBytePos { fn encode(&self, s: &mut S) { s.emit_u32(self.0); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index bf3d28a65440..257ac3f51c2c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -157,13 +157,12 @@ Abi, AcqRel, Acquire, + Active, + ActiveOnly, Alignment, - Any, Arc, ArcWeak, - Argument, Array, - ArrayIntoIter, AsMut, AsRef, AssertParamIsClone, @@ -172,46 +171,24 @@ AsyncGenFinished, AsyncGenPending, AsyncGenReady, - AtomicBool, - AtomicI8, - AtomicI16, - AtomicI32, - AtomicI64, - AtomicI128, - AtomicIsize, - AtomicPtr, - AtomicU8, - AtomicU16, - AtomicU32, - AtomicU64, - AtomicU128, - AtomicUsize, - AutoTrait, - BTreeEntry, + Atomic, BTreeMap, - BTreeSet, - BinaryHeap, Bool, Borrow, BorrowMut, Break, BuildHasher, - C, CStr, - C_dash_unwind: "C-unwind", CallOnceFuture, CallRefFuture, Capture, Cell, - Center, Char, - Child, Cleanup, Clone, CoercePointee, CoercePointeeValidated, CoerceUnsized, - Command, Const, ConstParamTy, ConstParamTy_, @@ -219,90 +196,61 @@ Continue, ControlFlow, Copy, - Cow, Debug, - DebugStruct, - Decodable, - Decoder, Default, Deref, - DiagMessage, - Diagnostic, - DirBuilder, DispatchFromDyn, Display, - DoubleEndedIterator, - Duration, + Dual, + DualOnly, + Dualv, + DualvOnly, + Duplicated, + DuplicatedOnly, DynTrait, - Encodable, - Encoder, Enum, - Enumerate, Eq, Equal, Err, - Error, - File, - FileType, + ExternC, + ExternRust, Float, FmtArgumentsNew, - FmtWrite, Fn, FnMut, FnOnce, + FnPtr, Formatter, Forward, From, FromIterator, FromResidual, - FsOpenOptions, - FsPermissions, - FusedIterator, - Future, GlobalAlloc, Hash, HashMap, - HashMapEntry, HashSet, Hasher, - Implied, InCleanup, IndexOutput, Input, - Instant, Int, Into, IntoFuture, IntoIterator, IntoIteratorItem, - IoBufRead, - IoLines, - IoRead, - IoSeek, - IoWrite, - IpAddr, - Ipv4Addr, - Ipv6Addr, IrTyKind, - Is, Item, ItemContext, - IterEmpty, - IterOnce, - IterPeekable, Iterator, IteratorItem, IteratorMap, Layout, - Left, Lifetime, - LinkedList, - LintDiagnostic, LintPass, LocalKey, Mutex, MutexGuard, - N, + Named, NonNull, NonZero, None, @@ -311,25 +259,17 @@ Option, Ord, Ordering, - OsStr, - OsString, Other, Output, - Param, ParamSet, PartialEq, PartialOrd, - Path, - PathBuf, Pending, - PinCoerceUnsized, PinDerefMutHelper, Pointer, Poll, ProcMacro, - ProceduralMasqueradeDummyType, Range, - RangeBounds, RangeCopy, RangeFrom, RangeFromCopy, @@ -345,48 +285,36 @@ Rc, RcWeak, Ready, - Receiver, RefCell, - RefCellRef, - RefCellRefMut, Reference, Relaxed, Release, Result, ResumeTy, - Return, Reverse, - Right, Rust, RustaceansAreAwesome, RwLock, RwLockReadGuard, RwLockWriteGuard, - Saturating, - SeekFrom, SelfTy, Send, SeqCst, Sized, Slice, SliceIndex, - SliceIter, Some, + Source, SpanCtxt, - Stdin, Str, String, Struct, StructuralPartialEq, - Subdiagnostic, SymbolIntern, Sync, SyncUnsafeCell, - T, Target, This, - ToOwned, - ToString, TokenStream, Trait, TrivialClone, @@ -401,21 +329,12 @@ TyKind, Type, Union, - Unknown, Unsize, - UnsizedConstParamTy, - Upvars, Vec, - VecDeque, - Waker, Wrapper, - Wrapping, - Yield, _DECLS, - __D, __H, __S, - __T, __awaitee, __try_var, _t, @@ -477,7 +396,6 @@ and, and_then, anon, - anon_adt, anon_assoc, anonymous_lifetime_in_impl_trait, any, @@ -496,9 +414,7 @@ arm_target_feature, array, as_dash_needed: "as-needed", - as_ptr, as_ref, - as_str, asm, asm_cfg, asm_const, @@ -511,6 +427,7 @@ assert, assert_eq, assert_eq_macro, + assert_fields_are_eq, assert_inhabited, assert_macro, assert_mem_uninitialized_valid, @@ -548,7 +465,6 @@ async_iterator, async_iterator_poll_next, async_trait_bounds, - atomic, atomic_and, atomic_cxchg, atomic_cxchgweak, @@ -556,7 +472,6 @@ atomic_load, atomic_max, atomic_min, - atomic_mod, atomic_nand, atomic_or, atomic_singlethreadfence, @@ -597,7 +512,6 @@ bikeshed, bikeshed_guaranteed_no_drop, bin, - binaryheap_iter, bind_by_move_pattern_guards, bindings_after_at, bitand, @@ -612,7 +526,6 @@ block, blocking, bool, - bool_then, borrowck_graphviz_format, borrowck_graphviz_postflow, box_new, @@ -626,15 +539,10 @@ breakpoint, bridge, bswap, - btreemap_contains_key, - btreemap_insert, - btreeset_iter, built, builtin_syntax, bundle, - c, c_dash_variadic, - c_str, c_str_literals, c_unwind, c_variadic, @@ -667,7 +575,6 @@ cfg_doctest, cfg_emscripten_wasm_eh, cfg_eval, - cfg_fmt_debug, cfg_overflow_checks, cfg_panic, cfg_relocation_model, @@ -688,10 +595,6 @@ cfi, cfi_encoding, char, - char_is_ascii, - char_to_digit, - child_id, - child_kill, client, clippy, clobber_abi, @@ -704,8 +607,6 @@ closure_to_fn_coercion, closure_track_caller, cmp, - cmp_max, - cmp_min, cmp_ord_max, cmp_ord_min, cmp_partialeq_eq, @@ -742,6 +643,7 @@ const_allocate, const_async_blocks, const_block_items, + const_c_variadic, const_closures, const_compare_raw_pointers, const_constructor, @@ -795,7 +697,6 @@ contracts_internals, contracts_requires, convert, - convert_identity, copy, copy_closures, copy_nonoverlapping, @@ -806,7 +707,6 @@ core, core_panic, core_panic_2015_macro, - core_panic_2021_macro, core_panic_macro, coroutine, coroutine_clone, @@ -832,7 +732,6 @@ csky, csky_target_feature, cstr_type, - cstring_as_c_str, cstring_type, ctlz, ctlz_nonzero, @@ -846,9 +745,7 @@ custom_inner_attributes, custom_mir, custom_test_frameworks, - d, d32, - dbg_macro, dead_code, dealloc, debug, @@ -856,14 +753,11 @@ debug_assert_macro, debug_assert_ne_macro, debug_assertions, - debug_struct, debug_struct_fields_finish, - debug_tuple, debug_tuple_fields_finish, debugger_visualizer, decl_macro, declare_lint_pass, - decode, decorated, default_alloc_error_handler, default_field_values, @@ -882,7 +776,6 @@ default_type_parameter_fallback, default_type_params, define_opaque, - delayed_bug_from_inside_query, deny, deprecated, deprecated_safe, @@ -890,12 +783,10 @@ deref, deref_method, deref_mut, - deref_mut_method, deref_patterns, deref_pure, deref_target, derive, - derive_coerce_pointee, derive_const, derive_const_issue: "118304", derive_default_enum, @@ -929,7 +820,6 @@ doc_primitive, doc_spotlight, doctest, - dotdot: "..", dotdot_in_tuple_patterns, dotdoteq_in_patterns, dreg, @@ -948,7 +838,6 @@ dyn_star, dyn_trait, dynamic_no_pic: "dynamic-no-pic", - e, edition_panic, effective_target_features, effects, @@ -956,16 +845,9 @@ eh_personality, eii, eii_declaration, - eii_impl, eii_internals, eii_shared_macro, element_ty, - emit, - emit_enum, - emit_enum_variant, - emit_enum_variant_arg, - emit_struct, - emit_struct_field, // Notes about `sym::empty`: // - It should only be used when it genuinely means "empty symbol". Use // `Option` when "no symbol" is a possibility. @@ -974,23 +856,19 @@ // it's clearer that it's intended as a dummy value, and more likely // to be detected if it accidentally does get used. empty: "", + empty_braces: "{}", emscripten_wasm_eh, enable, - encode, end, entry_nops, - enumerate_method, env, env_CFG_RELEASE: env!("CFG_RELEASE"), - eprint_macro, - eprintln_macro, eq, ergonomic_clones, ermsb_target_feature, exact_div, except, exception_handling: "exception-handling", - exchange_malloc, exclusive_range_pattern, exhaustive_integer_patterns, exhaustive_patterns, @@ -1027,51 +905,14 @@ extern_weak, external, external_doc, - f, f16, - f16_consts_mod, - f16_epsilon, f16_nan, f16c_target_feature, f32, - f32_consts_mod, - f32_epsilon, - f32_legacy_const_digits, - f32_legacy_const_epsilon, - f32_legacy_const_infinity, - f32_legacy_const_mantissa_dig, - f32_legacy_const_max, - f32_legacy_const_max_10_exp, - f32_legacy_const_max_exp, - f32_legacy_const_min, - f32_legacy_const_min_10_exp, - f32_legacy_const_min_exp, - f32_legacy_const_min_positive, - f32_legacy_const_nan, - f32_legacy_const_neg_infinity, - f32_legacy_const_radix, f32_nan, f64, - f64_consts_mod, - f64_epsilon, - f64_legacy_const_digits, - f64_legacy_const_epsilon, - f64_legacy_const_infinity, - f64_legacy_const_mantissa_dig, - f64_legacy_const_max, - f64_legacy_const_max_10_exp, - f64_legacy_const_max_exp, - f64_legacy_const_min, - f64_legacy_const_min_10_exp, - f64_legacy_const_min_exp, - f64_legacy_const_min_positive, - f64_legacy_const_nan, - f64_legacy_const_neg_infinity, - f64_legacy_const_radix, f64_nan, f128, - f128_consts_mod, - f128_epsilon, f128_nan, fabsf16, fabsf32, @@ -1087,17 +928,21 @@ fence, ferris: "🦀", fetch_update, - ffi, ffi_const, ffi_pure, ffi_returns_twice, field, + field_base, field_init_shorthand, + field_of, + field_offset, + field_projections, + field_representing_type, + field_representing_type_raw, + field_type, fields, file, - file_options, - flags, - float, + final_associated_functions, float_to_int_unchecked, floorf16, floorf32, @@ -1127,10 +972,8 @@ forbid, force_target_feature, forget, - format, format_args, format_args_capture, - format_args_macro, format_args_nl, format_argument, format_arguments, @@ -1143,28 +986,21 @@ frem_fast, from, from_desugaring, - from_fn, from_iter, - from_iter_fn, from_output, from_residual, from_size_align_unchecked, from_size_alignment_unchecked, from_str, - from_str_method, from_str_nonconst, - from_u16, from_usize, from_yeet, frontmatter, - fs_create_dir, fsub_algebraic, fsub_fast, full, fundamental, fused_iterator, - future, - future_drop_poll, future_output, future_trait, fxsr, @@ -1195,61 +1031,22 @@ half_open_range_patterns, half_open_range_patterns_in_slices, hash, - hashmap_contains_key, - hashmap_drain_ty, - hashmap_insert, - hashmap_iter_mut_ty, - hashmap_iter_ty, - hashmap_keys_ty, - hashmap_values_mut_ty, - hashmap_values_ty, - hashset_drain_ty, - hashset_iter, - hashset_iter_ty, hexagon, hexagon_target_feature, hidden, hide, - hint, homogeneous_aggregate, - host, html_favicon_url, html_logo_url, html_no_source, html_playground_url, html_root_url, hwaddress, - i, i8, - i8_legacy_const_max, - i8_legacy_const_min, - i8_legacy_fn_max_value, - i8_legacy_fn_min_value, - i8_legacy_mod, i16, - i16_legacy_const_max, - i16_legacy_const_min, - i16_legacy_fn_max_value, - i16_legacy_fn_min_value, - i16_legacy_mod, i32, - i32_legacy_const_max, - i32_legacy_const_min, - i32_legacy_fn_max_value, - i32_legacy_fn_min_value, - i32_legacy_mod, i64, - i64_legacy_const_max, - i64_legacy_const_min, - i64_legacy_fn_max_value, - i64_legacy_fn_min_value, - i64_legacy_mod, i128, - i128_legacy_const_max, - i128_legacy_const_min, - i128_legacy_fn_max_value, - i128_legacy_fn_min_value, - i128_legacy_mod, i128_type, ident, if_let, @@ -1260,11 +1057,11 @@ immediate_abort: "immediate-abort", impl_header_lifetime_elision, impl_lint_pass, + impl_restriction, impl_trait_in_assoc_type, impl_trait_in_bindings, impl_trait_in_fn_trait_return, impl_trait_projections, - implement_via_object, implied_by, import, import_name_type, @@ -1274,27 +1071,25 @@ in_band_lifetimes, include, include_bytes, - include_bytes_macro, include_str, - include_str_macro, inclusive_range_syntax, index, index_mut, infer_outlives_requirements, infer_static_outlives_requirements, inherent_associated_types, - inherit, initial, inlateout, inline, inline_const, inline_const_pat, inout, - instant_now, + inputs, instruction_set, integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below integral, internal, + internal_eq_trait_method_impls, internal_features, into_async_iter_into_iter, into_future, @@ -1302,34 +1097,19 @@ into_try_type, intra_doc_pointers, intrinsics, - intrinsics_unaligned_volatile_load, - intrinsics_unaligned_volatile_store, - io_error_new, - io_errorkind, - io_stderr, - io_stdout, irrefutable_let_patterns, is, is_auto, is_val_statically_known, isa_attribute, isize, - isize_legacy_const_max, - isize_legacy_const_min, - isize_legacy_fn_max_value, - isize_legacy_fn_min_value, - isize_legacy_mod, issue, issue_5723_bootstrap, issue_tracker_base_url, item, item_like_imports, iter, - iter_cloned, - iter_copied, - iter_filter, iter_mut, - iter_repeat, iterator, iterator_collect_fn, kcfi, @@ -1360,7 +1140,6 @@ lifetime, lifetime_capture_rules_2024, lifetimes, - likely, line, link, link_arg_attribute, @@ -1373,6 +1152,7 @@ link_section, linkage, linker, + linker_info, linker_messages, linkonce, linkonce_odr, @@ -1407,7 +1187,6 @@ macro_at_most_once_rep, macro_attr, macro_attributes_in_derive_output, - macro_concat, macro_derive, macro_escape, macro_export, @@ -1429,7 +1208,6 @@ masked, match_beginning_vert, match_default_bindings, - matches_macro, maximumf16, maximumf32, maximumf64, @@ -1440,18 +1218,15 @@ maxnumf128, may_dangle, may_unwind, + maybe_dangling, maybe_uninit, maybe_uninit_uninit, maybe_uninit_zeroed, mem_align_const, - mem_align_of, mem_discriminant, mem_drop, mem_forget, - mem_replace, mem_size_const, - mem_size_of, - mem_size_of_val, mem_swap, mem_uninitialized, mem_variant_count, @@ -1492,12 +1267,10 @@ mir_cast_unsize, mir_checked, mir_debuginfo, - mir_deinit, mir_discriminant, mir_drop, mir_field, mir_goto, - mir_len, mir_make_place, mir_move, mir_offset, @@ -1570,7 +1343,6 @@ never_type_fallback, new, new_binary, - new_const, new_debug, new_debug_noop, new_display, @@ -1582,10 +1354,7 @@ new_unchecked, new_upper_exp, new_upper_hex, - new_v1, - new_v1_formatted, next, - niko, nll, no, no_builtins, @@ -1641,7 +1410,6 @@ opaque, opaque_generic_const_args, opaque_module_name_placeholder: "", - open_options_new, ops, opt_out_copy, optimize, @@ -1650,16 +1418,13 @@ optin_builtin_traits, option, option_env, - option_expect, - option_unwrap, options, or, or_patterns, ord_cmp_method, - os_str_to_os_string, - os_string_as_os_str, other, out, + output, overflow_checks, overlapping_marker_traits, owned_box, @@ -1669,7 +1434,6 @@ panic_2015, panic_2021, panic_abort, - panic_any, panic_bounds_check, panic_cannot_unwind, panic_const_add_overflow, @@ -1719,22 +1483,17 @@ pat_param, patchable_function_entry, path, - path_main_separator, - path_to_pathbuf, - pathbuf_as_path, pattern_complexity_limit, pattern_parentheses, pattern_type, pattern_type_range_trait, pattern_types, - permissions_from_mode, phantom_data, phase, pic, pie, pin, pin_ergonomics, - pin_macro, pin_v2, platform_intrinsics, plugin, @@ -1745,8 +1504,6 @@ pointee_trait, pointer, poll, - poll_next, - position, post_cleanup: "post-cleanup", post_dash_lto: "post-lto", postfix_match, @@ -1765,7 +1522,6 @@ precise_capturing, precise_capturing_in_traits, precise_pointer_size_matching, - precision, predicates, pref_align_of, prefetch_read_data, @@ -1778,8 +1534,6 @@ prelude_import, preserves_flags, prfchw_target_feature, - print_macro, - println_macro, proc_dash_macro: "proc-macro", proc_macro, proc_macro_attribute, @@ -1791,9 +1545,6 @@ proc_macro_mod, proc_macro_non_items, proc_macro_path_invoc, - process_abort, - process_exit, - profiler_builtins, profiler_runtime, ptr, ptr_cast, @@ -1802,7 +1553,6 @@ ptr_const_is_null, ptr_copy, ptr_copy_nonoverlapping, - ptr_eq, ptr_from_ref, ptr_guaranteed_cmp, ptr_is_null, @@ -1814,14 +1564,9 @@ ptr_offset_from_unsigned, ptr_read, ptr_read_unaligned, - ptr_read_volatile, ptr_replace, - ptr_slice_from_raw_parts, - ptr_slice_from_raw_parts_mut, ptr_swap, ptr_swap_nonoverlapping, - ptr_without_provenance, - ptr_without_provenance_mut, ptr_write, ptr_write_bytes, ptr_write_unaligned, @@ -1838,7 +1583,6 @@ question_mark, quote, range_inclusive_new, - range_step, raw_dash_dylib: "raw-dylib", raw_dylib, raw_dylib_elf, @@ -1846,11 +1590,6 @@ raw_identifiers, raw_ref_op, re_rebalance_coherence, - read_enum, - read_enum_variant, - read_enum_variant_arg, - read_struct, - read_struct_field, read_via_copy, readonly, realloc, @@ -1899,8 +1638,6 @@ residual, result, result_ffi_guarantees, - result_ok_method, - resume, return_position_impl_trait_in_trait, return_type_notation, riscv32, @@ -1919,7 +1656,6 @@ roundf32, roundf64, roundf128, - rt, rtm_target_feature, runtime, rust, @@ -1974,9 +1710,13 @@ rustc_driver, rustc_dummy, rustc_dump_def_parents, + rustc_dump_inferred_outlives, rustc_dump_item_bounds, + rustc_dump_object_lifetime_defaults, rustc_dump_predicates, rustc_dump_user_args, + rustc_dump_variances, + rustc_dump_variances_of_opaques, rustc_dump_vtable, rustc_dyn_incompatible_trait, rustc_effective_visibility, @@ -2013,10 +1753,8 @@ rustc_nounwind, rustc_objc_class, rustc_objc_selector, - rustc_object_lifetime_default, rustc_offload_kernel, rustc_on_unimplemented, - rustc_outlives, rustc_paren_sugar, rustc_partition_codegened, rustc_partition_reused, @@ -2035,7 +1773,6 @@ rustc_regions, rustc_reservation_impl, rustc_scalable_vector, - rustc_serialize, rustc_should_not_be_called_on_const_items, rustc_simd_monomorphize_lane_limit, rustc_skip_during_method_dispatch, @@ -2047,15 +1784,12 @@ rustc_then_this_would_need, rustc_trivial_field_reads, rustc_unsafe_specialization_marker, - rustc_variance, - rustc_variance_of_opaques, rustdoc, rustdoc_internals, rustdoc_missing_doc_code_examples, rustfmt, rvalue_static_promotion, rwpi, - s, s390x, s390x_target_feature, s390x_target_feature_vector, @@ -2065,7 +1799,6 @@ sanitizer_cfi_normalize_integers, sanitizer_runtime, saturating_add, - saturating_div, saturating_sub, sdylib, search_unbox, @@ -2085,8 +1818,6 @@ show, shr, shr_assign, - sig_dfl, - sig_ign, signed, simd, simd_add, @@ -2176,14 +1907,10 @@ size_of_val, sized, sized_hierarchy, - skip, slice, slice_from_raw_parts, slice_from_raw_parts_mut, - slice_from_ref, slice_get_unchecked, - slice_into_vec, - slice_iter, slice_len_fn, slice_patterns, slicing_syntax, @@ -2224,8 +1951,6 @@ stop_after_dataflow, store, str, - str_chars, - str_ends_with, str_from_utf8, str_from_utf8_mut, str_from_utf8_unchecked, @@ -2234,20 +1959,8 @@ str_inherent_from_utf8_mut, str_inherent_from_utf8_unchecked, str_inherent_from_utf8_unchecked_mut, - str_len, - str_split_whitespace, - str_starts_with, - str_trim, - str_trim_end, - str_trim_start, strict_provenance_lints, - string_as_mut_str, - string_as_str, string_deref_patterns, - string_from_utf8, - string_insert_str, - string_new, - string_push_str, stringify, struct_field_attributes, struct_inherit, @@ -2263,9 +1976,6 @@ sym, sync, synthetic, - sys_mutex_lock, - sys_mutex_try_lock, - sys_mutex_unlock, t32, target, target_abi, @@ -2300,16 +2010,12 @@ test_unstable_lint, thread, thread_local, - thread_local_macro, three_way_compare, thumb2, thumb_mode: "thumb-mode", tmm_reg, - to_owned_method, to_string, - to_string_method, to_vec, - todo_macro, tool_attributes, tool_lints, trace_macros, @@ -2335,7 +2041,6 @@ try_blocks_heterogeneous, try_capture, try_from, - try_from_fn, try_into, try_trait_v2, try_trait_v2_residual, @@ -2353,6 +2058,7 @@ type_changing_struct_update, type_id, type_id_eq, + type_id_vtable, type_info, type_ir, type_ir_infer_ctxt_like, @@ -2412,7 +2118,6 @@ underscore_imports, underscore_lifetimes, uniform_paths, - unimplemented_macro, unit, universal_impl_trait, unix, @@ -2440,6 +2145,7 @@ unsafe_no_drop_flag, unsafe_pinned, unsafe_unpin, + unsafety, unsize, unsized_const_param_ty, unsized_const_params, @@ -2484,20 +2190,10 @@ value, values, var, + variadic, variant_count, variants, vec, - vec_as_mut_slice, - vec_as_slice, - vec_from_elem, - vec_is_empty, - vec_macro, - vec_new, - vec_pop, - vec_reserve, - vec_with_capacity, - vecdeque_iter, - vecdeque_reserve, vector, verbatim, version, @@ -2515,7 +2211,6 @@ vsreg, vsx, vtable_align, - vtable_for, vtable_size, warn, wasip2, @@ -2541,6 +2236,7 @@ wrapping_rem_euclid, wrapping_sub, wreg, + write_box_via_move, write_bytes, write_fmt, write_macro, @@ -2780,6 +2476,14 @@ impl MacroRulesNormalizedIdent { pub fn new(ident: Ident) -> Self { MacroRulesNormalizedIdent(ident.normalize_to_macro_rules()) } + + pub fn symbol(&self) -> Symbol { + self.0.name + } + + pub fn ident(&self) -> Ident { + self.0 + } } impl fmt::Debug for MacroRulesNormalizedIdent { @@ -3070,6 +2774,15 @@ pub mod sym { #[doc(inline)] pub use super::sym_generated::*; + // Used quite often in relation to C ABI. + pub const C: Symbol = ascii_letter_digit('C').unwrap(); + + // RISC-V stuff + #[expect(non_upper_case_globals)] + pub const f: Symbol = ascii_letter_digit('f').unwrap(); + #[expect(non_upper_case_globals)] + pub const d: Symbol = ascii_letter_digit('d').unwrap(); + /// Get the symbol for an integer. /// /// The first few non-negative integers each have a static symbol and therefore @@ -3084,6 +2797,23 @@ pub fn integer + Copy + itoa::Integer>(n: N) -> Symbol { let printed = buffer.format(n); Symbol::intern(printed) } + + pub const fn ascii_letter_digit(c: char) -> Option { + let i = c as u32; + Option::Some(Symbol::new(match c { + '0'..='9' => super::SYMBOL_DIGITS_BASE + (i - '0' as u32), + 'A'..='Z' => super::SYMBOL_UPPERCASE_LETTERS_BASE + (i - 'A' as u32), + 'a'..='z' => super::SYMBOL_LOWERCASE_LETTERS_BASE + (i - 'a' as u32), + _ => return Option::None, + })) + } + + pub fn character(c: char) -> Symbol { + ascii_letter_digit(c).unwrap_or_else(|| { + let mut buf: [u8; char::MAX_LEN_UTF8] = Default::default(); + Symbol::intern(c.encode_utf8(&mut buf)) + }) + } } impl Symbol { diff --git a/compiler/rustc_symbol_mangling/src/export.rs b/compiler/rustc_symbol_mangling/src/export.rs index c99ba1d58f31..71ee7d234167 100644 --- a/compiler/rustc_symbol_mangling/src/export.rs +++ b/compiler/rustc_symbol_mangling/src/export.rs @@ -1,5 +1,6 @@ +use std::debug_assert_matches; + use rustc_abi::IntegerType; -use rustc_data_structures::debug_assert_matches; use rustc_data_structures::stable_hasher::StableHasher; use rustc_hashes::Hash128; use rustc_hir::def::DefKind; diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 35a3d0dac8af..ae0f5ec2a72d 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -87,10 +87,6 @@ //! virtually impossible. Thus, symbol hash generation exclusively relies on //! DefPaths which are much more robust in the face of changes to the code base. -// tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] -// tidy-alphabetical-end - use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 8f842e030011..b4d9031469dd 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -4,7 +4,6 @@ //! def-path. This is used for unit testing the code that generates //! paths etc in all kinds of annoying scenarios. -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::find_attr; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -53,10 +52,7 @@ fn process_attrs(&mut self, def_id: LocalDefId) { // to test the entirety of the string, if they choose, or else just // some subset. - if let Some(attr_span) = find_attr!( - tcx.get_all_attrs(def_id), - AttributeKind::RustcSymbolName(span) => span - ) { + if let Some(attr_span) = find_attr!(tcx, def_id, RustcSymbolName(span) => span) { let def_id = def_id.to_def_id(); let instance = Instance::new_raw( def_id, @@ -83,8 +79,8 @@ fn process_attrs(&mut self, def_id: LocalDefId) { } if let Some(attr_span) = find_attr!( - tcx.get_all_attrs(def_id), - AttributeKind::RustcDefPath(span) => span + tcx, def_id, + RustcDefPath(span) => span ) { tcx.dcx().emit_err(TestOutput { span: *attr_span, diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index a10699bbce88..27dd0b764f3b 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -67,7 +67,7 @@ pub(super) fn regclass_map() -> rustc_data_structures::fx::FxHashMap< macro_rules! def_regs { ($arch:ident $arch_reg:ident $arch_regclass:ident { $( - $reg:ident: $class:ident $(, $extra_class:ident)* = [$reg_name:literal $(, $alias:literal)*] $(% $filter:ident)?, + $reg:ident: $class:ident $(, $extra_class:ident)* = [$reg_name:literal $(, $alias:literal)*] $(% $filter:ident)*, )* $( #error = [$($bad_reg:literal),+] => $error:literal, @@ -121,7 +121,7 @@ pub fn validate(self, _target_features, _target, _is_clobber - )?;)? + )?;)* Ok(()) } )* @@ -142,7 +142,7 @@ pub(super) fn fill_reg_map( #[allow(unused_imports)] use super::{InlineAsmReg, InlineAsmRegClass}; $( - if $($filter(_arch, _reloc_model, _target_features, _target, false).is_ok() &&)? true { + if $($filter(_arch, _reloc_model, _target_features, _target, false).is_ok() &&)* true { if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { set.insert(InlineAsmReg::$arch($arch_reg::$reg)); } @@ -614,7 +614,7 @@ pub fn supported_types( allow_experimental_reg: bool, ) -> &'static [(InlineAsmType, Option)] { match self { - Self::X86(r) => r.supported_types(arch), + Self::X86(r) => r.supported_types(arch, allow_experimental_reg), Self::Arm(r) => r.supported_types(arch), Self::AArch64(r) => r.supported_types(arch), Self::RiscV(r) => r.supported_types(arch), diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index 97eb93335818..3976d9890256 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -55,7 +55,7 @@ pub fn supported_types( } pub(crate) fn is_e(target_features: &FxIndexSet) -> bool { - target_features.contains(&sym::e) + target_features.contains(&sym::character('e')) } fn not_e( diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index 15c1925bcdaf..6f0faffa3298 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -105,6 +105,7 @@ pub fn default_modifier(self, arch: InlineAsmArch) -> Option { pub fn supported_types( self, arch: InlineAsmArch, + allow_experimental_reg: bool, ) -> &'static [(InlineAsmType, Option)] { match self { Self::reg | Self::reg_abcd => { @@ -115,21 +116,52 @@ pub fn supported_types( } } Self::reg_byte => types! { _: I8; }, - Self::xmm_reg => types! { - sse: I32, I64, F16, F32, F64, F128, - VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2); - }, - Self::ymm_reg => types! { - avx: I32, I64, F16, F32, F64, F128, - VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2), - VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF16(16), VecF32(8), VecF64(4); - }, - Self::zmm_reg => types! { - avx512f: I32, I64, F16, F32, F64, F128, - VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2), - VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF16(16), VecF32(8), VecF64(4), - VecI8(64), VecI16(32), VecI32(16), VecI64(8), VecF16(32), VecF32(16), VecF64(8); - }, + Self::xmm_reg => { + if allow_experimental_reg { + types! { + sse: I32, I64, I128, F16, F32, F64, F128, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2); + } + } else { + types! { + sse: I32, I64, F16, F32, F64, F128, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2); + } + } + } + Self::ymm_reg => { + if allow_experimental_reg { + types! { + avx: I32, I64, I128, F16, F32, F64, F128, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF16(16), VecF32(8), VecF64(4); + } + } else { + types! { + avx: I32, I64, F16, F32, F64, F128, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF16(16), VecF32(8), VecF64(4); + } + } + } + Self::zmm_reg => { + if allow_experimental_reg { + types! { + avx512f: I32, I64, I128, F16, F32, F64, F128, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF16(16), VecF32(8), VecF64(4), + VecI8(64), VecI16(32), VecI32(16), VecI64(8), VecF16(32), VecF32(16), VecF64(8); + } + } else { + types! { + avx512f: I32, I64, F16, F32, F64, F128, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF16(16), VecF32(8), VecF64(4), + VecI8(64), VecI16(32), VecI32(16), VecI64(8), VecF16(32), VecF32(16), VecF64(8); + } + } + } + Self::kreg => types! { avx512f: I8, I16; avx512bw: I32, I64; @@ -218,8 +250,8 @@ fn esi_reserved( r15: reg = ["r15", "r15w", "r15d"] % x86_64_only, al: reg_byte = ["al"], ah: reg_byte = ["ah"] % high_byte, - bl: reg_byte = ["bl"], - bh: reg_byte = ["bh"] % high_byte, + bl: reg_byte = ["bl"] % rbx_reserved, + bh: reg_byte = ["bh"] % rbx_reserved % high_byte, cl: reg_byte = ["cl"], ch: reg_byte = ["ch"] % high_byte, dl: reg_byte = ["dl"], diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index 13be3888611f..e9a19aa7024b 100644 --- a/compiler/rustc_target/src/callconv/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs @@ -3,7 +3,7 @@ use rustc_abi::{BackendRepr, HasDataLayout, Primitive, TyAbiInterface}; use crate::callconv::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::spec::{Abi, HasTargetSpec, Target}; +use crate::spec::{HasTargetSpec, RustcAbi, Target}; /// Indicates the variant of the AArch64 ABI we are compiling for. /// Used to accommodate Apple and Microsoft's deviations from the usual AAPCS ABI. @@ -34,7 +34,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Opti RegKind::Integer => false, // The softfloat ABI treats floats like integers, so they // do not get homogeneous aggregate treatment. - RegKind::Float => cx.target_spec().abi != Abi::SoftFloat, + RegKind::Float => cx.target_spec().rustc_abi != Some(RustcAbi::Softfloat), RegKind::Vector => size.bits() == 64 || size.bits() == 128, }; @@ -43,7 +43,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Opti } fn softfloat_float_abi(target: &Target, arg: &mut ArgAbi<'_, Ty>) { - if target.abi != Abi::SoftFloat { + if target.rustc_abi != Some(RustcAbi::Softfloat) { return; } // Do *not* use the float registers for passing arguments, as that would make LLVM pick the ABI diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 6c8e0e181c4a..4083e28c09cb 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -1,8 +1,8 @@ use std::{fmt, iter}; use rustc_abi::{ - AddressSpace, Align, BackendRepr, CanonAbi, ExternAbi, HasDataLayout, Primitive, Reg, RegKind, - Scalar, Size, TyAbiInterface, TyAndLayout, + AddressSpace, Align, BackendRepr, CanonAbi, ExternAbi, FieldsShape, HasDataLayout, Primitive, + Reg, RegKind, Scalar, Size, TyAbiInterface, TyAndLayout, Variants, }; use rustc_macros::HashStable_Generic; @@ -514,6 +514,11 @@ pub fn cast_to>(&mut self, target: T) { self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32: false }; } + pub fn cast_to_with_attrs>(&mut self, target: T, attrs: ArgAttributes) { + self.mode = + PassMode::Cast { cast: Box::new(target.into().with_attrs(attrs)), pad_i32: false }; + } + pub fn cast_to_and_pad_i32>(&mut self, target: T, pad_i32: bool) { self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32 }; } @@ -801,7 +806,12 @@ pub fn adjust_for_rust_abi(&mut self, cx: &C) // We want to pass small aggregates as immediates, but using // an LLVM aggregate type for this leads to bad optimizations, // so we pick an appropriately sized integer type instead. - arg.cast_to(Reg { kind: RegKind::Integer, size }); + let attr = if layout_is_noundef(arg.layout, cx) { + ArgAttribute::NoUndef + } else { + ArgAttribute::default() + }; + arg.cast_to_with_attrs(Reg { kind: RegKind::Integer, size }, attr.into()); } } @@ -836,6 +846,66 @@ pub fn adjust_for_rust_abi(&mut self, cx: &C) } } +/// Determines whether `layout` contains no uninit bytes (no padding, no unions), +/// using only the computed layout. +/// +/// Conservative: returns `false` for anything it cannot prove fully initialized, +/// including multi-variant enums and SIMD vectors. +// FIXME: extend to multi-variant enums (per-variant padding analysis needed). +fn layout_is_noundef<'a, Ty, C>(layout: TyAndLayout<'a, Ty>, cx: &C) -> bool +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout, +{ + match layout.backend_repr { + BackendRepr::Scalar(scalar) => !scalar.is_uninit_valid(), + BackendRepr::ScalarPair(s1, s2) => { + !s1.is_uninit_valid() + && !s2.is_uninit_valid() + // Ensure there is no padding. + && s1.size(cx) + s2.size(cx) == layout.size + } + BackendRepr::Memory { .. } => match layout.fields { + FieldsShape::Primitive | FieldsShape::Union(_) => false, + // Array elements are at stride offsets with no inter-element gaps. + FieldsShape::Array { stride: _, count } => { + count == 0 || layout_is_noundef(layout.field(cx, 0), cx) + } + FieldsShape::Arbitrary { .. } => { + // With `Variants::Multiple`, `layout.fields` only covers shared + // bytes (niche/discriminant); per-variant data is absent, so + // full coverage cannot be proven. + matches!(layout.variants, Variants::Single { .. }) && fields_are_noundef(layout, cx) + } + }, + BackendRepr::SimdVector { .. } | BackendRepr::ScalableVector { .. } => false, + } +} + +/// Returns `true` if the fields of `layout` contiguously cover bytes `0..layout.size` +/// with no padding gaps and each field is recursively `layout_is_noundef`. +fn fields_are_noundef<'a, Ty, C>(layout: TyAndLayout<'a, Ty>, cx: &C) -> bool +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout, +{ + let mut cursor = Size::ZERO; + for i in layout.fields.index_by_increasing_offset() { + let field = layout.field(cx, i); + if field.size == Size::ZERO { + continue; + } + if layout.fields.offset(i) != cursor { + return false; + } + if !layout_is_noundef(field, cx) { + return false; + } + cursor += field.size; + } + cursor == layout.size +} + // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index a77724a572dc..683b615eec83 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -5,7 +5,7 @@ use rustc_abi::{Endian, HasDataLayout, TyAbiInterface}; use crate::callconv::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::spec::{Env, HasTargetSpec, Os}; +use crate::spec::{HasTargetSpec, Os}; #[derive(Debug, Clone, Copy, PartialEq)] enum ABI { @@ -106,8 +106,10 @@ pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - let abi = if cx.target_spec().env == Env::Musl || cx.target_spec().os == Os::FreeBsd { + let abi = if cx.target_spec().options.llvm_abiname == "elfv2" { ELFv2 + } else if cx.target_spec().options.llvm_abiname == "elfv1" { + ELFv1 } else if cx.target_spec().os == Os::Aix { AIX } else { diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 2d2f15651c43..a4460a372517 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2210,8 +2210,10 @@ pub struct TargetOptions { pub env: Env, /// ABI name to distinguish multiple ABIs on the same OS and architecture. For instance, `"eabi"` /// or `"eabihf"`. Defaults to [`Abi::Unspecified`]. - /// This field is *not* forwarded directly to LLVM; its primary purpose is `cfg(target_abi)`. - /// However, parts of the backend do check this field for specific values to enable special behavior. + /// This field is *not* forwarded directly to LLVM and therefore does not control which ABI (in + /// the sense of function calling convention) is actually used; its primary purpose is + /// `cfg(target_abi)`. The actual calling convention is controlled by `llvm_abiname`, + /// `llvm_floatabi`, and `rustc_abi`. pub abi: Abi, /// Vendor name to use for conditional compilation (`target_vendor`). Defaults to "unknown". #[rustc_lint_opt_deny_field_access( @@ -3194,6 +3196,27 @@ macro_rules! check_matches { "ARM targets must set `llvm-floatabi` to `hard` or `soft`", ) } + // PowerPC64 targets that are not AIX must set their ABI to either ELFv1 or ELFv2 + Arch::PowerPC64 => { + if self.os == Os::Aix { + check!( + self.llvm_abiname.is_empty(), + "AIX targets always use the AIX ABI and `llvm_abiname` should be left empty", + ); + } else if self.endian == Endian::Big { + check_matches!( + &*self.llvm_abiname, + "elfv1" | "elfv2", + "invalid PowerPC64 ABI name: {}", + self.llvm_abiname, + ); + } else { + check!( + self.llvm_abiname == "elfv2", + "little-endian PowerPC64 targets only support the `elfv2` ABI", + ); + } + } _ => {} } @@ -3207,8 +3230,8 @@ macro_rules! check_matches { ), RustcAbi::Softfloat => check_matches!( self.arch, - Arch::X86 | Arch::X86_64 | Arch::S390x, - "`softfloat` ABI is only valid for x86 and s390x targets" + Arch::X86 | Arch::X86_64 | Arch::S390x | Arch::AArch64, + "`softfloat` ABI is only valid for x86, s390x, and aarch64 targets" ), } } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs index 07512c01dc4a..e204c6466e29 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs @@ -8,13 +8,14 @@ use rustc_abi::Endian; use crate::spec::{ - Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, - Target, TargetMetadata, TargetOptions, + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, RustcAbi, SanitizerSet, + StackProbeType, Target, TargetMetadata, TargetOptions, }; pub(crate) fn target() -> Target { let opts = TargetOptions { abi: Abi::SoftFloat, + rustc_abi: Some(RustcAbi::Softfloat), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), features: "+v8a,+strict-align,-neon".into(), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs index ad444c139bff..ed2e2fb6ab70 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs @@ -7,13 +7,14 @@ // For example, `-C target-cpu=cortex-a53`. use crate::spec::{ - Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, - Target, TargetMetadata, TargetOptions, + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, RustcAbi, SanitizerSet, + StackProbeType, Target, TargetMetadata, TargetOptions, }; pub(crate) fn target() -> Target { let opts = TargetOptions { abi: Abi::SoftFloat, + rustc_abi: Some(RustcAbi::Softfloat), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), features: "+v8a,+strict-align,-neon".into(), diff --git a/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs index 63f518873c60..6f11f97e3d1d 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs @@ -1,11 +1,12 @@ use crate::spec::{ - Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, - Target, TargetMetadata, TargetOptions, + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, RustcAbi, SanitizerSet, + StackProbeType, Target, TargetMetadata, TargetOptions, }; pub(crate) fn target() -> Target { let opts = TargetOptions { abi: Abi::SoftFloat, + rustc_abi: Some(RustcAbi::Softfloat), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index f17927484648..80674ab4cf8a 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -2,17 +2,17 @@ //! Note that these are similar to but not always identical to LLVM's feature names, //! and Rust adds some features that do not correspond to LLVM features at all. use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_macros::HashStable_Generic; use rustc_span::{Symbol, sym}; -use crate::spec::{Abi, Arch, FloatAbi, RustcAbi, Target}; +use crate::spec::{Arch, FloatAbi, RustcAbi, Target}; /// Features that control behaviour of rustc, rather than the codegen. /// These exist globally and are not in the target-specific lists below. pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; /// Stability information for target features. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, HashStable_Generic)] pub enum Stability { /// This target feature is stable, it can be used in `#[target_feature]` and /// `#[cfg(target_feature)]`. @@ -32,22 +32,6 @@ pub enum Stability { } use Stability::*; -impl HashStable for Stability { - #[inline] - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - std::mem::discriminant(self).hash_stable(hcx, hasher); - match self { - Stability::Stable => {} - Stability::Unstable(nightly_feature) => { - nightly_feature.hash_stable(hcx, hasher); - } - Stability::Forbidden { reason } => { - reason.hash_stable(hcx, hasher); - } - } - } -} - impl Stability { /// Returns whether the feature can be used in `cfg(target_feature)` ever. /// (It might still be nightly-only even if this returns `true`, so make sure to also check @@ -1170,17 +1154,21 @@ pub fn abi_required_features(&self) -> FeatureConstraints { Arch::AArch64 | Arch::Arm64EC => { // Aarch64 has no sane ABI specifier, and LLVM doesn't even have a way to force // the use of soft-float, so all we can do here is some crude hacks. - if self.abi == Abi::SoftFloat { - // LLVM will use float registers when `fp-armv8` is available, e.g. for - // calls to built-ins. The only way to ensure a consistent softfloat ABI - // on aarch64 is to never enable `fp-armv8`, so we enforce that. - // In Rust we tie `neon` and `fp-armv8` together, therefore `neon` is the - // feature we have to mark as incompatible. - FeatureConstraints { required: &[], incompatible: &["neon"] } - } else { - // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled. - // `FeatureConstraints` uses Rust feature names, hence only "neon" shows up. - FeatureConstraints { required: &["neon"], incompatible: &[] } + match self.rustc_abi { + Some(RustcAbi::Softfloat) => { + // LLVM will use float registers when `fp-armv8` is available, e.g. for + // calls to built-ins. The only way to ensure a consistent softfloat ABI + // on aarch64 is to never enable `fp-armv8`, so we enforce that. + // In Rust we tie `neon` and `fp-armv8` together, therefore `neon` is the + // feature we have to mark as incompatible. + FeatureConstraints { required: &[], incompatible: &["neon"] } + } + None => { + // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled. + // `FeatureConstraints` uses Rust feature names, hence only "neon" shows up. + FeatureConstraints { required: &["neon"], incompatible: &[] } + } + Some(r) => panic!("invalid Rust ABI for aarch64: {r:?}"), } } Arch::RiscV32 | Arch::RiscV64 => { diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index cd61a8f65473..0ba46c6ddd56 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -15,7 +15,6 @@ rustc_infer = { path = "../rustc_infer" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_next_trait_solver = { path = "../rustc_next_trait_solver" } -rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 2b2f9a07890f..22342a956710 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -53,15 +53,14 @@ use rustc_abi::ExternAbi; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart, pluralize}; +use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{self as hir}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_macros::extension; use rustc_middle::bug; -use rustc_middle::dep_graph::DepContext; use rustc_middle::traits::PatternOriginExpr; use rustc_middle::ty::error::{ExpectedFound, TypeError, TypeErrorToStringExt}; use rustc_middle::ty::print::{PrintTraitRefExt as _, WrapBinderMode, with_forced_trimmed_paths}; @@ -300,7 +299,16 @@ fn suggest_param_env_shadowing( let trait_def_id = alias.trait_def_id(tcx); let rebased_args = alias.args.rebase_onto(tcx, trait_def_id, impl_substs); + // The impl is erroneous missing a definition for the associated type. + // Skipping it since calling `TyCtxt::type_of` on its assoc ty will trigger an ICE. + if !leaf_def.item.defaultness(tcx).has_value() { + return false; + } + let impl_item_def_id = leaf_def.item.def_id; + if !tcx.check_args_compatible(impl_item_def_id, rebased_args) { + return false; + } let impl_assoc_ty = tcx.type_of(impl_item_def_id).instantiate(tcx, rebased_args); self.infcx.can_eq(param_env, impl_assoc_ty, concrete) @@ -1837,7 +1845,7 @@ pub fn type_error_additional_suggestions( // containing a single ASCII character, perhaps the user meant to write `b'c'` to // specify a byte literal (ty::Uint(ty::UintTy::U8), ty::Char) => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) + if let Ok(code) = self.tcx.sess.source_map().span_to_snippet(span) && let Some(code) = code.strip_circumfix('\'', '\'') // forbid all Unicode escapes && !code.starts_with("\\u") @@ -1854,7 +1862,7 @@ pub fn type_error_additional_suggestions( // containing a single character, perhaps the user meant to write `'c'` to // specify a character literal (issue #92479) (ty::Char, ty::Ref(_, r, _)) if r.is_str() => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) + if let Ok(code) = self.tcx.sess.source_map().span_to_snippet(span) && let Some(code) = code.strip_circumfix('"', '"') && code.chars().count() == 1 { @@ -1867,7 +1875,7 @@ pub fn type_error_additional_suggestions( // If a string was expected and the found expression is a character literal, // perhaps the user meant to write `"s"` to specify a string literal. (ty::Ref(_, r, _), ty::Char) if r.is_str() => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) + if let Ok(code) = self.tcx.sess.source_map().span_to_snippet(span) && code.starts_with("'") && code.ends_with("'") { @@ -2008,7 +2016,7 @@ fn suggest_wrap_to_build_a_tuple( return None; } - let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) else { return None }; + let Ok(code) = self.tcx.sess.source_map().span_to_snippet(span) else { return None }; let sugg = if code.starts_with('(') && code.ends_with(')') { let before_close = span.hi() - BytePos::from_u32(1); @@ -2085,7 +2093,7 @@ fn expected_found_str_term( // Use the terminal width as the basis to determine when to compress the printed // out type, but give ourselves some leeway to avoid ending up creating a file for // a type that is somewhat shorter than the path we'd write to. - let len = self.tcx.sess().diagnostic_width() + 40; + let len = self.tcx.sess.diagnostic_width() + 40; let exp_s = exp.content(); let fnd_s = fnd.content(); if exp_s.len() > len { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs index 7d061e65df80..739628f82065 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -160,7 +160,7 @@ fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { } // The visitor captures the corresponding `hir::Ty` of the anonymous region -// in the case of structs ie. `hir::TyKind::Path`. +// in the case of structs i.e. `hir::TyKind::Path`. // This visitor would be invoked for each lifetime corresponding to a struct, // and would walk the types like Vec in the above example and Ref looking for the HIR // where that lifetime appears. This allows us to highlight the diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 3ed1f7c3481f..b227cd065eab 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -238,7 +238,7 @@ pub fn suggest_new_region_bound( format!("you can use the named lifetime parameter `{name}`") }; spans_suggs.push((fn_return.span.shrink_to_hi(), format!(" + {name} "))); - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!("{declare} `{ty}` {captures}, {use_lt}"), spans_suggs, Applicability::MaybeIncorrect, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs index 5a8dd0364bee..7e6f566e242a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; -use rustc_span::Span; +use rustc_span::{Ident, Span}; use tracing::debug; use crate::error_reporting::infer::nice_region_error::NiceRegionError; @@ -99,7 +99,8 @@ fn visit_region(&mut self, r: ty::Region<'tcx>) { // Get the span of all the used type parameters in the method. let assoc_item = self.tcx().associated_item(trait_item_def_id); - let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] }; + let mut visitor = + TypeParamSpanVisitor { tcx: self.tcx(), types: vec![], elided_lifetime_paths: vec![] }; match assoc_item.kind { ty::AssocKind::Fn { .. } => { if let Some(hir_id) = @@ -122,13 +123,49 @@ fn visit_region(&mut self, r: ty::Region<'tcx>) { found, }; - self.tcx().dcx().emit_err(diag) + let mut diag = self.tcx().dcx().create_err(diag); + // A limit not to make diag verbose. + const ELIDED_LIFETIME_NOTE_LIMIT: usize = 5; + let elided_lifetime_paths = visitor.elided_lifetime_paths; + let total_elided_lifetime_paths = elided_lifetime_paths.len(); + let shown_elided_lifetime_paths = if tcx.sess.opts.verbose { + total_elided_lifetime_paths + } else { + ELIDED_LIFETIME_NOTE_LIMIT + }; + + for elided in elided_lifetime_paths.into_iter().take(shown_elided_lifetime_paths) { + diag.span_note( + elided.span, + format!("`{}` here is elided as `{}`", elided.ident, elided.shorthand), + ); + } + if total_elided_lifetime_paths > shown_elided_lifetime_paths { + diag.note(format!( + "and {} more elided lifetime{} in type paths", + total_elided_lifetime_paths - shown_elided_lifetime_paths, + if total_elided_lifetime_paths - shown_elided_lifetime_paths == 1 { + "" + } else { + "s" + }, + )); + } + diag.emit() } } +#[derive(Clone)] +struct ElidedLifetimeInPath { + span: Span, + ident: Ident, + shorthand: String, +} + struct TypeParamSpanVisitor<'tcx> { tcx: TyCtxt<'tcx>, types: Vec, + elided_lifetime_paths: Vec, } impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { @@ -138,6 +175,83 @@ fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { self.tcx } + fn visit_qpath(&mut self, qpath: &'tcx hir::QPath<'tcx>, id: hir::HirId, _span: Span) { + fn record_elided_lifetimes( + tcx: TyCtxt<'_>, + elided_lifetime_paths: &mut Vec, + segment: &hir::PathSegment<'_>, + ) { + let Some(args) = segment.args else { return }; + if args.parenthesized != hir::GenericArgsParentheses::No { + // Our diagnostic rendering below uses `<...>` syntax; skip cases like `Fn(..) -> ..`. + return; + } + let elided_count = args + .args + .iter() + .filter(|arg| { + let hir::GenericArg::Lifetime(l) = arg else { return false }; + l.syntax == hir::LifetimeSyntax::Implicit + && matches!(l.source, hir::LifetimeSource::Path { .. }) + }) + .count(); + if elided_count == 0 + || elided_lifetime_paths.iter().any(|p| p.span == segment.ident.span) + { + return; + } + + let sm = tcx.sess.source_map(); + let mut parts = args + .args + .iter() + .map(|arg| match arg { + hir::GenericArg::Lifetime(l) => { + if l.syntax == hir::LifetimeSyntax::Implicit + && matches!(l.source, hir::LifetimeSource::Path { .. }) + { + "'_".to_string() + } else { + sm.span_to_snippet(l.ident.span) + .unwrap_or_else(|_| format!("'{}", l.ident.name)) + } + } + hir::GenericArg::Type(ty) => { + sm.span_to_snippet(ty.span).unwrap_or_else(|_| "..".to_string()) + } + hir::GenericArg::Const(ct) => { + sm.span_to_snippet(ct.span).unwrap_or_else(|_| "..".to_string()) + } + hir::GenericArg::Infer(_) => "_".to_string(), + }) + .collect::>(); + parts.extend(args.constraints.iter().map(|constraint| { + sm.span_to_snippet(constraint.span) + .unwrap_or_else(|_| format!("{} = ..", constraint.ident)) + })); + let shorthand = format!("{}<{}>", segment.ident, parts.join(", ")); + + elided_lifetime_paths.push(ElidedLifetimeInPath { + span: segment.ident.span, + ident: segment.ident, + shorthand, + }); + } + + match qpath { + hir::QPath::Resolved(_, path) => { + for segment in path.segments { + record_elided_lifetimes(self.tcx, &mut self.elided_lifetime_paths, segment); + } + } + hir::QPath::TypeRelative(_, segment) => { + record_elided_lifetimes(self.tcx, &mut self.elided_lifetime_paths, segment); + } + } + + hir::intravisit::walk_qpath(self, qpath, id); + } + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) { match arg.kind { hir::TyKind::Ref(_, ref mut_ty) => { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index b0b858aa270c..adb2d2c185ca 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -1,7 +1,6 @@ use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; use rustc_errors::{Diag, MultiSpan, pluralize}; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::find_attr; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; @@ -358,9 +357,9 @@ fn foo(&self, x: T) -> T { x } tcx.def_kind(body_owner_def_id), DefKind::Fn | DefKind::Static { .. } - | DefKind::Const + | DefKind::Const { .. } | DefKind::AssocFn - | DefKind::AssocConst + | DefKind::AssocConst { .. } ) && matches!( tcx.opaque_ty_origin(opaque_ty.def_id), @@ -533,7 +532,7 @@ fn foo(&self, x: T) -> T { x } } } TypeError::TargetFeatureCast(def_id) => { - let target_spans = find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TargetFeature{attr_span: span, was_forced: false, ..} => *span); + let target_spans = find_attr!(tcx, def_id, TargetFeature{attr_span: span, was_forced: false, ..} => *span); diag.note( "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" ); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index fa30ef3af7e1..9a6cc3130340 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::{ self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _, Upcast as _, }; -use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol, kw}; +use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol, kw, sym}; use tracing::{debug, instrument}; use super::ObligationCauseAsDiagArg; @@ -863,7 +863,7 @@ pub fn construct_generic_bound_failure( } if !suggs.is_empty() { - err.multipart_suggestion_verbose( + err.multipart_suggestion( msg, suggs, Applicability::MaybeIncorrect, // Issue #41966 @@ -1431,9 +1431,9 @@ fn suggest_precise_capturing<'tcx>( }); } else { let mut next_fresh_param = || { - ["T", "U", "V", "W", "X", "Y", "A", "B", "C"] + ['T', 'U', 'V', 'W', 'X', 'Y', 'A', 'B', 'C'] .into_iter() - .map(Symbol::intern) + .map(sym::character) .chain((0..).map(|i| Symbol::intern(&format!("T{i}")))) .find(|s| captured_non_lifetimes.insert(*s)) .unwrap() diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 16c4ac68cdf0..2afc1b040353 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -185,9 +185,9 @@ pub(super) fn maybe_report_ambiguity( return e; } - if let Err(guar) = self.tcx.ensure_ok().coherent_trait(trait_pred.def_id()) { - // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case - // other `Foo` impls are incoherent. + if let Err(guar) = self.tcx.ensure_result().coherent_trait(trait_pred.def_id()) { + // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for + // Foo` in case other `Foo` impls are incoherent. return guar; } @@ -362,7 +362,7 @@ pub(super) fn maybe_report_ambiguity( && self.tcx.trait_of_assoc(*item_id) == Some(*trait_id) && let None = self.tainted_by_errors() { - let assoc_item = self.tcx.associated_item(item_id); + let assoc_item = self.tcx.associated_item(*item_id); let (verb, noun) = match assoc_item.kind { ty::AssocKind::Const { .. } => ("refer to the", "constant"), ty::AssocKind::Fn { .. } => ("call", "function"), @@ -394,7 +394,7 @@ pub(super) fn maybe_report_ambiguity( let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id); - if let Some(impl_def_id) = + if let Some(&impl_def_id) = trait_impls.non_blanket_impls().values().flatten().next() { let non_blanket_impl_count = @@ -418,7 +418,7 @@ pub(super) fn maybe_report_ambiguity( .non_blanket_impls() .values() .flatten() - .map(|id| { + .map(|&id| { format!( "{}", self.tcx.type_of(id).instantiate_identity() @@ -522,7 +522,7 @@ pub(super) fn maybe_report_ambiguity( if let Err(guar) = self .tcx - .ensure_ok() + .ensure_result() .coherent_trait(self.tcx.parent(data.projection_term.def_id)) { // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d9c6d339328a..364152475e94 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -14,9 +14,10 @@ Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions, msg, pluralize, struct_span_code_err, }; +use rustc_hir::attrs::diagnostic::{AppendConstMessage, OnUnimplementedNote}; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, LangItem, Node}; +use rustc_hir::{self as hir, LangItem, Node, find_attr}; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_infer::traits::ImplSource; use rustc_infer::traits::solve::Goal; @@ -37,14 +38,12 @@ use rustc_span::{BytePos, DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym}; use tracing::{debug, instrument}; -use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote}; use super::suggestions::get_explanation_based_on_obligation; use super::{ ArgKind, CandidateSimilarity, FindExprBySpan, GetSafeTransmuteErrorAndReason, ImplCandidate, }; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::TyCategory; -use crate::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; use crate::error_reporting::traits::report_dyn_incompatibility; use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, CoroClosureNotFn}; use crate::infer::{self, InferCtxt, InferCtxtExt as _}; @@ -254,9 +253,16 @@ pub fn report_selection_error( LangItem::TransmuteTrait, ) { // Recompute the safe transmute reason and use that for the error reporting + let (report_obligation, report_pred) = + self.select_transmute_obligation_for_reporting( + &obligation, + main_trait_predicate, + root_obligation, + ); + match self.get_safe_transmute_error_and_reason( - obligation.clone(), - main_trait_predicate, + report_obligation, + report_pred, span, ) { GetSafeTransmuteErrorAndReason::Silent => { @@ -282,23 +288,31 @@ pub fn report_selection_error( if self.tcx.is_diagnostic_item(sym::From, trait_def_id) || self.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id) { - let found_ty = leaf_trait_predicate.skip_binder().trait_ref.args.type_at(1); - let ty = main_trait_predicate.skip_binder().self_ty(); - if let Some(cast_ty) = self.find_explicit_cast_type( - obligation.param_env, - found_ty, - ty, - ) { - let found_ty_str = self.tcx.short_string(found_ty, &mut long_ty_file); - let cast_ty_str = self.tcx.short_string(cast_ty, &mut long_ty_file); - err.help( - format!( + let trait_ref = leaf_trait_predicate.skip_binder().trait_ref; + + // Defensive: next-solver may produce fewer args than expected. + if trait_ref.args.len() > 1 { + let found_ty = trait_ref.args.type_at(1); + let ty = main_trait_predicate.skip_binder().self_ty(); + + if let Some(cast_ty) = self.find_explicit_cast_type( + obligation.param_env, + found_ty, + ty, + ) { + let found_ty_str = + self.tcx.short_string(found_ty, &mut long_ty_file); + let cast_ty_str = + self.tcx.short_string(cast_ty, &mut long_ty_file); + + err.help(format!( "consider casting the `{found_ty_str}` value to `{cast_ty_str}`", - ), - ); + )); + } } } + *err.long_ty_path() = long_ty_file; let mut suggested = false; @@ -377,7 +391,7 @@ pub fn report_selection_error( if let Some(s) = label { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! - err.span_label(span, s); + err.span_label(span, s.as_str().to_owned()); if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point @@ -897,11 +911,9 @@ fn report_host_effect_error( diag.long_ty_path(), ); - if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, impl_did) - { - let note = command.evaluate( - self.tcx, - predicate.skip_binder().trait_ref, + if let Some(command) = find_attr!(self.tcx, impl_did, OnConst {directive, ..} => directive.as_deref()).flatten(){ + let note = command.evaluate_directive( + predicate.skip_binder().trait_ref, &condition_options, &format_args, ); @@ -1784,7 +1796,7 @@ fn maybe_detailed_projection_msg( with_forced_trimmed_paths! { if self.tcx.is_lang_item(projection_term.def_id, LangItem::FnOnceOutput) { - let (span, closure_span) = if let ty::Closure(def_id, _) = self_ty.kind() { + let (span, closure_span) = if let ty::Closure(def_id, _) = *self_ty.kind() { let def_span = self.tcx.def_span(def_id); if let Some(local_def_id) = def_id.as_local() && let node = self.tcx.hir_node_by_def_id(local_def_id) @@ -2290,7 +2302,7 @@ pub(super) fn report_similar_impl_candidates( }; if candidates.len() < 5 { let spans: Vec<_> = - candidates.iter().map(|(_, def_id)| self.tcx.def_span(def_id)).collect(); + candidates.iter().map(|&(_, def_id)| self.tcx.def_span(def_id)).collect(); let mut span: MultiSpan = spans.into(); for (c, def_id) in &candidates { let msg = if all_traits_equal { @@ -2302,7 +2314,7 @@ pub(super) fn report_similar_impl_candidates( self.tcx.short_string(c.print_only_trait_path(), err.long_ty_path()), ) }; - span.push_span_label(self.tcx.def_span(def_id), msg); + span.push_span_label(self.tcx.def_span(*def_id), msg); } err.span_help( span, @@ -2616,16 +2628,18 @@ fn check_same_name_different_path( ) }; let trait_name = self.tcx.item_name(trait_def_id); - if let Some(other_trait_def_id) = self.tcx.all_traits_including_private().find(|def_id| { - trait_def_id != *def_id + if let Some(other_trait_def_id) = self.tcx.all_traits_including_private().find(|&def_id| { + trait_def_id != def_id && trait_name == self.tcx.item_name(def_id) - && trait_has_same_params(*def_id) + && trait_has_same_params(def_id) + // `PointeeSized` is removed during lowering. + && !self.tcx.is_lang_item(def_id, LangItem::PointeeSized) && self.predicate_must_hold_modulo_regions(&Obligation::new( self.tcx, obligation.cause.clone(), obligation.param_env, trait_pred.map_bound(|tr| ty::TraitPredicate { - trait_ref: ty::TraitRef::new(self.tcx, *def_id, tr.trait_ref.args), + trait_ref: ty::TraitRef::new(self.tcx, def_id, tr.trait_ref.args), ..tr }), )) @@ -2817,6 +2831,51 @@ fn get_standard_error_message( }) } + fn select_transmute_obligation_for_reporting( + &self, + obligation: &PredicateObligation<'tcx>, + trait_predicate: ty::PolyTraitPredicate<'tcx>, + root_obligation: &PredicateObligation<'tcx>, + ) -> (PredicateObligation<'tcx>, ty::PolyTraitPredicate<'tcx>) { + let ocx = ObligationCtxt::new(self); + let normalized_predicate = self.tcx.erase_and_anonymize_regions( + self.tcx.instantiate_bound_regions_with_erased(trait_predicate), + ); + let trait_ref = normalized_predicate.trait_ref; + + let Ok(assume) = ocx.structurally_normalize_const( + &obligation.cause, + obligation.param_env, + trait_ref.args.const_at(2), + ) else { + return (obligation.clone(), trait_predicate); + }; + + let Some(assume) = rustc_transmute::Assume::from_const(self.tcx, assume) else { + return (obligation.clone(), trait_predicate); + }; + + let is_normalized_yes = matches!( + rustc_transmute::TransmuteTypeEnv::new(self.tcx).is_transmutable( + trait_ref.args.type_at(1), + trait_ref.args.type_at(0), + assume, + ), + rustc_transmute::Answer::Yes, + ); + + // If the normalized check unexpectedly passes, fall back to root obligation for reporting. + if is_normalized_yes + && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(root_pred)) = + root_obligation.predicate.kind().skip_binder() + && root_pred.def_id() == trait_predicate.def_id() + { + return (root_obligation.clone(), root_obligation.predicate.kind().rebind(root_pred)); + } + + (obligation.clone(), trait_predicate) + } + fn get_safe_transmute_error_and_reason( &self, obligation: PredicateObligation<'tcx>, @@ -3231,12 +3290,14 @@ fn report_closure_error( err.fn_once_label = Some(ClosureFnOnceLabel { span: *span, place: ty::place_to_string_for_capture(self.tcx, place), + trait_prefix, }) } (ty::ClosureKind::FnMut, Some((span, place))) => { err.fn_mut_label = Some(ClosureFnMutLabel { span: *span, place: ty::place_to_string_for_capture(self.tcx, place), + trait_prefix, }) } _ => {} @@ -3254,7 +3315,7 @@ fn report_cyclic_signature_error( terr: TypeError<'tcx>, ) -> Diag<'a> { let self_ty = found_trait_ref.self_ty(); - let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() { + let (cause, terr) = if let ty::Closure(def_id, _) = *self_ty.kind() { ( ObligationCause::dummy_with_span(self.tcx.def_span(def_id)), TypeError::CyclicTy(self_ty), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index a7a685d62b34..bda0c4fa2c6f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -2,8 +2,6 @@ pub mod call_kind; pub mod fulfillment_errors; pub mod on_unimplemented; -pub mod on_unimplemented_condition; -pub mod on_unimplemented_format; mod overflow; pub mod suggestions; @@ -373,12 +371,12 @@ fn extern_crates_with_the_same_name( self.tcx.extern_crate(trait_def_id.krate), ) { ( - Some(ExternCrate { + Some(&ExternCrate { src: ExternCrateSource::Extern(expected_def_id), dependency_of: LOCAL_CRATE, .. }), - Some(ExternCrate { + Some(&ExternCrate { src: ExternCrateSource::Extern(trait_def_id), dependency_of: LOCAL_CRATE, .. @@ -402,9 +400,9 @@ pub fn check_same_definition_different_crate( let krate = self.tcx.crate_name(expected_did.krate); let name = self.tcx.item_name(expected_did); let definitions_with_same_path: UnordSet<_> = found_dids - .filter(|def_id| { + .filter(|&def_id| { def_id.krate != expected_did.krate - && (self.extern_crates_with_the_same_name(expected_did, *def_id) + && (self.extern_crates_with_the_same_name(expected_did, def_id) || self.tcx.crate_name(def_id.krate) == krate) && self.tcx.item_name(def_id) == name }) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index a98f952d55a3..d08e0fa3521b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -1,81 +1,18 @@ -use std::iter; use std::path::PathBuf; -use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit}; -use rustc_errors::codes::*; -use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{AttrArgs, Attribute}; -use rustc_macros::LintDiagnostic; -use rustc_middle::bug; +use rustc_hir::attrs::diagnostic::{ConditionOptions, FormatArgs, OnUnimplementedNote}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::find_attr; +pub use rustc_hir::lints::FormatWarning; use rustc_middle::ty::print::PrintTraitRefExt; -use rustc_middle::ty::{self, GenericArgsRef, GenericParamDef, GenericParamDefKind, TyCtxt}; -use rustc_session::lint::builtin::{ - MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, -}; -use rustc_span::{Span, Symbol, sym}; -use tracing::{debug, info}; +use rustc_middle::ty::{self, GenericParamDef, GenericParamDefKind}; +use rustc_span::Symbol; use super::{ObligationCauseCode, PredicateObligation}; use crate::error_reporting::TypeErrCtxt; -use crate::error_reporting::traits::on_unimplemented_condition::{ - ConditionOptions, OnUnimplementedCondition, -}; -use crate::error_reporting::traits::on_unimplemented_format::{ - Ctx, FormatArgs, FormatString, FormatWarning, -}; -use crate::errors::{InvalidOnClause, NoValueInOnUnimplemented}; -use crate::infer::InferCtxtExt; impl<'tcx> TypeErrCtxt<'_, 'tcx> { - fn impl_similar_to( - &self, - trait_pred: ty::PolyTraitPredicate<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) -> Option<(DefId, GenericArgsRef<'tcx>)> { - let tcx = self.tcx; - let param_env = obligation.param_env; - self.enter_forall(trait_pred, |trait_pred| { - let trait_self_ty = trait_pred.self_ty(); - - let mut self_match_impls = vec![]; - let mut fuzzy_match_impls = vec![]; - - self.tcx.for_each_relevant_impl(trait_pred.def_id(), trait_self_ty, |def_id| { - let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id); - let impl_trait_ref = tcx.impl_trait_ref(def_id).instantiate(tcx, impl_args); - - let impl_self_ty = impl_trait_ref.self_ty(); - - if self.can_eq(param_env, trait_self_ty, impl_self_ty) { - self_match_impls.push((def_id, impl_args)); - - if iter::zip( - trait_pred.trait_ref.args.types().skip(1), - impl_trait_ref.args.types().skip(1), - ) - .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some()) - { - fuzzy_match_impls.push((def_id, impl_args)); - } - } - }); - - let impl_def_id_and_args = if let [impl_] = self_match_impls[..] { - impl_ - } else if let [impl_] = fuzzy_match_impls[..] { - impl_ - } else { - return None; - }; - - tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented) - .then_some(impl_def_id_and_args) - }) - } - /// Used to set on_unimplemented's `ItemContext` /// to be the enclosing (async) block/function/closure fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str> { @@ -106,10 +43,8 @@ pub fn on_unimplemented_note( } let (condition_options, format_args) = self.on_unimplemented_components(trait_pred, obligation, long_ty_path); - if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, trait_pred.def_id()) - { - command.evaluate( - self.tcx, + if let Some(command) = find_attr!(self.tcx, trait_pred.def_id(), OnUnimplemented {directive, ..} => directive.as_deref()).flatten() { + command.evaluate_directive( trait_pred.skip_binder().trait_ref, &condition_options, &format_args, @@ -124,10 +59,8 @@ pub(crate) fn on_unimplemented_components( trait_pred: ty::PolyTraitPredicate<'tcx>, obligation: &PredicateObligation<'tcx>, long_ty_path: &mut Option, - ) -> (ConditionOptions, FormatArgs<'tcx>) { - let (def_id, args) = self - .impl_similar_to(trait_pred, obligation) - .unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args)); + ) -> (ConditionOptions, FormatArgs) { + let (def_id, args) = (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args); let trait_pred = trait_pred.skip_binder(); let mut self_types = vec![]; @@ -206,7 +139,7 @@ pub(crate) fn on_unimplemented_components( if self_ty.is_fn() { let fn_sig = self_ty.fn_sig(self.tcx); - let shortname = if let ty::FnDef(def_id, _) = self_ty.kind() + let shortname = if let ty::FnDef(def_id, _) = *self_ty.kind() && self.tcx.codegen_fn_attrs(def_id).safe_target_features { "#[target_feature] fn" @@ -275,7 +208,7 @@ pub(crate) fn on_unimplemented_components( })); let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id); - let trait_sugared = trait_pred.trait_ref.print_trait_sugared(); + let trait_sugared = trait_pred.trait_ref.print_trait_sugared().to_string(); let condition_options = ConditionOptions { self_types, @@ -317,614 +250,3 @@ pub(crate) fn on_unimplemented_components( (condition_options, format_args) } } - -/// Represents a format string in a on_unimplemented attribute, -/// like the "content" in `#[diagnostic::on_unimplemented(message = "content")]` -#[derive(Clone, Debug)] -pub struct OnUnimplementedFormatString { - /// Symbol of the format string, i.e. `"content"` - symbol: Symbol, - /// The span of the format string, i.e. `"content"` - span: Span, - is_diagnostic_namespace_variant: bool, -} - -#[derive(Debug)] -pub struct OnUnimplementedDirective { - condition: Option, - subcommands: Vec, - message: Option<(Span, OnUnimplementedFormatString)>, - label: Option<(Span, OnUnimplementedFormatString)>, - notes: Vec, - parent_label: Option, - append_const_msg: Option, -} - -/// For the `#[rustc_on_unimplemented]` attribute -#[derive(Default, Debug)] -pub struct OnUnimplementedNote { - pub message: Option, - pub label: Option, - pub notes: Vec, - pub parent_label: Option, - // If none, should fall back to a generic message - pub append_const_msg: Option, -} - -/// Append a message for `[const] Trait` errors. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] -pub enum AppendConstMessage { - #[default] - Default, - Custom(Symbol, Span), -} - -#[derive(LintDiagnostic)] -#[diag("malformed `on_unimplemented` attribute")] -#[help("only `message`, `note` and `label` are allowed as options")] -pub struct MalformedOnUnimplementedAttrLint { - #[label("invalid option found here")] - pub span: Span, -} - -impl MalformedOnUnimplementedAttrLint { - pub fn new(span: Span) -> Self { - Self { span } - } -} - -#[derive(LintDiagnostic)] -#[diag("missing options for `on_unimplemented` attribute")] -#[help("at least one of the `message`, `note` and `label` options are expected")] -pub struct MissingOptionsForOnUnimplementedAttr; - -#[derive(LintDiagnostic)] -#[diag("`{$option_name}` is ignored due to previous definition of `{$option_name}`")] -pub struct IgnoredDiagnosticOption { - pub option_name: &'static str, - #[label("`{$option_name}` is already declared here")] - pub span: Span, - #[label("`{$option_name}` is first declared here")] - pub prev_span: Span, -} - -impl IgnoredDiagnosticOption { - pub fn maybe_emit_warning<'tcx>( - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - new: Option, - old: Option, - option_name: &'static str, - ) { - if let (Some(new_item), Some(old_item)) = (new, old) - && let Some(item_def_id) = item_def_id.as_local() - { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - new_item, - IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name }, - ); - } - } -} - -#[derive(LintDiagnostic)] -#[diag("{$description}")] -pub struct WrappedParserError { - pub description: String, - pub label: String, -} - -impl<'tcx> OnUnimplementedDirective { - fn parse( - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - items: &[MetaItemInner], - span: Span, - is_root: bool, - is_diagnostic_namespace_variant: bool, - ) -> Result, ErrorGuaranteed> { - let mut errored = None; - let mut item_iter = items.iter(); - - let parse_value = |value_str, span| { - OnUnimplementedFormatString::try_parse( - tcx, - item_def_id, - value_str, - span, - is_diagnostic_namespace_variant, - ) - .map(Some) - }; - - let condition = if is_root { - None - } else { - let cond = item_iter - .next() - .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClause::Empty { span }))?; - - let generics: Vec = tcx - .generics_of(item_def_id) - .own_params - .iter() - .filter_map(|param| { - if matches!(param.kind, GenericParamDefKind::Lifetime) { - None - } else { - Some(param.name) - } - }) - .collect(); - match OnUnimplementedCondition::parse(cond, &generics) { - Ok(condition) => Some(condition), - Err(e) => return Err(tcx.dcx().emit_err(e)), - } - }; - - let mut message = None; - let mut label = None; - let mut notes = Vec::new(); - let mut parent_label = None; - let mut subcommands = vec![]; - let mut append_const_msg = None; - - let get_value_and_span = |item: &_, key| { - if let MetaItemInner::MetaItem(MetaItem { - path, - kind: MetaItemKind::NameValue(MetaItemLit { span, kind: LitKind::Str(s, _), .. }), - .. - }) = item - && *path == key - { - Some((*s, *span)) - } else { - None - } - }; - - for item in item_iter { - if let Some((message_, span)) = get_value_and_span(item, sym::message) - && message.is_none() - { - message = parse_value(message_, span)?.map(|l| (item.span(), l)); - continue; - } else if let Some((label_, span)) = get_value_and_span(item, sym::label) - && label.is_none() - { - label = parse_value(label_, span)?.map(|l| (item.span(), l)); - continue; - } else if let Some((note_, span)) = get_value_and_span(item, sym::note) { - if let Some(note) = parse_value(note_, span)? { - notes.push(note); - continue; - } - } else if item.has_name(sym::parent_label) - && parent_label.is_none() - && !is_diagnostic_namespace_variant - { - if let Some(parent_label_) = item.value_str() { - parent_label = parse_value(parent_label_, item.span())?; - continue; - } - } else if item.has_name(sym::on) - && is_root - && message.is_none() - && label.is_none() - && notes.is_empty() - && !is_diagnostic_namespace_variant - // FIXME(diagnostic_namespace): disallow filters for now - { - if let Some(items) = item.meta_item_list() { - match Self::parse( - tcx, - item_def_id, - items, - item.span(), - false, - is_diagnostic_namespace_variant, - ) { - Ok(Some(subcommand)) => subcommands.push(subcommand), - Ok(None) => bug!( - "This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false" - ), - Err(reported) => errored = Some(reported), - }; - continue; - } - } else if item.has_name(sym::append_const_msg) - && append_const_msg.is_none() - && !is_diagnostic_namespace_variant - { - if let Some(msg) = item.value_str() { - append_const_msg = Some(AppendConstMessage::Custom(msg, item.span())); - continue; - } else if item.is_word() { - append_const_msg = Some(AppendConstMessage::Default); - continue; - } - } - - if is_diagnostic_namespace_variant { - if let Some(def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(def_id), - vec![item.span()], - MalformedOnUnimplementedAttrLint::new(item.span()), - ); - } - } else { - // nothing found - tcx.dcx().emit_err(NoValueInOnUnimplemented { span: item.span() }); - } - } - - if let Some(reported) = errored { - if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) } - } else { - Ok(Some(OnUnimplementedDirective { - condition, - subcommands, - message, - label, - notes, - parent_label, - append_const_msg, - })) - } - } - - pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result, ErrorGuaranteed> { - let attr = if tcx.is_trait(item_def_id) { - sym::on_unimplemented - } else if let DefKind::Impl { of_trait: true } = tcx.def_kind(item_def_id) { - sym::on_const - } else { - // It could be a trait_alias (`trait MyTrait = SomeOtherTrait`) - // or an implementation (`impl MyTrait for Foo {}`) - // - // We don't support those. - return Ok(None); - }; - if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) { - return Self::parse_attribute(attr, false, tcx, item_def_id); - } else { - tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, attr]) - .filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose()) - .try_fold(None, |aggr: Option, directive| { - let directive = directive?; - if let Some(aggr) = aggr { - let mut subcommands = aggr.subcommands; - subcommands.extend(directive.subcommands); - let mut notes = aggr.notes; - notes.extend(directive.notes); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.message.as_ref().map(|f| f.0), - aggr.message.as_ref().map(|f| f.0), - "message", - ); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.label.as_ref().map(|f| f.0), - aggr.label.as_ref().map(|f| f.0), - "label", - ); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.condition.as_ref().map(|i| i.span()), - aggr.condition.as_ref().map(|i| i.span()), - "condition", - ); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.parent_label.as_ref().map(|f| f.span), - aggr.parent_label.as_ref().map(|f| f.span), - "parent_label", - ); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.append_const_msg.as_ref().and_then(|c| { - if let AppendConstMessage::Custom(_, s) = c { - Some(*s) - } else { - None - } - }), - aggr.append_const_msg.as_ref().and_then(|c| { - if let AppendConstMessage::Custom(_, s) = c { - Some(*s) - } else { - None - } - }), - "append_const_msg", - ); - - Ok(Some(Self { - condition: aggr.condition.or(directive.condition), - subcommands, - message: aggr.message.or(directive.message), - label: aggr.label.or(directive.label), - notes, - parent_label: aggr.parent_label.or(directive.parent_label), - append_const_msg: aggr.append_const_msg.or(directive.append_const_msg), - })) - } else { - Ok(Some(directive)) - } - }) - } - } - - fn parse_attribute( - attr: &Attribute, - is_diagnostic_namespace_variant: bool, - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - ) -> Result, ErrorGuaranteed> { - let result = if let Some(items) = attr.meta_item_list() { - Self::parse( - tcx, - item_def_id, - &items, - attr.span(), - true, - is_diagnostic_namespace_variant, - ) - } else if let Some(value) = attr.value_str() { - if !is_diagnostic_namespace_variant { - Ok(Some(OnUnimplementedDirective { - condition: None, - message: None, - subcommands: vec![], - label: Some(( - attr.span(), - OnUnimplementedFormatString::try_parse( - tcx, - item_def_id, - value, - attr.value_span().unwrap_or(attr.span()), - is_diagnostic_namespace_variant, - )?, - )), - notes: Vec::new(), - parent_label: None, - append_const_msg: None, - })) - } else { - let item = attr.get_normal_item(); - let report_span = match &item.args { - AttrArgs::Empty => item.path.span, - AttrArgs::Delimited(args) => args.dspan.entire(), - AttrArgs::Eq { eq_span, expr } => eq_span.to(expr.span), - }; - - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - report_span, - MalformedOnUnimplementedAttrLint::new(report_span), - ); - } - Ok(None) - } - } else if is_diagnostic_namespace_variant { - match attr { - Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - attr.span(), - MalformedOnUnimplementedAttrLint::new(attr.span()), - ); - } - } - _ => { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - attr.span(), - MissingOptionsForOnUnimplementedAttr, - ) - } - } - }; - - Ok(None) - } else { - let reported = tcx.dcx().delayed_bug("of_item: neither meta_item_list nor value_str"); - return Err(reported); - }; - debug!("of_item({:?}) = {:?}", item_def_id, result); - result - } - - pub(crate) fn evaluate( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - condition_options: &ConditionOptions, - args: &FormatArgs<'tcx>, - ) -> OnUnimplementedNote { - let mut message = None; - let mut label = None; - let mut notes = Vec::new(); - let mut parent_label = None; - let mut append_const_msg = None; - info!( - "evaluate({:?}, trait_ref={:?}, options={:?}, args ={:?})", - self, trait_ref, condition_options, args - ); - - for command in self.subcommands.iter().chain(Some(self)).rev() { - debug!(?command); - if let Some(ref condition) = command.condition - && !condition.matches_predicate(condition_options) - { - debug!("evaluate: skipping {:?} due to condition", command); - continue; - } - debug!("evaluate: {:?} succeeded", command); - if let Some(ref message_) = command.message { - message = Some(message_.clone()); - } - - if let Some(ref label_) = command.label { - label = Some(label_.clone()); - } - - notes.extend(command.notes.clone()); - - if let Some(ref parent_label_) = command.parent_label { - parent_label = Some(parent_label_.clone()); - } - - append_const_msg = command.append_const_msg; - } - - OnUnimplementedNote { - label: label.map(|l| l.1.format(tcx, trait_ref, args)), - message: message.map(|m| m.1.format(tcx, trait_ref, args)), - notes: notes.into_iter().map(|n| n.format(tcx, trait_ref, args)).collect(), - parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, args)), - append_const_msg, - } - } -} - -impl<'tcx> OnUnimplementedFormatString { - fn try_parse( - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - from: Symbol, - span: Span, - is_diagnostic_namespace_variant: bool, - ) -> Result { - let result = - OnUnimplementedFormatString { symbol: from, span, is_diagnostic_namespace_variant }; - result.verify(tcx, item_def_id)?; - Ok(result) - } - - fn verify(&self, tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> { - if !tcx.is_trait(trait_def_id) { - return Ok(()); - }; - - let ctx = if self.is_diagnostic_namespace_variant { - Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id } - } else { - Ctx::RustcOnUnimplemented { tcx, trait_def_id } - }; - - let mut result = Ok(()); - - let snippet = tcx.sess.source_map().span_to_snippet(self.span).ok(); - match FormatString::parse(self.symbol, snippet, self.span, &ctx) { - // Warnings about format specifiers, deprecated parameters, wrong parameters etc. - // In other words we'd like to let the author know, but we can still try to format the string later - Ok(FormatString { warnings, .. }) => { - if self.is_diagnostic_namespace_variant { - for w in warnings { - w.emit_warning(tcx, trait_def_id) - } - } else { - for w in warnings { - match w { - FormatWarning::UnknownParam { argument_name, span } => { - let reported = struct_span_code_err!( - tcx.dcx(), - span, - E0230, - "cannot find parameter {} on this trait", - argument_name, - ) - .emit(); - result = Err(reported); - } - FormatWarning::PositionalArgument { span, .. } => { - let reported = struct_span_code_err!( - tcx.dcx(), - span, - E0231, - "positional format arguments are not allowed here" - ) - .emit(); - result = Err(reported); - } - FormatWarning::InvalidSpecifier { .. } - | FormatWarning::FutureIncompat { .. } => {} - } - } - } - } - // Error from the underlying `rustc_parse_format::Parser` - Err(e) => { - // we cannot return errors from processing the format string as hard error here - // as the diagnostic namespace guarantees that malformed input cannot cause an error - // - // if we encounter any error while processing we nevertheless want to show it as warning - // so that users are aware that something is not correct - if self.is_diagnostic_namespace_variant { - if let Some(trait_def_id) = trait_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - tcx.local_def_id_to_hir_id(trait_def_id), - self.span, - WrappedParserError { description: e.description, label: e.label }, - ); - } - } else { - let reported = - struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description) - .emit(); - result = Err(reported); - } - } - } - - result - } - - pub fn format( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - args: &FormatArgs<'tcx>, - ) -> String { - let trait_def_id = trait_ref.def_id; - let ctx = if self.is_diagnostic_namespace_variant { - Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id } - } else { - Ctx::RustcOnUnimplemented { tcx, trait_def_id } - }; - - // No point passing a snippet here, we already did that in `verify` - if let Ok(s) = FormatString::parse(self.symbol, None, self.span, &ctx) { - s.format(args) - } else { - // we cannot return errors from processing the format string as hard error here - // as the diagnostic namespace guarantees that malformed input cannot cause an error - // - // if we encounter any error while processing the format string - // we don't want to show the potentially half assembled formatted string, - // therefore we fall back to just showing the input string in this case - // - // The actual parser errors are emitted earlier - // as lint warnings in OnUnimplementedFormatString::verify - self.symbol.as_str().into() - } - } -} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs deleted file mode 100644 index 171d05230d46..000000000000 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs +++ /dev/null @@ -1,322 +0,0 @@ -use rustc_ast::{MetaItemInner, MetaItemKind, MetaItemLit}; -use rustc_parse_format::{ParseMode, Parser, Piece, Position}; -use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym}; - -use crate::errors::InvalidOnClause; - -/// Represents the `on` filter in `#[rustc_on_unimplemented]`. -#[derive(Debug)] -pub(crate) struct OnUnimplementedCondition { - span: Span, - pred: Predicate, -} - -impl OnUnimplementedCondition { - pub(crate) fn span(&self) -> Span { - self.span - } - - pub(crate) fn matches_predicate(&self, options: &ConditionOptions) -> bool { - self.pred.eval(&mut |p| match p { - FlagOrNv::Flag(b) => options.has_flag(*b), - FlagOrNv::NameValue(NameValue { name, value }) => { - let value = value.format(&options.generic_args); - options.contains(*name, value) - } - }) - } - - pub(crate) fn parse( - input: &MetaItemInner, - generics: &[Symbol], - ) -> Result { - let span = input.span(); - let pred = Predicate::parse(input, generics)?; - Ok(OnUnimplementedCondition { span, pred }) - } -} - -/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`OnUnimplementedCondition`]. -/// -/// It is similar to the predicate in the `cfg` attribute, -/// and may contain nested predicates. -#[derive(Debug)] -enum Predicate { - /// A condition like `on(crate_local)`. - Flag(Flag), - /// A match, like `on(Rhs = "Whatever")`. - Match(NameValue), - /// Negation, like `on(not($pred))`. - Not(Box), - /// True if all predicates are true, like `on(all($a, $b, $c))`. - All(Vec), - /// True if any predicate is true, like `on(any($a, $b, $c))`. - Any(Vec), -} - -impl Predicate { - fn parse(input: &MetaItemInner, generics: &[Symbol]) -> Result { - let meta_item = match input { - MetaItemInner::MetaItem(meta_item) => meta_item, - MetaItemInner::Lit(lit) => { - return Err(InvalidOnClause::UnsupportedLiteral { span: lit.span }); - } - }; - - let Some(predicate) = meta_item.ident() else { - return Err(InvalidOnClause::ExpectedIdentifier { - span: meta_item.path.span, - path: meta_item.path.clone(), - }); - }; - - match meta_item.kind { - MetaItemKind::List(ref mis) => match predicate.name { - sym::any => Ok(Predicate::Any(Predicate::parse_sequence(mis, generics)?)), - sym::all => Ok(Predicate::All(Predicate::parse_sequence(mis, generics)?)), - sym::not => match &**mis { - [one] => Ok(Predicate::Not(Box::new(Predicate::parse(one, generics)?))), - [first, .., last] => Err(InvalidOnClause::ExpectedOnePredInNot { - span: first.span().to(last.span()), - }), - [] => Err(InvalidOnClause::ExpectedOnePredInNot { span: meta_item.span }), - }, - invalid_pred => { - Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred }) - } - }, - MetaItemKind::NameValue(MetaItemLit { symbol, .. }) => { - let name = Name::parse(predicate, generics)?; - let value = FilterFormatString::parse(symbol); - let kv = NameValue { name, value }; - Ok(Predicate::Match(kv)) - } - MetaItemKind::Word => { - let flag = Flag::parse(predicate)?; - Ok(Predicate::Flag(flag)) - } - } - } - - fn parse_sequence( - sequence: &[MetaItemInner], - generics: &[Symbol], - ) -> Result, InvalidOnClause> { - sequence.iter().map(|item| Predicate::parse(item, generics)).collect() - } - - fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool { - match self { - Predicate::Flag(flag) => eval(FlagOrNv::Flag(flag)), - Predicate::Match(nv) => eval(FlagOrNv::NameValue(nv)), - Predicate::Not(not) => !not.eval(eval), - Predicate::All(preds) => preds.into_iter().all(|pred| pred.eval(eval)), - Predicate::Any(preds) => preds.into_iter().any(|pred| pred.eval(eval)), - } - } -} - -/// Represents a `MetaWord` in an `on`-filter. -#[derive(Debug, Clone, Copy)] -enum Flag { - /// Whether the code causing the trait bound to not be fulfilled - /// is part of the user's crate. - CrateLocal, - /// Whether the obligation is user-specified rather than derived. - Direct, - /// Whether we are in some kind of desugaring like - /// `?` or `try { .. }`. - FromDesugaring, -} - -impl Flag { - fn parse(Ident { name, span }: Ident) -> Result { - match name { - sym::crate_local => Ok(Flag::CrateLocal), - sym::direct => Ok(Flag::Direct), - sym::from_desugaring => Ok(Flag::FromDesugaring), - invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }), - } - } -} - -/// A `MetaNameValueStr` in an `on`-filter. -/// -/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. -#[derive(Debug, Clone)] -struct NameValue { - name: Name, - /// Something like `"&str"` or `"alloc::string::String"`, - /// in which case it just contains a single string piece. - /// But if it is something like `"&[{A}]"` then it must be formatted later. - value: FilterFormatString, -} - -/// The valid names of the `on` filter. -#[derive(Debug, Clone, Copy)] -enum Name { - Cause, - FromDesugaring, - SelfUpper, - GenericArg(Symbol), -} - -impl Name { - fn parse(Ident { name, span }: Ident, generics: &[Symbol]) -> Result { - match name { - kw::SelfUpper => Ok(Name::SelfUpper), - sym::from_desugaring => Ok(Name::FromDesugaring), - sym::cause => Ok(Name::Cause), - generic if generics.contains(&generic) => Ok(Name::GenericArg(generic)), - invalid_name => Err(InvalidOnClause::InvalidName { invalid_name, span }), - } - } -} - -#[derive(Debug, Clone)] -enum FlagOrNv<'p> { - Flag(&'p Flag), - NameValue(&'p NameValue), -} - -/// Represents a value inside an `on` filter. -/// -/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. -/// If it is a simple literal like this then `pieces` will be `[LitOrArg::Lit("value")]`. -/// The `Arg` variant is used when it contains formatting like -/// `#[rustc_on_unimplemented(on(Self = "&[{A}]", message = "hello"))]`. -#[derive(Debug, Clone)] -struct FilterFormatString { - pieces: Vec, -} - -#[derive(Debug, Clone)] -enum LitOrArg { - Lit(String), - Arg(String), -} - -impl FilterFormatString { - fn parse(input: Symbol) -> Self { - let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic) - .map(|p| match p { - Piece::Lit(s) => LitOrArg::Lit(s.to_owned()), - // We just ignore formatspecs here - Piece::NextArgument(a) => match a.position { - // In `TypeErrCtxt::on_unimplemented_note` we substitute `"{integral}"` even - // if the integer type has been resolved, to allow targeting all integers. - // `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet, - // from the `Display` impl of `InferTy` to be precise. - // - // Don't try to format these later! - Position::ArgumentNamed(arg @ "integer" | arg @ "integral" | arg @ "float") => { - LitOrArg::Lit(format!("{{{arg}}}")) - } - - // FIXME(mejrs) We should check if these correspond to a generic of the trait. - Position::ArgumentNamed(arg) => LitOrArg::Arg(arg.to_owned()), - - // FIXME(mejrs) These should really be warnings/errors - Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(String::from("{}")), - Position::ArgumentIs(idx) => LitOrArg::Lit(format!("{{{idx}}}")), - }, - }) - .collect(); - Self { pieces } - } - - fn format(&self, generic_args: &[(Symbol, String)]) -> String { - let mut ret = String::new(); - - for piece in &self.pieces { - match piece { - LitOrArg::Lit(s) => ret.push_str(s), - LitOrArg::Arg(arg) => { - let s = Symbol::intern(arg); - match generic_args.iter().find(|(k, _)| *k == s) { - Some((_, val)) => ret.push_str(val), - None => { - // FIXME(mejrs) If we start checking as mentioned in - // FilterFormatString::parse then this shouldn't happen - let _ = std::fmt::write(&mut ret, format_args!("{{{s}}}")); - } - } - } - } - } - - ret - } -} - -/// Used with `OnUnimplementedCondition::matches_predicate` to evaluate the -/// [`OnUnimplementedCondition`]. -/// -/// For example, given a -/// ```rust,ignore (just an example) -/// #[rustc_on_unimplemented( -/// on(all(from_desugaring = "QuestionMark"), -/// message = "the `?` operator can only be used in {ItemContext} \ -/// that returns `Result` or `Option` \ -/// (or another type that implements `{FromResidual}`)", -/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`", -/// parent_label = "this function should return `Result` or `Option` to accept `?`" -/// ), -/// )] -/// pub trait FromResidual::Residual> { -/// ... -/// } -/// -/// async fn an_async_function() -> u32 { -/// let x: Option = None; -/// x?; //~ ERROR the `?` operator -/// 22 -/// } -/// ``` -/// it will look like this: -/// -/// ```rust,ignore (just an example) -/// ConditionOptions { -/// self_types: ["u32", "{integral}"], -/// from_desugaring: Some("QuestionMark"), -/// cause: None, -/// crate_local: false, -/// direct: true, -/// generic_args: [("Self","u32"), -/// ("R", "core::option::Option"), -/// ("R", "core::option::Option" ), -/// ], -/// } -/// ``` -#[derive(Debug)] -pub(crate) struct ConditionOptions { - /// All the self types that may apply. - pub(crate) self_types: Vec, - // The kind of compiler desugaring. - pub(crate) from_desugaring: Option, - /// Match on a variant of [rustc_infer::traits::ObligationCauseCode]. - pub(crate) cause: Option, - pub(crate) crate_local: bool, - /// Is the obligation "directly" user-specified, rather than derived? - pub(crate) direct: bool, - // A list of the generic arguments and their reified types. - pub(crate) generic_args: Vec<(Symbol, String)>, -} - -impl ConditionOptions { - fn has_flag(&self, name: Flag) -> bool { - match name { - Flag::CrateLocal => self.crate_local, - Flag::Direct => self.direct, - Flag::FromDesugaring => self.from_desugaring.is_some(), - } - } - fn contains(&self, name: Name, value: String) -> bool { - match name { - Name::SelfUpper => self.self_types.contains(&value), - Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)), - Name::Cause => self.cause == Some(value), - Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)), - } - } -} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs deleted file mode 100644 index 8488d76e2c77..000000000000 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs +++ /dev/null @@ -1,336 +0,0 @@ -use std::fmt; -use std::ops::Range; - -use errors::*; -use rustc_middle::ty::print::TraitRefPrintSugared; -use rustc_middle::ty::{GenericParamDefKind, TyCtxt}; -use rustc_parse_format::{ - Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position, -}; -use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_FORMAT_LITERALS; -use rustc_span::def_id::DefId; -use rustc_span::{InnerSpan, Span, Symbol, kw, sym}; - -/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces", -/// either as string pieces or dynamic arguments. -#[derive(Debug)] -pub struct FormatString { - #[allow(dead_code, reason = "Debug impl")] - input: Symbol, - span: Span, - pieces: Vec, - /// The formatting string was parsed successfully but with warnings - pub warnings: Vec, -} - -#[derive(Debug)] -enum Piece { - Lit(String), - Arg(FormatArg), -} - -#[derive(Debug)] -enum FormatArg { - // A generic parameter, like `{T}` if we're on the `From` trait. - GenericParam { - generic_param: Symbol, - }, - // `{Self}` - SelfUpper, - /// `{This}` or `{TraitName}` - This, - /// The sugared form of the trait - Trait, - /// what we're in, like a function, method, closure etc. - ItemContext, - /// What the user typed, if it doesn't match anything we can use. - AsIs(String), -} - -pub enum Ctx<'tcx> { - // `#[rustc_on_unimplemented]` - RustcOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId }, - // `#[diagnostic::...]` - DiagnosticOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId }, -} - -#[derive(Debug)] -pub enum FormatWarning { - UnknownParam { argument_name: Symbol, span: Span }, - PositionalArgument { span: Span, help: String }, - InvalidSpecifier { name: String, span: Span }, - FutureIncompat { span: Span, help: String }, -} - -impl FormatWarning { - pub fn emit_warning<'tcx>(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) { - match *self { - FormatWarning::UnknownParam { argument_name, span } => { - let this = tcx.item_ident(item_def_id); - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - tcx.local_def_id_to_hir_id(item_def_id), - span, - UnknownFormatParameterForOnUnimplementedAttr { - argument_name, - trait_name: this, - }, - ); - } - } - FormatWarning::PositionalArgument { span, .. } => { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - tcx.local_def_id_to_hir_id(item_def_id), - span, - DisallowedPositionalArgument, - ); - } - } - FormatWarning::InvalidSpecifier { span, .. } => { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - tcx.local_def_id_to_hir_id(item_def_id), - span, - InvalidFormatSpecifier, - ); - } - } - FormatWarning::FutureIncompat { .. } => { - // We've never deprecated anything in diagnostic namespace format strings - // but if we do we will emit a warning here - - // FIXME(mejrs) in a couple releases, start emitting warnings for - // #[rustc_on_unimplemented] deprecated args - } - } - } -} - -/// Arguments to fill a [FormatString] with. -/// -/// For example, given a -/// ```rust,ignore (just an example) -/// -/// #[rustc_on_unimplemented( -/// on(all(from_desugaring = "QuestionMark"), -/// message = "the `?` operator can only be used in {ItemContext} \ -/// that returns `Result` or `Option` \ -/// (or another type that implements `{FromResidual}`)", -/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`", -/// parent_label = "this function should return `Result` or `Option` to accept `?`" -/// ), -/// )] -/// pub trait FromResidual::Residual> { -/// ... -/// } -/// -/// async fn an_async_function() -> u32 { -/// let x: Option = None; -/// x?; //~ ERROR the `?` operator -/// 22 -/// } -/// ``` -/// it will look like this: -/// -/// ```rust,ignore (just an example) -/// FormatArgs { -/// this: "FromResidual", -/// trait_sugared: "FromResidual>", -/// item_context: "an async function", -/// generic_args: [("Self", "u32"), ("R", "Option")], -/// } -/// ``` -#[derive(Debug)] -pub struct FormatArgs<'tcx> { - pub this: String, - pub trait_sugared: TraitRefPrintSugared<'tcx>, - pub item_context: &'static str, - pub generic_args: Vec<(Symbol, String)>, -} - -impl FormatString { - pub fn span(&self) -> Span { - self.span - } - - pub fn parse<'tcx>( - input: Symbol, - snippet: Option, - span: Span, - ctx: &Ctx<'tcx>, - ) -> Result { - let s = input.as_str(); - let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic); - let pieces: Vec<_> = parser.by_ref().collect(); - - if let Some(err) = parser.errors.into_iter().next() { - return Err(err); - } - let mut warnings = Vec::new(); - - let pieces = pieces - .into_iter() - .map(|piece| match piece { - RpfPiece::Lit(lit) => Piece::Lit(lit.into()), - RpfPiece::NextArgument(arg) => { - warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal); - let arg = parse_arg(&arg, ctx, &mut warnings, span, parser.is_source_literal); - Piece::Arg(arg) - } - }) - .collect(); - - Ok(FormatString { input, pieces, span, warnings }) - } - - pub fn format(&self, args: &FormatArgs<'_>) -> String { - let mut ret = String::new(); - for piece in &self.pieces { - match piece { - Piece::Lit(s) | Piece::Arg(FormatArg::AsIs(s)) => ret.push_str(&s), - - // `A` if we have `trait Trait {}` and `note = "i'm the actual type of {A}"` - Piece::Arg(FormatArg::GenericParam { generic_param }) => { - // Should always be some but we can't raise errors here - let value = match args.generic_args.iter().find(|(p, _)| p == generic_param) { - Some((_, val)) => val.to_string(), - None => generic_param.to_string(), - }; - ret.push_str(&value); - } - // `{Self}` - Piece::Arg(FormatArg::SelfUpper) => { - let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) { - Some((_, val)) => val.to_string(), - None => "Self".to_string(), - }; - ret.push_str(&slf); - } - - // It's only `rustc_onunimplemented` from here - Piece::Arg(FormatArg::This) => ret.push_str(&args.this), - Piece::Arg(FormatArg::Trait) => { - let _ = fmt::write(&mut ret, format_args!("{}", &args.trait_sugared)); - } - Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context), - } - } - ret - } -} - -fn parse_arg<'tcx>( - arg: &Argument<'_>, - ctx: &Ctx<'tcx>, - warnings: &mut Vec, - input_span: Span, - is_source_literal: bool, -) -> FormatArg { - let (Ctx::RustcOnUnimplemented { tcx, trait_def_id } - | Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = ctx; - - let span = slice_span(input_span, arg.position_span.clone(), is_source_literal); - - match arg.position { - // Something like "hello {name}" - Position::ArgumentNamed(name) => match (ctx, Symbol::intern(name)) { - // Only `#[rustc_on_unimplemented]` can use these - (Ctx::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext, - (Ctx::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This, - (Ctx::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait, - // Any attribute can use these - ( - Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. }, - kw::SelfUpper, - ) => FormatArg::SelfUpper, - ( - Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. }, - generic_param, - ) if tcx.generics_of(trait_def_id).own_params.iter().any(|param| { - !matches!(param.kind, GenericParamDefKind::Lifetime) && param.name == generic_param - }) => - { - FormatArg::GenericParam { generic_param } - } - - (_, argument_name) => { - warnings.push(FormatWarning::UnknownParam { argument_name, span }); - FormatArg::AsIs(format!("{{{}}}", argument_name.as_str())) - } - }, - - // `{:1}` and `{}` are ignored - Position::ArgumentIs(idx) => { - warnings.push(FormatWarning::PositionalArgument { - span, - help: format!("use `{{{idx}}}` to print a number in braces"), - }); - FormatArg::AsIs(format!("{{{idx}}}")) - } - Position::ArgumentImplicitlyIs(_) => { - warnings.push(FormatWarning::PositionalArgument { - span, - help: String::from("use `{{}}` to print empty braces"), - }); - FormatArg::AsIs(String::from("{}")) - } - } -} - -/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything -/// with specifiers, so emit a warning if they are used. -fn warn_on_format_spec( - spec: &FormatSpec<'_>, - warnings: &mut Vec, - input_span: Span, - is_source_literal: bool, -) { - if spec.ty != "" { - let span = spec - .ty_span - .as_ref() - .map(|inner| slice_span(input_span, inner.clone(), is_source_literal)) - .unwrap_or(input_span); - warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() }) - } -} - -fn slice_span(input: Span, Range { start, end }: Range, is_source_literal: bool) -> Span { - if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input } -} - -pub mod errors { - use rustc_macros::LintDiagnostic; - use rustc_span::Ident; - - use super::*; - - #[derive(LintDiagnostic)] - #[diag("there is no parameter `{$argument_name}` on trait `{$trait_name}`")] - #[help("expect either a generic argument name or {\"`{Self}`\"} as format argument")] - pub struct UnknownFormatParameterForOnUnimplementedAttr { - pub argument_name: Symbol, - pub trait_name: Ident, - } - - #[derive(LintDiagnostic)] - #[diag("positional format arguments are not allowed here")] - #[help( - "only named format arguments with the name of one of the generic types are allowed in this context" - )] - pub struct DisallowedPositionalArgument; - - #[derive(LintDiagnostic)] - #[diag("invalid format specifier")] - #[help("no format specifier are supported in this position")] - pub struct InvalidFormatSpecifier; - - #[derive(LintDiagnostic)] - #[diag("missing options for `on_unimplemented` attribute")] - #[help("at least one of the `message`, `note` and `label` options are expected")] - pub struct MissingOptionsForOnUnimplementedAttr; -} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 63fd61cb257b..14aff65d4b51 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1,12 +1,11 @@ // ignore-tidy-filelength use std::borrow::Cow; -use std::iter; use std::path::PathBuf; +use std::{debug_assert_matches, iter}; use itertools::{EitherOrBoth, Itertools}; use rustc_abi::ExternAbi; -use rustc_data_structures::debug_assert_matches; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::codes::*; @@ -558,7 +557,7 @@ pub(super) fn suggest_dereferences( if receiver_expr.hir_id == *arg_hir_id ); if is_receiver { - err.multipart_suggestion_verbose( + err.multipart_suggestion( msg, vec![ (span.shrink_to_lo(), format!("({derefs}")), @@ -697,7 +696,7 @@ pub(super) fn suggest_dereferences( { let mut suggestion = make_sugg(lhs, lsteps).1; suggestion.append(&mut make_sugg(rhs, rsteps).1); - err.multipart_suggestion_verbose( + err.multipart_suggestion( "consider dereferencing both sides of the expression", suggestion, Applicability::MachineApplicable, @@ -707,21 +706,13 @@ pub(super) fn suggest_dereferences( && lsteps > 0 { let (msg, suggestion) = make_sugg(lhs, lsteps); - err.multipart_suggestion_verbose( - msg, - suggestion, - Applicability::MachineApplicable, - ); + err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable); return true; } else if let Some(rsteps) = rsteps && rsteps > 0 { let (msg, suggestion) = make_sugg(rhs, rsteps); - err.multipart_suggestion_verbose( - msg, - suggestion, - Applicability::MachineApplicable, - ); + err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable); return true; } } @@ -806,6 +797,44 @@ pub(super) fn suggest_fn_call( return false; } + // If this is a zero-argument async closure directly passed as an argument + // and the expected type is `Future`, suggest using `async {}` block instead + // of `async || {}` + if let ty::CoroutineClosure(def_id, args) = *self_ty.kind() + && let sig = args.as_coroutine_closure().coroutine_closure_sig().skip_binder() + && let ty::Tuple(inputs) = *sig.tupled_inputs_ty.kind() + && inputs.is_empty() + && self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Future) + && let Some(hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::Closure(hir::Closure { + kind: hir::ClosureKind::CoroutineClosure(CoroutineDesugaring::Async), + fn_arg_span: Some(arg_span), + .. + }), + .. + })) = self.tcx.hir_get_if_local(def_id) + && obligation.cause.span.contains(*arg_span) + { + let sm = self.tcx.sess.source_map(); + let removal_span = if let Ok(snippet) = + sm.span_to_snippet(arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1))) + && snippet.ends_with(' ') + { + // There's a space after `||`, include it in the removal + arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1)) + } else { + *arg_span + }; + err.span_suggestion_verbose( + removal_span, + "use `async {}` instead of `async || {}` to introduce an async block", + "", + Applicability::MachineApplicable, + ); + return true; + } + // Get the name of the callable and the arguments to be used in the suggestion. let msg = match def_id_or_name { DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { @@ -1268,7 +1297,7 @@ pub(super) fn suggest_add_reference_to_arg( }; match (imm_ref_self_ty_satisfies_pred, mut_ref_self_ty_satisfies_pred, mtbl) { (true, _, hir::Mutability::Not) | (_, true, hir::Mutability::Mut) => { - err.multipart_suggestion_verbose( + err.multipart_suggestion( sugg_msg(mtbl.prefix_str()), vec![ (outer.span.shrink_to_lo(), "<".to_string()), @@ -1279,7 +1308,7 @@ pub(super) fn suggest_add_reference_to_arg( } (true, _, hir::Mutability::Mut) => { // There's an associated function found on the immutable borrow of the - err.multipart_suggestion_verbose( + err.multipart_suggestion( sugg_msg("mut "), vec![ (outer.span.shrink_to_lo().until(span), "<&".to_string()), @@ -1289,7 +1318,7 @@ pub(super) fn suggest_add_reference_to_arg( ); } (_, true, hir::Mutability::Not) => { - err.multipart_suggestion_verbose( + err.multipart_suggestion( sugg_msg(""), vec![ (outer.span.shrink_to_lo().until(span), "<&mut ".to_string()), @@ -1608,11 +1637,7 @@ pub(super) fn suggest_remove_reference( format!("consider removing {count} leading `&`-references") }; - err.multipart_suggestion_verbose( - msg, - suggestions, - Applicability::MachineApplicable, - ); + err.multipart_suggestion(msg, suggestions, Applicability::MachineApplicable); true } else { false @@ -2256,9 +2281,9 @@ fn note_conflicting_closure_bounds( // First, look for an `WhereClauseInExpr`, which means we can get // the uninstantiated predicate list of the called function. And check // that the predicate that we failed to satisfy is a `Fn`-like trait. - if let ObligationCauseCode::WhereClauseInExpr(def_id, _, _, idx) = cause + if let ObligationCauseCode::WhereClauseInExpr(def_id, _, _, idx) = *cause && let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx) - && let Some(pred) = predicates.predicates.get(*idx) + && let Some(pred) = predicates.predicates.get(idx) && let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() && self.tcx.is_fn_trait(trait_pred.def_id()) { @@ -2269,7 +2294,7 @@ fn note_conflicting_closure_bounds( // Find another predicate whose self-type is equal to the expected self type, // but whose args don't match. - let other_pred = predicates.into_iter().enumerate().find(|(other_idx, (pred, _))| { + let other_pred = predicates.into_iter().enumerate().find(|&(other_idx, (pred, _))| { match pred.kind().skip_binder() { ty::ClauseKind::Trait(trait_pred) if self.tcx.is_fn_trait(trait_pred.def_id()) @@ -3041,10 +3066,10 @@ pub(super) fn note_obligation_cause_code( let len = impls.len(); let mut types = impls .iter() - .map(|t| { + .map(|&&t| { with_no_trimmed_paths!(format!( " {}", - tcx.type_of(*t).instantiate_identity(), + tcx.type_of(t).instantiate_identity(), )) }) .collect::>(); @@ -3273,7 +3298,7 @@ pub(super) fn note_obligation_cause_code( (ty.span.shrink_to_hi(), ")".to_string()), ] }; - err.multipart_suggestion_verbose( + err.multipart_suggestion( borrowed_msg, sugg, Applicability::MachineApplicable, @@ -3350,7 +3375,7 @@ pub(super) fn note_obligation_cause_code( "&", Applicability::MachineApplicable, ); - err.multipart_suggestion_verbose( + err.multipart_suggestion( "the `Box` type always has a statically known size and allocates its contents \ in the heap", vec![ @@ -3433,7 +3458,7 @@ pub(super) fn note_obligation_cause_code( let ty_str = tcx.short_string(ty, err.long_ty_path()); format!("required because it appears within the type `{ty_str}`") }; - match ty.kind() { + match *ty.kind() { ty::Adt(def, _) => { let msg = msg(); match tcx.opt_item_ident(def.did()) { @@ -3857,7 +3882,6 @@ pub(super) fn note_obligation_cause_code( err.span_note(assoc_span, msg); } ObligationCauseCode::TrivialBound => { - err.help("see issue #48214"); tcx.disabled_nightly_features(err, [(String::new(), sym::trivial_bounds)]); } ObligationCauseCode::OpaqueReturnType(expr_info) => { @@ -4228,11 +4252,11 @@ fn note_function_argument_obligation( // to an associated type (as seen from `trait_pred`) in the predicate. Like in // trait_pred `S: Sum<::Item>` and predicate `i32: Sum<&()>` let mut type_diffs = vec![]; - if let ObligationCauseCode::WhereClauseInExpr(def_id, _, _, idx) = parent_code + if let ObligationCauseCode::WhereClauseInExpr(def_id, _, _, idx) = *parent_code && let Some(node_args) = typeck_results.node_args_opt(call_hir_id) && let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_args) - && let Some(where_pred) = where_clauses.predicates.get(*idx) + && let Some(where_pred) = where_clauses.predicates.get(idx) { if let Some(where_pred) = where_pred.as_trait_clause() && let Some(failed_pred) = failed_pred.as_trait_clause() @@ -4849,7 +4873,7 @@ pub(super) fn suggest_convert_to_slice( suggestions.push((span.shrink_to_lo(), "&".into())); } suggestions.push((span.shrink_to_hi(), "[..]".into())); - err.multipart_suggestion_verbose(msg, suggestions, Applicability::MaybeIncorrect); + err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); } else { err.span_help(span, msg); } @@ -4884,7 +4908,7 @@ pub(super) fn suggest_tuple_wrapping( if self.predicate_must_hold_modulo_regions(&obligation) { let arg_span = self.tcx.hir_span(*arg_hir_id); - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!("use a unary tuple instead"), vec![(arg_span.shrink_to_lo(), "(".into()), (arg_span.shrink_to_hi(), ",)".into())], Applicability::MaybeIncorrect, @@ -5178,7 +5202,7 @@ fn choose_suggest_items<'tcx, 'hir>( ), )); } - err.multipart_suggestion_verbose( + err.multipart_suggestion( format!("consider adding return type"), sugg_spans, Applicability::MaybeIncorrect, @@ -5268,7 +5292,7 @@ pub(super) fn suggest_unsized_bound_if_applicable( suggs.push((span, suggestion)); } - err.multipart_suggestion_verbose( + err.multipart_suggestion( "consider relaxing the implicit `Sized` restriction", suggs, Applicability::MachineApplicable, @@ -5608,15 +5632,17 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>( None => String::new(), }; if let ty::PredicatePolarity::Positive = trait_predicate.polarity() { + // If the trait in question is unstable, mention that fact in the diagnostic. + // But if we're building with `-Zforce-unstable-if-unmarked` then _any_ trait + // not explicitly marked stable is considered unstable, so the extra text is + // unhelpful noise. See . + let mention_unstable = !tcx.sess.opts.unstable_opts.force_unstable_if_unmarked + && try { tcx.lookup_stability(trait_predicate.def_id())?.level.is_stable() } + == Some(false); + let unstable = if mention_unstable { "nightly-only, unstable " } else { "" }; + format!( - "{pre_message}the {}trait `{}` is not implemented for{desc} `{}`", - if tcx.lookup_stability(trait_predicate.def_id()).map(|s| s.level.is_stable()) - == Some(false) - { - "nightly-only, unstable " - } else { - "" - }, + "{pre_message}the {unstable}trait `{}` is not implemented for{desc} `{}`", trait_predicate.print_modifiers_and_trait_path(), tcx.short_string(trait_predicate.self_ty().skip_binder(), long_ty_path), ) diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 8f353cae0beb..6f63c1d663f1 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,6 +1,6 @@ -use rustc_ast::Path; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, msg, @@ -12,7 +12,7 @@ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath}; use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, GenericArg, Region, Ty, TyCtxt}; -use rustc_span::{BytePos, Ident, Span, Symbol, kw}; +use rustc_span::{BytePos, Ident, Span, Symbol, kw, sym}; use crate::error_reporting::infer::ObligationCauseAsDiagArg; use crate::error_reporting::infer::need_type_info::UnderspecifiedArgKind; @@ -28,60 +28,6 @@ pub struct UnableToConstructConstantValue<'a> { pub unevaluated: ty::UnevaluatedConst<'a>, } -#[derive(Diagnostic)] -pub enum InvalidOnClause { - #[diag("empty `on`-clause in `#[rustc_on_unimplemented]`", code = E0232)] - Empty { - #[primary_span] - #[label("empty `on`-clause here")] - span: Span, - }, - #[diag("expected a single predicate in `not(..)`", code = E0232)] - ExpectedOnePredInNot { - #[primary_span] - #[label("unexpected quantity of predicates here")] - span: Span, - }, - #[diag("literals inside `on`-clauses are not supported", code = E0232)] - UnsupportedLiteral { - #[primary_span] - #[label("unexpected literal here")] - span: Span, - }, - #[diag("expected an identifier inside this `on`-clause", code = E0232)] - ExpectedIdentifier { - #[primary_span] - #[label("expected an identifier here, not `{$path}`")] - span: Span, - path: Path, - }, - #[diag("this predicate is invalid", code = E0232)] - InvalidPredicate { - #[primary_span] - #[label("expected one of `any`, `all` or `not` here, not `{$invalid_pred}`")] - span: Span, - invalid_pred: Symbol, - }, - #[diag("invalid flag in `on`-clause", code = E0232)] - InvalidFlag { - #[primary_span] - #[label( - "expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}`" - )] - span: Span, - invalid_flag: Symbol, - }, - #[diag("invalid name in `on`-clause", code = E0232)] - InvalidName { - #[primary_span] - #[label( - "expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `{$invalid_name}`" - )] - span: Span, - invalid_name: Symbol, - }, -} - #[derive(Diagnostic)] #[diag("this attribute must have a value", code = E0232)] #[note("e.g. `#[rustc_on_unimplemented(message=\"foo\")]`")] @@ -156,7 +102,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { AdjustSignatureBorrow::Borrow { to_borrow } => { diag.arg("borrow_len", to_borrow.len()); - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( msg!( "consider adjusting the signature so it borrows its {$borrow_len -> [one] argument @@ -169,7 +115,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } AdjustSignatureBorrow::RemoveBorrow { remove_borrow } => { diag.arg("remove_borrow_len", remove_borrow.len()); - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( msg!( "consider adjusting the signature so it does not borrow its {$remove_borrow_len -> [one] argument @@ -212,6 +158,7 @@ pub struct ClosureFnOnceLabel { #[primary_span] pub span: Span, pub place: String, + pub trait_prefix: &'static str, } #[derive(Subdiagnostic)] @@ -220,6 +167,7 @@ pub struct ClosureFnMutLabel { #[primary_span] pub span: Span, pub place: String, + pub trait_prefix: &'static str, } #[derive(Diagnostic)] @@ -477,7 +425,7 @@ pub enum RegionOriginNote<'a> { impl Subdiagnostic for RegionOriginNote<'_> { fn add_to_diag(self, diag: &mut Diag<'_, G>) { - let mut label_or_note = |span, msg: DiagMessage| { + let label_or_note = |diag: &mut Diag<'_, G>, span, msg: DiagMessage| { let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count(); let span_is_primary = diag.span.primary_spans().iter().all(|&sp| sp == span); @@ -491,37 +439,36 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { }; match self { RegionOriginNote::Plain { span, msg } => { - label_or_note(span, msg); + label_or_note(diag, span, msg); } RegionOriginNote::WithName { span, msg, name, continues } => { - label_or_note(span, msg); diag.arg("name", name); diag.arg("continues", continues); + label_or_note(diag, span, msg); } RegionOriginNote::WithRequirement { span, requirement, expected_found: Some((expected, found)), } => { - label_or_note( - span, - msg!( - "...so that the {$requirement -> - [method_compat] method type is compatible with trait - [type_compat] associated type is compatible with trait - [const_compat] const is compatible with trait - [expr_assignable] expression is assignable - [if_else_different] `if` and `else` have incompatible types - [no_else] `if` missing an `else` returns `()` - [fn_main_correct_type] `main` function has the correct type - [fn_lang_correct_type] lang item function has the correct type - [intrinsic_correct_type] intrinsic has the correct type - [method_correct_type] method receiver has the correct type - *[other] types are compatible - }" - ), - ); - diag.arg("requirement", requirement); + let msg = msg!( + "...so that the {$requirement -> + [method_compat] method type is compatible with trait + [type_compat] associated type is compatible with trait + [const_compat] const is compatible with trait + [expr_assignable] expression is assignable + [if_else_different] `if` and `else` have incompatible types + [no_else] `if` missing an `else` returns `()` + [fn_main_correct_type] `main` function has the correct type + [fn_lang_correct_type] lang item function has the correct type + [intrinsic_correct_type] intrinsic has the correct type + [method_correct_type] method receiver has the correct type + *[other] types are compatible + }" + ) + .arg("requirement", requirement) + .format(); + label_or_note(diag, span, msg); diag.note_expected_found("", expected, "", found); } @@ -529,10 +476,8 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { // FIXME: this really should be handled at some earlier stage. Our // handling of region checking when type errors are present is // *terrible*. - label_or_note( - span, - msg!( - "...so that {$requirement -> + let msg = msg!( + "...so that {$requirement -> [method_compat] method type is compatible with trait [type_compat] associated type is compatible with trait [const_compat] const is compatible with trait @@ -545,9 +490,10 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { [method_correct_type] method receiver has the correct type *[other] types are compatible }" - ), - ); - diag.arg("requirement", requirement); + ) + .arg("requirement", requirement) + .format(); + label_or_note(diag, span, msg); } }; } @@ -788,7 +734,7 @@ fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { visitor.suggestions.push(new_param_suggestion); } - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( msg!( "consider {$is_reuse -> [true] reusing @@ -1223,7 +1169,9 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { type_param_span .push_span_label(span, msg!("consider borrowing this type parameter in the trait")); } - let msg = diag.eagerly_translate(msg!("the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`")); + let msg = msg!( + "the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`" + ); diag.span_help(type_param_span, msg); } } @@ -1267,10 +1215,9 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { self.ident.span, msg!("calling this method introduces the `impl`'s `'static` requirement"), ); - let msg = diag.eagerly_translate(msg!("the used `impl` has a `'static` requirement")); + let msg = msg!("the used `impl` has a `'static` requirement"); diag.span_note(multi_span, msg); - let msg = - diag.eagerly_translate(msg!("consider relaxing the implicit `'static` requirement")); + let msg = msg!("consider relaxing the implicit `'static` requirement"); diag.span_suggestion_verbose( self.span.shrink_to_hi(), msg, @@ -1280,38 +1227,6 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } } -#[derive(Diagnostic)] -#[diag("{$has_param_name -> - [true] `{$param_name}` - *[false] `fn` parameter -} has {$lifetime_kind -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` -} but calling `{$assoc_item}` introduces an implicit `'static` lifetime requirement", code = E0772)] -pub struct ButCallingIntroduces { - #[label( - "{$has_lifetime -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` - }" - )] - pub param_ty_span: Span, - #[primary_span] - #[label("...is used and required to live as long as `'static` here because of an implicit lifetime bound on the {$has_impl_path -> - [true] `impl` of `{$impl_path}` - *[false] inherent `impl` - }")] - pub cause_span: Span, - - pub has_param_name: bool, - pub param_name: String, - pub has_lifetime: bool, - pub lifetime: String, - pub assoc_item: Symbol, - pub has_impl_path: bool, - pub impl_path: String, -} - pub struct ReqIntroducedLocations { pub span: MultiSpan, pub spans: Vec, @@ -1333,9 +1248,7 @@ fn add_to_diag(mut self, diag: &mut Diag<'_, G>) { ); } self.span.push_span_label(self.cause_span, msg!("because of this returned expression")); - let msg = diag.eagerly_translate(msg!( - "\"`'static` lifetime requirement introduced by the return type" - )); + let msg = msg!("\"`'static` lifetime requirement introduced by the return type"); diag.span_note(self.span, msg); } } @@ -1775,8 +1688,7 @@ pub struct SuggestTuplePatternMany { impl Subdiagnostic for SuggestTuplePatternMany { fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("path", self.path); - let message = - diag.eagerly_translate(msg!("try wrapping the pattern in a variant of `{$path}`")); + let message = msg!("try wrapping the pattern in a variant of `{$path}`"); diag.multipart_suggestions( message, self.compatible_variants.into_iter().map(|variant| { @@ -2019,7 +1931,7 @@ pub struct AddPreciseCapturingAndParams { impl Subdiagnostic for AddPreciseCapturingAndParams { fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("new_lifetime", self.new_lifetime); - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( msg!("add a `use<...>` bound to explicitly capture `{$new_lifetime}` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate"), self.suggs, Applicability::MaybeIncorrect, @@ -2062,9 +1974,9 @@ pub fn impl_trait_overcapture_suggestion<'tcx>( } let mut next_fresh_param = || { - ["T", "U", "V", "W", "X", "Y", "A", "B", "C"] + ['T', 'U', 'V', 'W', 'X', 'Y', 'A', 'B', 'C'] .into_iter() - .map(Symbol::intern) + .map(sym::character) .chain((0..).map(|i| Symbol::intern(&format!("T{i}")))) .find(|s| captured_non_lifetimes.insert(*s)) .unwrap() @@ -2166,7 +2078,7 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { // not intended. Applicability::MaybeIncorrect }; - diag.multipart_suggestion_verbose( + diag.multipart_suggestion( msg!("use the precise capturing `use<...>` syntax to make the captures explicit"), self.suggs, applicability, diff --git a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs index 08618be03e75..fd943bff3700 100644 --- a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs @@ -1,3 +1,4 @@ +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, Subdiagnostic, msg}; use rustc_hir::def_id::LocalDefId; use rustc_middle::bug; @@ -163,13 +164,7 @@ pub fn new<'tcx>( impl Subdiagnostic for RegionExplanation<'_> { fn add_to_diag(self, diag: &mut Diag<'_, G>) { - diag.store_args(); - diag.arg("pref_kind", self.prefix); - diag.arg("suff_kind", self.suffix); - diag.arg("desc_kind", self.desc.kind); - diag.arg("desc_arg", self.desc.arg); - - let msg = diag.eagerly_translate(msg!( + let msg = msg!( "{$pref_kind -> *[should_not_happen] [{$pref_kind}] [ref_valid_for] ...the reference is valid for @@ -202,8 +197,14 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { [continues] ... [req_by_binding] {\" \"}as required by this binding }" - )); - diag.restore_args(); + ) + .arg("pref_kind", self.prefix) + .arg("suff_kind", self.suffix) + .arg("desc_kind", self.desc.kind) + .arg("desc_arg", self.desc.arg) + .format(); + + // diag.restore_args(); if let Some(span) = self.desc.span { diag.span_note(span, msg); } else { diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 708e0b9e8497..f0c67e4b4f89 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -11,12 +11,10 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(default_field_values)] #![feature(hash_set_entry)] -#![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iterator_try_reduce)] #![feature(never_type)] diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index b8da64b9729a..370aa36e5f77 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -9,7 +9,8 @@ //! coherence right now and was annoying to implement, so I am leaving it //! as is until we start using it for something else. -use rustc_data_structures::assert_matches; +use std::assert_matches; + use rustc_infer::infer::InferCtxt; use rustc_infer::traits::Obligation; use rustc_macros::extension; diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index fc628e78a3e2..a5d33c7f61a5 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -8,7 +8,6 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::{Diag, EmissionGuarantee}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_hir::find_attr; @@ -498,22 +497,19 @@ fn impl_intersection_has_negative_obligation( ) -> bool { debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id); - // N.B. We need to unify impl headers *with* intercrate mode, even if proving negative predicates - // do not need intercrate mode enabled. + // N.B. We need to unify impl headers *with* `TypingMode::Coherence`, + // even if proving negative predicates doesn't need `TypingMode::Coherence`. let ref infcx = tcx.infer_ctxt().with_next_trait_solver(true).build(TypingMode::Coherence); let root_universe = infcx.universe(); assert_eq!(root_universe, ty::UniverseIndex::ROOT); let impl1_header = fresh_impl_header(infcx, impl1_def_id, is_of_trait); - let param_env = - ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)).instantiate(tcx, impl1_header.impl_args); - let impl2_header = fresh_impl_header(infcx, impl2_def_id, is_of_trait); // Equate the headers to find their intersection (the general type, with infer vars, // that may apply both impls). let Some(equate_obligations) = - equate_impl_headers(infcx, param_env, &impl1_header, &impl2_header) + equate_impl_headers(infcx, ty::ParamEnv::empty(), &impl1_header, &impl2_header) else { return false; }; @@ -531,7 +527,16 @@ fn impl_intersection_has_negative_obligation( root_universe, (impl1_header.impl_args, impl2_header.impl_args), ); - let param_env = infcx.resolve_vars_if_possible(param_env); + + // Right above we plug inference variables with placeholders, + // this gets us new impl1_header_args with the inference variables actually resolved + // to those placeholders. + let impl1_header_args = infcx.resolve_vars_if_possible(impl1_header.impl_args); + // So there are no infer variables left now, except regions which aren't resolved by `resolve_vars_if_possible`. + assert!(!impl1_header_args.has_non_region_infer()); + + let param_env = + ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)).instantiate(tcx, impl1_header_args); util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args)) .elaborate_sized() @@ -740,7 +745,7 @@ fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) { ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) if matches!( infcx.tcx.def_kind(proj.projection_term.def_id), - DefKind::AssocTy | DefKind::AssocConst + DefKind::AssocTy | DefKind::AssocConst { .. } ) => { proj.projection_term.trait_ref(infcx.tcx) @@ -760,8 +765,9 @@ fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) { } = cand.kind() && let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) { - let message = find_attr!(infcx.tcx.get_all_attrs(def_id), AttributeKind::RustcReservationImpl(_, message) => *message); - if let Some(message) = message { + if let Some(message) = + find_attr!(infcx.tcx, def_id, RustcReservationImpl(_, message) => *message) + { self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { message }); } } diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 512973e45287..f1ab59abc6ae 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -314,6 +314,11 @@ pub fn dyn_compatibility_violations_for_assoc_item( trait_def_id: DefId, item: ty::AssocItem, ) -> Vec { + // `final` assoc functions don't prevent a trait from being dyn-compatible + if tcx.defaultness(item.def_id).is_final() { + return Vec::new(); + } + // Any item that has a `Self: Sized` requisite is otherwise exempt from the regulations. if tcx.generics_require_sized_self(item.def_id) { return Vec::new(); @@ -322,7 +327,7 @@ pub fn dyn_compatibility_violations_for_assoc_item( let span = || item.ident(tcx).span; match item.kind { - ty::AssocKind::Const { name } => { + ty::AssocKind::Const { name, is_type_const } => { // We will permit type associated consts if they are explicitly mentioned in the // trait object type. We can't check this here, as here we only check if it is // guaranteed to not be possible. @@ -332,7 +337,7 @@ pub fn dyn_compatibility_violations_for_assoc_item( if tcx.features().min_generic_const_args() { if !tcx.generics_of(item.def_id).is_own_empty() { errors.push(AssocConstViolation::Generic); - } else if !tcx.is_type_const(item.def_id) { + } else if !is_type_const { errors.push(AssocConstViolation::NonType); } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 681f015c1799..71a8e081146e 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -685,7 +685,11 @@ fn process_obligation( use rustc_hir::def::DefKind; match (c1.kind(), c2.kind()) { (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) - if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst => + if a.def == b.def + && matches!( + tcx.def_kind(a.def), + DefKind::AssocConst { .. } + ) => { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 1475e5f5f243..19a80893e898 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -456,7 +456,7 @@ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { // if it was marked with `type const`. Using this attribute without the mgca // feature gate causes a parse error. let ct = match tcx.def_kind(uv.def) { - DefKind::AssocConst => match tcx.def_kind(tcx.parent(uv.def)) { + DefKind::AssocConst { .. } => match tcx.def_kind(tcx.parent(uv.def)) { DefKind::Trait => self.normalize_trait_projection(uv.into()).expect_const(), DefKind::Impl { of_trait: false } => { self.normalize_inherent_projection(uv.into()).expect_const() @@ -466,7 +466,7 @@ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { kind ), }, - DefKind::Const => self.normalize_free_alias(uv.into()).expect_const(), + DefKind::Const { .. } => self.normalize_free_alias(uv.into()).expect_const(), DefKind::AnonConst => { let ct = ct.super_fold_with(self); super::with_replaced_escaping_bound_vars( diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 5bbbad0b5bbf..3df5c9e33438 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -13,7 +13,7 @@ use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{ - self, Term, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Upcast, + self, FieldInfo, Term, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_span::sym; @@ -987,6 +987,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | LangItem::Future | LangItem::Iterator | LangItem::AsyncIterator + | LangItem::Field | LangItem::Fn | LangItem::FnMut | LangItem::FnOnce @@ -1547,6 +1548,20 @@ fn confirm_builtin_candidate<'cx, 'tcx>( } }); (metadata_ty.into(), obligations) + } else if tcx.is_lang_item(trait_def_id, LangItem::Field) { + let ty::Adt(def, args) = self_ty.kind() else { + bug!("only field representing types can implement `Field`") + }; + let Some(FieldInfo { base, ty, .. }) = def.field_representing_type_info(tcx, args) else { + bug!("only field representing types can implement `Field`") + }; + if tcx.is_lang_item(item_def_id, LangItem::FieldBase) { + (base.into(), PredicateObligations::new()) + } else if tcx.is_lang_item(item_def_id, LangItem::FieldType) { + (ty.into(), PredicateObligations::new()) + } else { + bug!("unexpected associated type {:?} in `Field`", obligation.predicate); + } } else { bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate); }; diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 2e60805cd10a..8a61d9a02175 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -267,7 +267,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( return; } - match ty.kind() { + match *ty.kind() { ty::Bool | ty::Char | ty::Int(_) @@ -287,7 +287,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( ty::Pat(ety, _) | ty::Array(ety, _) | ty::Slice(ety) => { // single-element containers, behave like their element rustc_data_structures::stack::ensure_sufficient_stack(|| { - dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, *ety, constraints) + dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ety, constraints) }); } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 91a025131855..c4353fe25b85 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -99,7 +99,7 @@ fn relate_mir_and_user_args<'tcx>( // For IACs, the user args are in the format [SelfTy, GAT_args...] but type_of expects [impl_args..., GAT_args...]. // We need to infer the impl args by equating the impl's self type with the user-provided self type. - let is_inherent_assoc_const = tcx.def_kind(def_id) == DefKind::AssocConst + let is_inherent_assoc_const = matches!(tcx.def_kind(def_id), DefKind::AssocConst { .. }) && tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false } && tcx.is_type_const(def_id); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 2b58a65051e1..812e087a0cf6 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -92,7 +92,40 @@ pub fn scrape_region_constraints<'tcx, Op, R>( let value = infcx.commit_if_ok(|_| { let ocx = ObligationCtxt::new(infcx); let value = op(&ocx).map_err(|_| { - infcx.dcx().span_delayed_bug(span, format!("error performing operation: {name}")) + infcx.tcx.check_potentially_region_dependent_goals(root_def_id).err().unwrap_or_else( + // FIXME: In this region-dependent context, `type_op` should only fail due to + // region-dependent goals. Any other kind of failure indicates a bug and we + // should ICE. + // + // In principle, all non-region errors are expected to be emitted before + // borrowck. Such errors should taint the body and prevent borrowck from + // running at all. However, this invariant does not currently hold. + // + // Consider: + // + // ```ignore (illustrative) + // struct Foo(T) + // where + // T: Iterator, + // ::Item: Default; + // + // fn foo() -> Foo { + // loop {} + // } + // ``` + // + // Here, `fn foo` ought to error and taint the body before MIR build, since + // `Foo` is not well-formed. However, we currently avoid checking this + // during HIR typeck to prevent duplicate diagnostics. + // + // If we ICE here on any non-region-dependent failure, we would trigger ICEs + // too often for such cases. For now, we emit a delayed bug instead. + || { + infcx + .dcx() + .span_delayed_bug(span, format!("error performing operation: {name}")) + }, + ) })?; let errors = ocx.evaluate_obligations_error_on_ambiguity(); if errors.is_empty() { diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 935834b832f2..b0af60d2aecf 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -12,14 +12,18 @@ use hir::def_id::DefId; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind}; -use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; +use rustc_infer::traits::{Obligation, PolyTraitObligation, PredicateObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; -use rustc_middle::ty::{self, SizedTraitKind, Ty, TypeVisitableExt, TypingMode, elaborate}; +use rustc_middle::ty::{ + self, FieldInfo, SizedTraitKind, TraitRef, Ty, TypeVisitableExt, TypingMode, elaborate, +}; use rustc_middle::{bug, span_bug}; +use rustc_span::DUMMY_SP; use tracing::{debug, instrument, trace}; use super::SelectionCandidate::*; use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack}; +use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::util; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { @@ -128,6 +132,9 @@ pub(super) fn assemble_candidates<'o>( &mut candidates, ); } + Some(LangItem::Field) => { + self.assemble_candidates_for_field_trait(obligation, &mut candidates); + } _ => { // We re-match here for traits that can have both builtin impls and user written impls. // After the builtin impls we need to also add user written impls, which we do not want to @@ -1439,4 +1446,52 @@ fn assemble_candidates_for_bikeshed_guaranteed_no_drop_trait( } } } + + fn assemble_candidates_for_field_trait( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + if let ty::Adt(def, args) = obligation.predicate.self_ty().skip_binder().kind() + && let Some(FieldInfo { base, ty, .. }) = + def.field_representing_type_info(self.tcx(), args) + // 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 new trait solver `fn + // consider_builtin_field_candidate` in + // `compiler/rustc_next_trait_solver/src/solve/trait_goals.rs`. + && match self.infcx.evaluate_obligation(&PredicateObligation::new( + self.tcx(), + obligation.cause.clone(), + obligation.param_env, + TraitRef::new( + self.tcx(), + self.tcx().require_lang_item(LangItem::Sized, DUMMY_SP), + [base], + ), + )) { + Ok(res) if res.must_apply_modulo_regions() => true, + _ => false, + } + && match self.infcx.evaluate_obligation(&PredicateObligation::new( + self.tcx(), + obligation.cause.clone(), + obligation.param_env, + TraitRef::new( + self.tcx(), + self.tcx().require_lang_item(LangItem::Sized, DUMMY_SP), + [ty], + ), + )) { + Ok(res) if res.must_apply_modulo_regions() => true, + _ => false, + } + && match base.kind() { + ty::Adt(def, _) => def.is_struct() && !def.repr().packed(), + ty::Tuple(..) => true, + _ => false, + } + { + candidates.vec.push(BuiltinCandidate); + } + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index b0e045274d0d..9e7637df2649 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -262,6 +262,7 @@ fn confirm_builtin_candidate( Some( LangItem::Destruct | LangItem::DiscriminantKind + | LangItem::Field | LangItem::FnPtrTrait | LangItem::PointeeTrait | LangItem::Tuple diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 3c3160bc533e..a7f47a8615c2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -3,16 +3,14 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection use std::cell::{Cell, RefCell}; -use std::cmp; use std::fmt::{self, Display}; use std::ops::ControlFlow; +use std::{assert_matches, cmp}; use hir::def::DefKind; -use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Diag, EmissionGuarantee}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_infer::infer::BoundRegionConversionTime::{self, HigherRankedType}; @@ -22,7 +20,7 @@ use rustc_infer::traits::{PredicateObligations, TraitObligation}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::bug; -use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds}; +use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; pub use rustc_middle::traits::select::*; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::TypeErrorToStringExt; @@ -878,7 +876,11 @@ fn evaluate_predicate_recursively<'o>( use rustc_hir::def::DefKind; match (c1.kind(), c2.kind()) { (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) - if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst => + if a.def == b.def + && matches!( + tcx.def_kind(a.def), + DefKind::AssocConst { .. } + ) => { if let Ok(InferOk { obligations, value: () }) = self .infcx @@ -1399,7 +1401,7 @@ fn in_task(&mut self, op: OP) -> (R, DepNodeIndex) where OP: FnOnce(&mut Self) -> R, { - self.tcx().dep_graph.with_anon_task(self.tcx(), dep_kinds::TraitSelect, || op(self)) + self.tcx().dep_graph.with_anon_task(self.tcx(), DepKind::TraitSelect, || op(self)) } /// filter_impls filters candidates that have a positive impl for a negative @@ -1445,7 +1447,7 @@ fn filter_reservation_impls( && let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) { if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes { - let message = find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcReservationImpl(_, message) => *message); + let message = find_attr!(tcx, def_id, RustcReservationImpl(_, message) => *message); if let Some(message) = message { debug!( "filter_reservation_impls: \ diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index ca6fd780dd88..e66aa6bc4a9e 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -563,7 +563,7 @@ fn decorate<'tcx, G: EmissionGuarantee>( match used_to_be_allowed { None => { let reported = if overlap.with_impl.is_local() - || tcx.ensure_ok().orphan_check_impl(impl_def_id).is_ok() + || tcx.ensure_result().orphan_check_impl(impl_def_id).is_ok() { let mut err = tcx.dcx().struct_span_err(impl_span, msg()); err.code(E0119); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 02db81d7a759..8462471ba421 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -367,7 +367,7 @@ pub(crate) fn assoc_def( // Ensure that the impl is constrained, otherwise projection may give us // bad unconstrained infer vars. if let Some(impl_def_id) = impl_def_id.as_local() { - tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; + tcx.ensure_result().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; } let item = tcx.associated_item(impl_item_id); @@ -390,7 +390,7 @@ pub(crate) fn assoc_def( if let ty::AssocContainer::TraitImpl(_) = assoc_item.item.container && let Some(impl_def_id) = assoc_item.item.container_id(tcx).as_local() { - tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; + tcx.ensure_result().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; } Ok(assoc_item) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 825c06883a54..0383f23e2b4f 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -12,7 +12,7 @@ use smallvec::{SmallVec, smallvec}; use tracing::debug; -use crate::traits::is_vtable_safe_method; +use crate::traits::{impossible_predicates, is_vtable_safe_method}; #[derive(Clone, Debug)] pub enum VtblSegment<'tcx> { @@ -210,6 +210,11 @@ fn own_existential_vtable_entries_iter( debug!("own_existential_vtable_entry: trait_method={:?}", trait_method); let def_id = trait_method.def_id; + // Final methods should not be included in the vtable. + if trait_method.defaultness(tcx).is_final() { + return None; + } + // Some methods cannot be called on an object; skip those. if !is_vtable_safe_method(tcx, trait_def_id, trait_method) { debug!("own_existential_vtable_entry: not vtable safe"); @@ -271,7 +276,11 @@ fn vtable_entries<'tcx>( // do not hold for this particular set of type parameters. // Note that this method could then never be called, so we // do not want to try and codegen it, in that case (see #23435). - if tcx.instantiate_and_check_impossible_predicates((def_id, args)) { + let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args); + if impossible_predicates( + tcx, + predicates.map(|(predicate, _)| predicate).collect(), + ) { debug!("vtable_entries: predicates do not hold"); return VtblEntry::Vacant; } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 358f14221a0d..4a0a99ea2764 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1016,19 +1016,22 @@ fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result { match c.kind() { ty::ConstKind::Unevaluated(uv) => { if !c.has_escaping_bound_vars() { - let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::ConstEvaluatable(c), - )); - let cause = self.cause(ObligationCauseCode::WellFormed(None)); - self.out.push(traits::Obligation::with_depth( - tcx, - cause, - self.recursion_depth, - self.param_env, - predicate, - )); + // Skip type consts as mGCA doesn't support evaluatable clauses + if !tcx.is_type_const(uv.def) { + let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstEvaluatable(c), + )); + let cause = self.cause(ObligationCauseCode::WellFormed(None)); + self.out.push(traits::Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + } - if tcx.def_kind(uv.def) == DefKind::AssocConst + if matches!(tcx.def_kind(uv.def), DefKind::AssocConst { .. }) && tcx.def_kind(tcx.parent(uv.def)) == (DefKind::Impl { of_trait: false }) { self.add_wf_preds_for_inherent_projection(uv.into()); @@ -1122,6 +1125,23 @@ fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result { }, )); } + ty::Array(elem_ty, _len) => { + let elem_vals = val.to_branch(); + let cause = self.cause(ObligationCauseCode::WellFormed(None)); + + self.out.extend(elem_vals.iter().map(|&elem_val| { + let predicate = ty::PredicateKind::Clause( + ty::ClauseKind::ConstArgHasType(elem_val, *elem_ty), + ); + traits::Obligation::with_depth( + tcx, + cause.clone(), + self.recursion_depth, + self.param_env, + predicate, + ) + })); + } _ => {} } } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 72f31bbf62a6..44762caf6e83 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -1,8 +1,7 @@ -use std::iter; +use std::{assert_matches, iter}; use rustc_abi::Primitive::Pointer; -use rustc_abi::{BackendRepr, ExternAbi, PointerKind, Scalar, Size}; -use rustc_data_structures::assert_matches; +use rustc_abi::{Align, BackendRepr, ExternAbi, PointerKind, Scalar, Size}; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_middle::bug; @@ -12,7 +11,6 @@ FnAbiError, HasTyCtxt, HasTypingEnv, LayoutCx, LayoutOf, TyAndLayout, fn_can_unwind, }; use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt}; -use rustc_session::config::OptLevel; use rustc_span::DUMMY_SP; use rustc_span::def_id::DefId; use rustc_target::callconv::{ @@ -21,7 +19,12 @@ use tracing::debug; pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { fn_abi_of_fn_ptr, fn_abi_of_instance, ..*providers }; + *providers = Providers { + fn_abi_of_fn_ptr, + fn_abi_of_instance_no_deduced_attrs, + fn_abi_of_instance_raw, + ..*providers + }; } // NOTE(eddyb) this is private to avoid using it from outside of @@ -243,33 +246,108 @@ fn fn_sig_for_fn_abi<'tcx>( } } +/// Describes a function for determination of its ABI. +struct FnAbiDesc<'tcx> { + layout_cx: LayoutCx<'tcx>, + sig: ty::FnSig<'tcx>, + + /// The function's definition, if its body can be used to deduce parameter attributes. + determined_fn_def_id: Option, + caller_location: Option>, + is_virtual_call: bool, + extra_args: &'tcx [Ty<'tcx>], +} + +impl<'tcx> FnAbiDesc<'tcx> { + fn for_fn_ptr( + tcx: TyCtxt<'tcx>, + query: ty::PseudoCanonicalInput<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List>)>, + ) -> Self { + let ty::PseudoCanonicalInput { typing_env, value: (sig, extra_args) } = query; + Self { + layout_cx: LayoutCx::new(tcx, typing_env), + sig: tcx.normalize_erasing_regions( + typing_env, + tcx.instantiate_bound_regions_with_erased(sig), + ), + // Parameter attributes can never be deduced for indirect calls, as there is no + // function body available to use. + determined_fn_def_id: None, + caller_location: None, + is_virtual_call: false, + extra_args, + } + } + + fn for_instance( + tcx: TyCtxt<'tcx>, + query: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)>, + ) -> Self { + let ty::PseudoCanonicalInput { typing_env, value: (instance, extra_args) } = query; + let is_virtual_call = matches!(instance.def, ty::InstanceKind::Virtual(..)); + let is_tls_shim_call = matches!(instance.def, ty::InstanceKind::ThreadLocalShim(_)); + Self { + layout_cx: LayoutCx::new(tcx, typing_env), + sig: tcx.normalize_erasing_regions( + typing_env, + fn_sig_for_fn_abi(tcx, instance, typing_env), + ), + // Parameter attributes can be deduced from the bodies of neither: + // - virtual calls, as they might call other functions from the vtable; nor + // - TLS shims, as they would refer to the underlying static. + determined_fn_def_id: (!is_virtual_call && !is_tls_shim_call) + .then(|| instance.def_id()), + caller_location: instance + .def + .requires_caller_location(tcx) + .then(|| tcx.caller_location_ty()), + is_virtual_call, + extra_args, + } + } +} + fn fn_abi_of_fn_ptr<'tcx>( tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List>)>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { - let ty::PseudoCanonicalInput { typing_env, value: (sig, extra_args) } = query; - fn_abi_new_uncached( - &LayoutCx::new(tcx, typing_env), - tcx.instantiate_bound_regions_with_erased(sig), - extra_args, - None, - ) + let desc = FnAbiDesc::for_fn_ptr(tcx, query); + fn_abi_new_uncached(desc) } -fn fn_abi_of_instance<'tcx>( +fn fn_abi_of_instance_no_deduced_attrs<'tcx>( tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { - let ty::PseudoCanonicalInput { typing_env, value: (instance, extra_args) } = query; - fn_abi_new_uncached( - &LayoutCx::new(tcx, typing_env), - fn_sig_for_fn_abi(tcx, instance, typing_env), - extra_args, - Some(instance), - ) + let desc = FnAbiDesc::for_instance(tcx, query); + fn_abi_new_uncached(desc) } -// Handle safe Rust thin and wide pointers. +fn fn_abi_of_instance_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)>, +) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { + // The `fn_abi_of_instance_no_deduced_attrs` query may have been called during CTFE, so we + // delegate to it here in order to reuse (and, if necessary, augment) its result. + tcx.fn_abi_of_instance_no_deduced_attrs(query).map(|fn_abi| { + let params = FnAbiDesc::for_instance(tcx, query); + // If the function's body can be used to deduce parameter attributes, then adjust such + // "no deduced attrs" ABI; otherwise, return that ABI unadjusted. + params.determined_fn_def_id.map_or(fn_abi, |fn_def_id| { + fn_abi_adjust_for_deduced_attrs(¶ms.layout_cx, fn_abi, params.sig.abi, fn_def_id) + }) + }) +} + +/// Returns argument attributes for a scalar argument. +/// +/// `drop_target_pointee`, if set, causes pointer-typed scalars to be treated like mutable +/// references to the given type. This is used to special-case the argument of `ptr::drop_in_place`, +/// interpreting it as `&mut T` instead of `*mut T`, for the purposes of attributes (which is valid +/// as per its safety contract). If `drop_target_pointee` is set, `offset` must be 0 and `layout.ty` +/// must be a pointer to the given type. Note that for wide pointers this function is called twice +/// -- once for the data pointer and once for the vtable pointer. `drop_target_pointee` must only +/// be set for the data pointer. fn arg_attrs_for_rust_scalar<'tcx>( cx: LayoutCx<'tcx>, scalar: Scalar, @@ -302,42 +380,31 @@ fn arg_attrs_for_rust_scalar<'tcx>( let tcx = cx.tcx(); - if let Some(pointee) = layout.pointee_info_at(&cx, offset) { - let kind = if let Some(kind) = pointee.safe { - Some(kind) - } else if let Some(pointee) = drop_target_pointee { - assert_eq!(pointee, layout.ty.builtin_deref(true).unwrap()); - assert_eq!(offset, Size::ZERO); - // The argument to `drop_in_place` is semantically equivalent to a mutable reference. - let mutref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, pointee); - let layout = cx.layout_of(mutref).unwrap(); - layout.pointee_info_at(&cx, offset).and_then(|pi| pi.safe) - } else { - None - }; - if let Some(kind) = kind { + let drop_target_pointee_info = drop_target_pointee.and_then(|pointee| { + assert_eq!(pointee, layout.ty.builtin_deref(true).unwrap()); + assert_eq!(offset, Size::ZERO); + // The argument to `drop_in_place` is semantically equivalent to a mutable reference. + let mutref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, pointee); + let layout = cx.layout_of(mutref).unwrap(); + layout.pointee_info_at(&cx, offset) + }); + + if let Some(pointee) = drop_target_pointee_info.or_else(|| layout.pointee_info_at(&cx, offset)) + { + if pointee.align > Align::ONE { attrs.pointee_align = Some(pointee.align.min(cx.tcx().sess.target.max_reliable_alignment())); + } - attrs.pointee_size = match kind { - // LLVM dereferenceable attribute has unclear semantics on the return type, - // they seem to be "dereferenceable until the end of the program", which is - // generally, not valid for references. See - // - _ if is_return => Size::ZERO, - // `Box` are not necessarily dereferenceable for the entire duration of the function as - // they can be deallocated at any time. Same for non-frozen shared references (see - // ), and for mutable references to - // potentially self-referential types (see - // ). If LLVM had a way - // to say "dereferenceable on entry" we could use it here. - PointerKind::Box { .. } - | PointerKind::SharedRef { frozen: false } - | PointerKind::MutableRef { unpin: false } => Size::ZERO, - PointerKind::SharedRef { frozen: true } - | PointerKind::MutableRef { unpin: true } => pointee.size, - }; + // LLVM dereferenceable attribute has unclear semantics on the return type, + // they seem to be "dereferenceable until the end of the program", which is + // generally, not valid for references. See + // + if !is_return { + attrs.pointee_size = pointee.size; + }; + if let Some(kind) = pointee.safe { // The aliasing rules for `Box` are still not decided, but currently we emit // `noalias` for it. This can be turned off using an unstable flag. // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326 @@ -483,27 +550,21 @@ fn fn_arg_sanity_check<'tcx>( fn_arg_sanity_check(cx, fn_abi, spec_abi, &fn_abi.ret); } -#[tracing::instrument(level = "debug", skip(cx, instance))] +#[tracing::instrument( + level = "debug", + skip(cx, caller_location, determined_fn_def_id, is_virtual_call) +)] fn fn_abi_new_uncached<'tcx>( - cx: &LayoutCx<'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>], - instance: Option>, + FnAbiDesc { + layout_cx: ref cx, + sig, + determined_fn_def_id, + caller_location, + is_virtual_call, + extra_args, + }: FnAbiDesc<'tcx>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let tcx = cx.tcx(); - let (caller_location, determined_fn_def_id, is_virtual_call) = if let Some(instance) = instance - { - let is_virtual_call = matches!(instance.def, ty::InstanceKind::Virtual(..)); - let is_tls_shim_call = matches!(instance.def, ty::InstanceKind::ThreadLocalShim(_)); - ( - instance.def.requires_caller_location(tcx).then(|| tcx.caller_location_ty()), - if is_virtual_call || is_tls_shim_call { None } else { Some(instance.def_id()) }, - is_virtual_call, - ) - } else { - (None, None, false) - }; - let sig = tcx.normalize_erasing_regions(cx.typing_env, sig); let abi_map = AbiMap::from_target(&tcx.sess.target); let conv = abi_map.canonize_abi(sig.abi, sig.c_variadic).unwrap(); @@ -554,7 +615,16 @@ fn fn_abi_new_uncached<'tcx>( }; Ok(ArgAbi::new(cx, layout, |scalar, offset| { - arg_attrs_for_rust_scalar(*cx, scalar, layout, offset, is_return, drop_target_pointee) + arg_attrs_for_rust_scalar( + *cx, + scalar, + layout, + offset, + is_return, + // Only set `drop_target_pointee` for the data part of a wide pointer. + // See `arg_attrs_for_rust_scalar` docs for more information. + drop_target_pointee.filter(|_| offset == Size::ZERO), + ) })) }; @@ -579,16 +649,7 @@ fn fn_abi_new_uncached<'tcx>( sig.abi, ), }; - fn_abi_adjust_for_abi( - cx, - &mut fn_abi, - sig.abi, - // If this is a virtual call, we cannot pass the `fn_def_id`, as it might call other - // functions from vtable. And for a tls shim, passing the `fn_def_id` would refer to - // the underlying static. Internally, `deduced_param_attrs` attempts to infer attributes - // by visit the function body. - determined_fn_def_id, - ); + fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi); debug!("fn_abi_new_uncached = {:?}", fn_abi); fn_abi_sanity_check(cx, &fn_abi, sig.abi); Ok(tcx.arena.alloc(fn_abi)) @@ -599,7 +660,6 @@ fn fn_abi_adjust_for_abi<'tcx>( cx: &LayoutCx<'tcx>, fn_abi: &mut FnAbi<'tcx, Ty<'tcx>>, abi: ExternAbi, - fn_def_id: Option, ) { if abi == ExternAbi::Unadjusted { // The "unadjusted" ABI passes aggregates in "direct" mode. That's fragile but needed for @@ -620,33 +680,38 @@ fn unadjust<'tcx>(arg: &mut ArgAbi<'tcx, Ty<'tcx>>) { for arg in fn_abi.args.iter_mut() { unadjust(arg); } - return; - } - - let tcx = cx.tcx(); - - if abi.is_rustic_abi() { + } else if abi.is_rustic_abi() { fn_abi.adjust_for_rust_abi(cx); - // Look up the deduced parameter attributes for this function, if we have its def ID and - // we're optimizing in non-incremental mode. We'll tag its parameters with those attributes - // as appropriate. - let deduced = - if tcx.sess.opts.optimize != OptLevel::No && tcx.sess.opts.incremental.is_none() { - fn_def_id.map(|fn_def_id| tcx.deduced_param_attrs(fn_def_id)).unwrap_or_default() - } else { - &[] - }; - if !deduced.is_empty() { - apply_deduced_attributes(cx, deduced, 0, &mut fn_abi.ret); - for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() { - apply_deduced_attributes(cx, deduced, arg_idx + 1, arg); - } - } } else { fn_abi.adjust_for_foreign_abi(cx, abi); } } +#[tracing::instrument(level = "trace", skip(cx))] +fn fn_abi_adjust_for_deduced_attrs<'tcx>( + cx: &LayoutCx<'tcx>, + fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>, + abi: ExternAbi, + fn_def_id: DefId, +) -> &'tcx FnAbi<'tcx, Ty<'tcx>> { + let tcx = cx.tcx(); + // Look up the deduced parameter attributes for this function, if we have its def ID. + // We'll tag its parameters with those attributes as appropriate. + let deduced = if abi.is_rustic_abi() { tcx.deduced_param_attrs(fn_def_id) } else { &[] }; + if deduced.is_empty() { + fn_abi + } else { + let mut fn_abi = fn_abi.clone(); + apply_deduced_attributes(cx, deduced, 0, &mut fn_abi.ret); + for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() { + apply_deduced_attributes(cx, deduced, arg_idx + 1, arg); + } + debug!("fn_abi_adjust_for_deduced_attrs = {:?}", fn_abi); + fn_abi_sanity_check(cx, &fn_abi, abi); + tcx.arena.alloc(fn_abi) + } +} + /// Apply deduced optimization attributes to a parameter using an indirect pass mode. /// /// `deduced` is a possibly truncated list of deduced attributes for a return place and arguments. diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 84f52e7fc9d2..497f9205eb19 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -2,7 +2,7 @@ use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::definitions::{DefPathData, DisambiguatorState}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, ImplItemImplKind, ItemKind}; +use rustc_hir::{self as hir, ConstItemRhs, ImplItemImplKind, ItemKind}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -88,7 +88,9 @@ fn associated_item_from_trait_item( let owner_id = trait_item.owner_id; let name = trait_item.ident.name; let kind = match trait_item.kind { - hir::TraitItemKind::Const { .. } => ty::AssocKind::Const { name }, + hir::TraitItemKind::Const(_, _, is_type_const) => { + ty::AssocKind::Const { name, is_type_const: is_type_const.into() } + } hir::TraitItemKind::Fn { .. } => { ty::AssocKind::Fn { name, has_self: fn_has_self_parameter(tcx, owner_id) } } @@ -104,7 +106,9 @@ fn associated_item_from_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_> let owner_id = impl_item.owner_id; let name = impl_item.ident.name; let kind = match impl_item.kind { - hir::ImplItemKind::Const { .. } => ty::AssocKind::Const { name }, + hir::ImplItemKind::Const(_, rhs) => { + ty::AssocKind::Const { name, is_type_const: matches!(rhs, ConstItemRhs::TypeConst(_)) } + } hir::ImplItemKind::Fn { .. } => { ty::AssocKind::Fn { name, has_self: fn_has_self_parameter(tcx, owner_id) } } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 0edbe9624c4b..1d444079f896 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -174,7 +174,6 @@ fn recurse_build<'tcx>( | ExprKind::LoopMatch { .. } => { error(GenericConstantTooComplexSub::LoopNotSupported(node.span))? } - ExprKind::Box { .. } => error(GenericConstantTooComplexSub::BoxNotSupported(node.span))?, ExprKind::ByUse { .. } => { error(GenericConstantTooComplexSub::ByUseNotSupported(node.span))? } @@ -260,7 +259,6 @@ fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { count.has_non_region_param() } thir::ExprKind::Scope { .. } - | thir::ExprKind::Box { .. } | thir::ExprKind::If { .. } | thir::ExprKind::Call { .. } | thir::ExprKind::ByUse { .. } diff --git a/compiler/rustc_ty_utils/src/errors.rs b/compiler/rustc_ty_utils/src/errors.rs index ccea5a49bd7c..07a2c844a717 100644 --- a/compiler/rustc_ty_utils/src/errors.rs +++ b/compiler/rustc_ty_utils/src/errors.rs @@ -51,8 +51,6 @@ pub(crate) enum GenericConstantTooComplexSub { YieldNotSupported(#[primary_span] Span), #[label("loops and loop control flow are not supported in generic constants")] LoopNotSupported(#[primary_span] Span), - #[label("allocations are not allowed in generic constants")] - BoxNotSupported(#[primary_span] Span), #[label("unsupported binary operation in generic constants")] BinaryNotSupported(#[primary_span] Span), #[label(".use is not allowed in generic constants")] diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 990120db9726..09e7eb93504c 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -123,9 +123,11 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' } } } - DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)), + DefKind::AssocConst { .. } | DefKind::AssocTy => { + tcx.assumed_wf_types(tcx.local_parent(def_id)) + } DefKind::Static { .. } - | DefKind::Const + | DefKind::Const { .. } | DefKind::AnonConst | DefKind::InlineConst | DefKind::Struct diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index cbe15eb54787..cf881961b3ce 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -230,13 +230,19 @@ fn resolve_associated_item<'tcx>( if trait_item_id != leaf_def.item.def_id && let Some(leaf_def_item) = leaf_def.item.def_id.as_local() { - tcx.ensure_ok().compare_impl_item(leaf_def_item)?; + tcx.ensure_result().compare_impl_item(leaf_def_item)?; } Some(ty::Instance::new_raw(leaf_def.item.def_id, args)) } traits::ImplSource::Builtin(BuiltinImplSource::Object(_), _) => { let trait_ref = ty::TraitRef::from_assoc(tcx, trait_id, rcvr_args); + + // `final` methods should be called directly. + if tcx.defaultness(trait_item_id).is_final() { + return Ok(Some(ty::Instance::new_raw(trait_item_id, rcvr_args))); + } + if trait_ref.has_non_region_infer() || trait_ref.has_non_region_param() { // We only resolve totally substituted vtable entries. None @@ -380,6 +386,22 @@ fn resolve_associated_item<'tcx>( assert_eq!(name, sym::transmute); let args = tcx.erase_and_anonymize_regions(rcvr_args); Some(ty::Instance::new_raw(trait_item_id, args)) + } else if tcx.is_lang_item(trait_ref.def_id, LangItem::Field) { + if tcx.is_lang_item(trait_item_id, LangItem::FieldOffset) { + let self_ty = trait_ref.self_ty(); + match self_ty.kind() { + ty::Adt(def, _) if def.is_field_representing_type() => {} + _ => bug!("expected field representing type, found {self_ty}"), + } + Some(Instance { + def: ty::InstanceKind::Item( + tcx.lang_items().get(LangItem::FieldOffset).unwrap(), + ), + args: rcvr_args, + }) + } else { + bug!("unexpected associated associated item") + } } else { Instance::try_resolve_item_for_coroutine(tcx, trait_item_id, trait_id, rcvr_args) } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 5a41e46f3a6b..391f50edf23f 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -1,4 +1,5 @@ use hir::def_id::DefId; +use rustc_abi as abi; use rustc_abi::Integer::{I8, I32}; use rustc_abi::Primitive::{self, Float, Int, Pointer}; use rustc_abi::{ @@ -7,7 +8,7 @@ TagEncoding, VariantIdx, Variants, WrappingRange, }; use rustc_hashes::Hash64; -use rustc_hir::attrs::AttributeKind; +use rustc_hir as hir; use rustc_hir::find_attr; use rustc_index::{Idx as _, IndexVec}; use rustc_middle::bug; @@ -23,7 +24,6 @@ use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use rustc_span::{Symbol, sym}; use tracing::{debug, instrument}; -use {rustc_abi as abi, rustc_hir as hir}; use crate::errors::NonPrimitiveSimdType; @@ -624,8 +624,8 @@ fn layout_of_uncached<'tcx>( // Check for the rustc_simd_monomorphize_lane_limit attribute and check the lane limit if let Some(limit) = find_attr!( - tcx.get_all_attrs(def.did()), - AttributeKind::RustcSimdMonomorphizeLaneLimit(limit) => limit + tcx, def.did(), + RustcSimdMonomorphizeLaneLimit(limit) => limit ) { if !limit.value_within_limit(e_len as usize) { return Err(map_error( diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 97c98d0c2403..166e44d3c486 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -1,5 +1,6 @@ +use std::assert_matches; + use rustc_abi::{BackendRepr, FieldsShape, Scalar, Size, TagEncoding, Variants}; -use rustc_data_structures::assert_matches; use rustc_middle::bug; use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index b7f73fd5570e..5f42a134ec9a 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -5,10 +5,8 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(associated_type_defaults)] #![feature(box_patterns)] -#![feature(if_let_guard)] #![feature(iterator_try_collect)] #![feature(never_type)] // tidy-alphabetical-end diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 06eef7e95145..2cff60de9d1c 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -1,7 +1,6 @@ //! Check whether a type has (potentially) non-trivial drop glue. use rustc_data_structures::fx::FxHashSet; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; use rustc_hir::find_attr; use rustc_hir::limit::Limit; @@ -397,7 +396,7 @@ fn adt_consider_insignificant_dtor<'tcx>( tcx: TyCtxt<'tcx>, ) -> impl Fn(ty::AdtDef<'tcx>) -> Option { move |adt_def: ty::AdtDef<'tcx>| { - if find_attr!(tcx.get_all_attrs(adt_def.did()), AttributeKind::RustcInsignificantDtor) { + if find_attr!(tcx, adt_def.did(), RustcInsignificantDtor) { // In some cases like `std::collections::HashMap` where the struct is a wrapper around // a type that is a Drop type, and the wrapped type (eg: `hashbrown::HashMap`) lies // outside stdlib, we might choose to still annotate the wrapper (std HashMap) with diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 445021801bef..027f26cff623 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -40,7 +40,7 @@ enum CollectionMode { impl<'tcx> OpaqueTypeCollector<'tcx> { fn new(tcx: TyCtxt<'tcx>, item: LocalDefId) -> Self { let mode = match tcx.def_kind(item) { - DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy => { + DefKind::AssocConst { .. } | DefKind::AssocFn | DefKind::AssocTy => { CollectionMode::ImplTraitInAssocTypes } DefKind::TyAlias => CollectionMode::Taits, @@ -302,8 +302,8 @@ fn opaque_types_defined_by<'tcx>( DefKind::AssocFn | DefKind::Fn | DefKind::Static { .. } - | DefKind::Const - | DefKind::AssocConst + | DefKind::Const { .. } + | DefKind::AssocConst { .. } | DefKind::AnonConst => { collector.collect_taits_declared_in_body(); } diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index 33d334092ba9..1814e7604a2d 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -6,69 +6,71 @@ use rustc_span::def_id::LocalDefId; pub(crate) fn provide(providers: &mut Providers) { - *providers = - Providers { representability, representability_adt_ty, params_in_repr, ..*providers }; -} - -macro_rules! rtry { - ($e:expr) => { - match $e { - e @ Representability::Infinite(_) => return e, - Representability::Representable => {} - } + *providers = Providers { + check_representability, + check_representability_adt_ty, + params_in_repr, + ..*providers }; } -fn representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representability { +fn check_representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representability { match tcx.def_kind(def_id) { DefKind::Struct | DefKind::Union | DefKind::Enum => { for variant in tcx.adt_def(def_id).variants() { for field in variant.fields.iter() { - rtry!(tcx.representability(field.did.expect_local())); + let _ = tcx.check_representability(field.did.expect_local()); } } - Representability::Representable } - DefKind::Field => representability_ty(tcx, tcx.type_of(def_id).instantiate_identity()), + DefKind::Field => { + check_representability_ty(tcx, tcx.type_of(def_id).instantiate_identity()); + } def_kind => bug!("unexpected {def_kind:?}"), } + Representability } -fn representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability { +fn check_representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { match *ty.kind() { - ty::Adt(..) => tcx.representability_adt_ty(ty), + // This one must be a query rather than a vanilla `check_representability_adt_ty` call. See + // the comment on `check_representability_adt_ty` below for why. + ty::Adt(..) => { + let _ = tcx.check_representability_adt_ty(ty); + } // FIXME(#11924) allow zero-length arrays? - ty::Array(ty, _) => representability_ty(tcx, ty), + ty::Array(ty, _) => { + check_representability_ty(tcx, ty); + } ty::Tuple(tys) => { for ty in tys { - rtry!(representability_ty(tcx, ty)); + check_representability_ty(tcx, ty); } - Representability::Representable } - _ => Representability::Representable, + _ => {} } } -/* -The reason for this being a separate query is very subtle: -Consider this infinitely sized struct: `struct Foo(Box, Bar)`: -When calling representability(Foo), a query cycle will occur: - representability(Foo) - -> representability_adt_ty(Bar) - -> representability(Foo) -For the diagnostic output (in `Value::from_cycle_error`), we want to detect that -the `Foo` in the *second* field of the struct is culpable. This requires -traversing the HIR of the struct and calling `params_in_repr(Bar)`. But we can't -call params_in_repr for a given type unless it is known to be representable. -params_in_repr will cycle/panic on infinitely sized types. Looking at the query -cycle above, we know that `Bar` is representable because -representability_adt_ty(Bar<..>) is in the cycle and representability(Bar) is -*not* in the cycle. -*/ -fn representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability { +// The reason for this being a separate query is very subtle. Consider this +// infinitely sized struct: `struct Foo(Box, Bar)`. When calling +// check_representability(Foo), a query cycle will occur: +// +// check_representability(Foo) +// -> check_representability_adt_ty(Bar) +// -> check_representability(Foo) +// +// For the diagnostic output (in `Value::from_cycle_error`), we want to detect +// that the `Foo` in the *second* field of the struct is culpable. This +// requires traversing the HIR of the struct and calling `params_in_repr(Bar)`. +// But we can't call params_in_repr for a given type unless it is known to be +// representable. params_in_repr will cycle/panic on infinitely sized types. +// Looking at the query cycle above, we know that `Bar` is representable +// because `check_representability_adt_ty(Bar<..>)` is in the cycle and +// `check_representability(Bar)` is *not* in the cycle. +fn check_representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability { let ty::Adt(adt, args) = ty.kind() else { bug!("expected adt") }; if let Some(def_id) = adt.did().as_local() { - rtry!(tcx.representability(def_id)); + let _ = tcx.check_representability(def_id); } // At this point, we know that the item of the ADT type is representable; // but the type parameters may cause a cycle with an upstream type @@ -76,11 +78,11 @@ fn representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representab for (i, arg) in args.iter().enumerate() { if let ty::GenericArgKind::Type(ty) = arg.kind() { if params_in_repr.contains(i as u32) { - rtry!(representability_ty(tcx, ty)); + check_representability_ty(tcx, ty); } } } - Representability::Representable + Representability } fn params_in_repr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> DenseBitSet { diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index 07f379d3f9a8..796aa2093bd5 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -44,7 +44,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( // Walk over the type behind the alias DefKind::TyAlias { .. } | DefKind::AssocTy | // Walk over the type of the item - DefKind::Static { .. } | DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => { + DefKind::Static { .. } | DefKind::Const { .. } | DefKind::AssocConst { .. } | DefKind::AnonConst => { if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { // If the type of the item uses `_`, we're gonna error out anyway, but // typeck (which type_of invokes below), will call back into opaque_types_defined_by diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index d6a29aba22b9..78f3a1922887 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -116,11 +116,10 @@ fn adt_sizedness_constraint<'tcx>( tcx: TyCtxt<'tcx>, (def_id, sizedness): (DefId, SizedTraitKind), ) -> Option>> { - if let Some(def_id) = def_id.as_local() - && let ty::Representability::Infinite(_) = tcx.representability(def_id) - { - return None; + if let Some(def_id) = def_id.as_local() { + let _ = tcx.check_representability(def_id); } + let def = tcx.adt_def(def_id); if !def.is_struct() { diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index fbe382712893..10c53c881a36 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -11,6 +11,7 @@ derive-where = "1.2.7" ena = "0.14.4" indexmap = "2.0.0" rustc-hash = "2.0.0" +rustc_abi = { path = "../rustc_abi", default-features = false } rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_error_messages = { path = "../rustc_error_messages", optional = true } diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 1c39f31469b1..49fb94c830e2 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -140,7 +140,7 @@ fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { /// /// `ValTree` does not have this problem with representation, as it only contains integers or /// lists of (nested) `ty::Const`s (which may indirectly contain more `ValTree`s). -#[derive_where(Clone, Debug, Hash, Eq, PartialEq; I: Interner)] +#[derive_where(Clone, Copy, Debug, Hash, Eq, PartialEq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", @@ -159,8 +159,7 @@ pub enum ValTreeKind { /// the fields of the variant. /// /// ZST types are represented as an empty slice. - // FIXME(mgca): Use a `List` here instead of a boxed slice - Branch(Box<[I::Const]>), + Branch(I::Consts), } impl ValTreeKind { @@ -177,9 +176,9 @@ pub fn to_leaf(&self) -> I::ScalarInt { /// Converts to a `ValTreeKind::Branch` value, `panic`'ing /// if this valtree is some other kind. #[inline] - pub fn to_branch(&self) -> &[I::Const] { + pub fn to_branch(&self) -> I::Consts { match self { - ValTreeKind::Branch(branch) => &**branch, + ValTreeKind::Branch(branch) => *branch, ValTreeKind::Leaf(..) => panic!("expected branch, got {:?}", self), } } @@ -193,9 +192,9 @@ pub fn try_to_leaf(&self) -> Option { } /// Attempts to convert to a `ValTreeKind::Branch` value. - pub fn try_to_branch(&self) -> Option<&[I::Const]> { + pub fn try_to_branch(&self) -> Option { match self { - ValTreeKind::Branch(branch) => Some(&**branch), + ValTreeKind::Branch(branch) => Some(*branch), ValTreeKind::Leaf(_) => None, } } diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 8b057e5866cd..7e905da0785f 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -482,8 +482,8 @@ fn add_const_kind(&mut self, c: &ty::ConstKind) { match cv.valtree().kind() { ty::ValTreeKind::Leaf(_) => (), ty::ValTreeKind::Branch(cts) => { - for ct in cts { - self.add_const(*ct); + for ct in cts.iter() { + self.add_const(ct); } } } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index c9580d589d21..116fd100bf0a 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -13,7 +13,9 @@ use crate::relate::Relate; use crate::solve::{AdtDestructorKind, SizedTraitKind}; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; -use crate::{self as ty, ClauseKind, CollectAndApply, Interner, PredicateKind, UpcastFrom}; +use crate::{ + self as ty, ClauseKind, CollectAndApply, FieldInfo, Interner, PredicateKind, UpcastFrom, +}; pub trait Ty>: Copy @@ -292,12 +294,6 @@ pub trait ValueConst>: Copy + Debug + Hash + Eq { fn valtree(self) -> I::ValTree; } -// FIXME(mgca): This trait can be removed once we're not using a `Box` in `Branch` -pub trait ValTree>: Copy + Debug + Hash + Eq { - // This isnt' `IntoKind` because then we can't return a reference - fn kind(&self) -> &ty::ValTreeKind; -} - pub trait ExprConst>: Copy + Debug + Hash + Eq + Relate { fn args(self) -> I::GenericArgs; } @@ -577,6 +573,8 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn is_struct(self) -> bool; + fn is_packed(self) -> bool; + /// Returns the type of the struct tail. /// /// Expects the `AdtDef` to be a struct. If it is not, then this will panic. @@ -586,6 +584,12 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn is_manually_drop(self) -> bool; + fn field_representing_type_info( + self, + interner: I, + args: I::GenericArgs, + ) -> Option>; + // FIXME: perhaps use `all_fields` and expose `FieldDef`. fn all_field_tys(self, interner: I) -> ty::EarlyBinder>; diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 8f446cdfba6d..e77e7af071b9 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -150,10 +150,11 @@ fn mk_tracked( // Kinds of consts type Const: Const; + type Consts: Copy + Debug + Hash + Eq + SliceLike + Default; type ParamConst: Copy + Debug + Hash + Eq + ParamLike; type ValueConst: ValueConst; type ExprConst: ExprConst; - type ValTree: ValTree; + type ValTree: Copy + Debug + Hash + Eq + IntoKind>; type ScalarInt: Copy + Debug + Hash + Eq; // Kinds of regions diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 39b575ebab63..f1c45a4d98b5 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -9,6 +9,8 @@ pub enum SolverLangItem { CoroutineReturn, CoroutineYield, DynMetadata, + FieldBase, + FieldType, FutureOutput, Metadata, // tidy-alphabetical-end @@ -36,6 +38,7 @@ pub enum SolverTraitLangItem { Destruct, DiscriminantKind, Drop, + Field, Fn, FnMut, FnOnce, diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 3d9fc7f5c998..62965f947387 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -13,6 +13,7 @@ use std::fmt; use std::hash::Hash; +use rustc_abi::{FieldIdx, VariantIdx}; #[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable, HashStable_NoContext}; @@ -440,3 +441,12 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.as_str().fmt(f) } } + +pub struct FieldInfo { + pub base: I::Ty, + pub ty: I::Ty, + pub variant: Option, + pub variant_idx: VariantIdx, + pub name: I::Symbol, + pub field_idx: FieldIdx, +} diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 42d204a8b094..113192cc02eb 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -42,7 +42,9 @@ fn lift_to_interner(self, cx: U) -> Option { } } -/// A complete reference to a trait. These take numerous guises in syntax, +/// A complete reference to a trait. +/// +/// These take numerous guises in syntax, /// but perhaps the most recognizable form is in a where-clause: /// ```ignore (illustrative) /// T: Foo @@ -241,7 +243,9 @@ pub fn as_str(self) -> &'static str { } } -/// Polarity for a trait predicate. May either be negative or positive. +/// Polarity for a trait predicate. +/// +/// May either be negative or positive. /// Distinguished from [`ImplPolarity`] since we never compute goals with /// "reservation" level. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] @@ -327,6 +331,7 @@ pub fn with_self_ty(&self, cx: I, self_ty: I::Ty) -> I::Clause { } /// An existential reference to a trait, where `Self` is erased. +/// /// For example, the trait object `Trait<'a, 'b, X, Y>` is: /// ```ignore (illustrative) /// exists T. T: Trait<'a, 'b, X, Y> @@ -415,6 +420,7 @@ pub struct ExistentialProjection { /// This field exists to prevent the creation of `ExistentialProjection` /// without using [`ExistentialProjection::new_from_args`]. + #[derive_where(skip(Debug))] use_existential_projection_new_instead: (), } @@ -442,6 +448,7 @@ pub fn new( } /// Extracts the underlying existential trait reference from this projection. + /// /// For example, if this is a projection of `exists T. ::Item == X`, /// then this function would return an `exists T. T: Iterator` existential trait /// reference. @@ -493,14 +500,17 @@ pub fn item_def_id(&self) -> I::DefId { #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum AliasTermKind { /// A projection `::AssocType`. + /// /// Can get normalized away if monomorphic enough. ProjectionTy, /// An associated type in an inherent `impl` InherentTy, /// An opaque type (usually from `impl Trait` in type aliases or function return types) + /// /// Can only be normalized away in PostAnalysis mode or its defining scope. OpaqueTy, /// A free type alias that actually checks its trait bounds. + /// /// Currently only used if the type alias references opaque types. /// Can always be normalized away. FreeTy, diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 3610605462ba..d33c6036dadd 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -577,9 +577,9 @@ pub fn structurally_relate_consts>( if branches_a.len() == branches_b.len() => { branches_a - .into_iter() - .zip(branches_b) - .all(|(a, b)| relation.relate(*a, *b).is_ok()) + .iter() + .zip(branches_b.iter()) + .all(|(a, b)| relation.relate(a, b).is_ok()) } _ => false, } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 8e6376b22ce6..7c58cd7303eb 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -916,10 +916,9 @@ fn clear_dependent_provisional_results_for_rerun(&mut self) { /// heads from the stack. This may not necessarily mean that we've actually /// reached a fixpoint for that cycle head, which impacts the way we rebase /// provisional cache entries. -#[derive_where(Debug; X: Cx)] -enum RebaseReason { +#[derive(Debug)] +enum RebaseReason { NoCycleUsages, - Ambiguity(X::AmbiguityInfo), Overflow, /// We've actually reached a fixpoint. /// @@ -956,7 +955,7 @@ fn rebase_provisional_cache_entries( &mut self, cx: X, stack_entry: &StackEntry, - rebase_reason: RebaseReason, + rebase_reason: RebaseReason, ) { let popped_head_index = self.stack.next_index(); #[allow(rustc::potential_query_instability)] @@ -1035,9 +1034,6 @@ fn rebase_provisional_cache_entries( // is not actually equal to the final provisional result. We // need to discard the provisional cache entry in this case. RebaseReason::NoCycleUsages => return false, - RebaseReason::Ambiguity(info) => { - *result = D::propagate_ambiguity(cx, input, info); - } RebaseReason::Overflow => *result = D::fixpoint_overflow_result(cx, input), RebaseReason::ReachedFixpoint(None) => {} RebaseReason::ReachedFixpoint(Some(path_kind)) => { @@ -1352,27 +1348,6 @@ fn evaluate_goal_in_task( return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } - // If computing this goal results in ambiguity with no constraints, - // we do not rerun it. It's incredibly difficult to get a different - // response in the next iteration in this case. These changes would - // likely either be caused by incompleteness or can change the maybe - // cause from ambiguity to overflow. Returning ambiguity always - // preserves soundness and completeness even if the goal is be known - // to succeed or fail. - // - // This prevents exponential blowup affecting multiple major crates. - // As we only get to this branch if we haven't yet reached a fixpoint, - // we also taint all provisional cache entries which depend on the - // current goal. - if let Some(info) = D::is_ambiguous_result(result) { - self.rebase_provisional_cache_entries( - cx, - &stack_entry, - RebaseReason::Ambiguity(info), - ); - return EvaluationResult::finalize(stack_entry, encountered_overflow, result); - }; - // If we've reached the fixpoint step limit, we bail with overflow and taint all // provisional cache entries which depend on the current goal. i += 1; diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 7cb71387e868..983d8f0820b6 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -29,14 +29,17 @@ )] pub enum AliasTyKind { /// A projection `::AssocType`. + /// /// Can get normalized away if monomorphic enough. Projection, /// An associated type in an inherent `impl` Inherent, /// An opaque type (usually from `impl Trait` in type aliases or function return types) + /// /// Can only be normalized away in PostAnalysis mode or its defining scope. Opaque, /// A type alias that actually checks its trait bounds. + /// /// Currently only used if the type alias references opaque types. /// Can always be normalized away. Free, @@ -99,7 +102,9 @@ pub enum TyKind { /// An array with the given length. Written as `[T; N]`. Array(I::Ty, I::Const), - /// A pattern newtype. Takes any type and restricts its valid values to its pattern. + /// A pattern newtype. + /// + /// Takes any type and restricts its valid values to its pattern. /// This will also change the layout to take advantage of this restriction. /// Only `Copy` and `Clone` will automatically get implemented for pattern types. /// Auto-traits treat this as if it were an aggregate with a single nested type. @@ -116,8 +121,9 @@ pub enum TyKind { /// `&'a mut T` or `&'a T`. Ref(I::Region, I::Ty, Mutability), - /// The anonymous type of a function declaration/definition. Each - /// function has a unique type. + /// The anonymous type of a function declaration/definition. + /// + /// Each function has a unique type. /// /// For the function `fn foo() -> i32 { 3 }` this type would be /// shown to the user as `fn() -> i32 {foo}`. @@ -129,7 +135,9 @@ pub enum TyKind { /// ``` FnDef(I::FunctionId, I::GenericArgs), - /// A pointer to a function. Written as `fn() -> i32`. + /// A pointer to a function. + /// + /// Written as `fn() -> i32`. /// /// Note that both functions and closures start out as either /// [FnDef] or [Closure] which can be then be coerced to this variant. @@ -179,6 +187,7 @@ pub enum TyKind { Coroutine(I::CoroutineId, I::GenericArgs), /// A type representing the types stored inside a coroutine. + /// /// This should only appear as part of the `CoroutineArgs`. /// /// Unlike upvars, the witness can reference lifetimes from @@ -210,6 +219,7 @@ pub enum TyKind { Tuple(I::Tys), /// A projection, opaque type, free type alias, or inherent associated type. + /// /// All of these types are represented as pairs of def-id and args, and can /// be normalized, so they are grouped conceptually. Alias(AliasTyKind, AliasTy), @@ -253,8 +263,9 @@ pub enum TyKind { /// inside of the type. Infer(InferTy), - /// A placeholder for a type which could not be computed; this is - /// propagated to avoid useless error messages. + /// A placeholder for a type which could not be computed. + /// + /// This is propagated to avoid useless error messages. Error(I::ErrorGuaranteed), } @@ -282,7 +293,9 @@ pub fn fn_sig(self, interner: I) -> ty::Binder> { } /// Returns `true` when the outermost type cannot be further normalized, - /// resolved, or instantiated. This includes all primitive types, but also + /// resolved, or instantiated. + /// + /// This includes all primitive types, but also /// things like ADTs and trait objects, since even if their arguments or /// nested types may be further simplified, the outermost [`ty::TyKind`] or /// type constructor remains the same. @@ -481,6 +494,7 @@ pub fn trait_def_id(self, interner: I) -> I::DefId { } /// Extracts the underlying trait reference and own args from this projection. + /// /// For example, if this is a projection of `::Item<'a>`, /// then this function would return a `T: StreamingIterator` trait reference and /// `['a]` as the own args. @@ -490,6 +504,7 @@ pub fn trait_ref_and_own_args(self, interner: I) -> (ty::TraitRef, I::Generic } /// Extracts the underlying trait reference from this projection. + /// /// For example, if this is a projection of `::Item`, /// then this function would return a `T: Iterator` trait reference. /// @@ -593,8 +608,9 @@ pub enum InferTy { FloatVar(FloatVid), /// A [`FreshTy`][Self::FreshTy] is one that is generated as a replacement - /// for an unbound type variable. This is convenient for caching etc. See - /// `TypeFreshener` for more details. + /// for an unbound type variable. + /// + /// This is convenient for caching etc. See `TypeFreshener` for more details. /// /// Compare with [`TyVar`][Self::TyVar]. FreshTy(u32), diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 263bb1036d8c..7e09a88156a0 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -22,11 +22,16 @@ #[rustc_deallocator] #[rustc_nounwind] #[rustc_std_internal_symbol] - fn __rust_dealloc(ptr: *mut u8, size: usize, align: Alignment); + fn __rust_dealloc(ptr: NonNull, size: usize, align: Alignment); #[rustc_reallocator] #[rustc_nounwind] #[rustc_std_internal_symbol] - fn __rust_realloc(ptr: *mut u8, old_size: usize, align: Alignment, new_size: usize) -> *mut u8; + fn __rust_realloc( + ptr: NonNull, + old_size: usize, + align: Alignment, + new_size: usize, + ) -> *mut u8; #[rustc_allocator_zeroed] #[rustc_nounwind] #[rustc_std_internal_symbol] @@ -112,6 +117,13 @@ pub unsafe fn alloc(layout: Layout) -> *mut u8 { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + unsafe { dealloc_nonnull(NonNull::new_unchecked(ptr), layout) } +} + +/// Same as [`dealloc`] but when you already have a non-null pointer +#[inline] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +unsafe fn dealloc_nonnull(ptr: NonNull, layout: Layout) { unsafe { __rust_dealloc(ptr, layout.size(), layout.alignment()) } } @@ -132,6 +144,13 @@ pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + unsafe { realloc_nonnull(NonNull::new_unchecked(ptr), layout, new_size) } +} + +/// Same as [`realloc`] but when you already have a non-null pointer +#[inline] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +unsafe fn realloc_nonnull(ptr: NonNull, layout: Layout, new_size: usize) -> *mut u8 { unsafe { __rust_realloc(ptr, layout.size(), layout.alignment(), new_size) } } @@ -206,7 +225,7 @@ fn deallocate_impl_runtime(ptr: NonNull, layout: Layout) { // allocation than requested. // * Other conditions must be upheld by the caller, as per `Allocator::deallocate()`'s // safety documentation. - unsafe { dealloc(ptr.as_ptr(), layout) } + unsafe { dealloc_nonnull(ptr, layout) } } } @@ -236,7 +255,7 @@ fn grow_impl_runtime( // `realloc` probably checks for `new_size >= old_layout.size()` or something similar. hint::assert_unchecked(new_size >= old_layout.size()); - let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); + let raw_ptr = realloc_nonnull(ptr, old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; if zeroed { raw_ptr.add(old_size).write_bytes(0, new_size - old_size); @@ -285,7 +304,7 @@ fn shrink_impl_runtime( // `realloc` probably checks for `new_size <= old_layout.size()` or something similar. hint::assert_unchecked(new_size <= old_layout.size()); - let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); + let raw_ptr = realloc_nonnull(ptr, old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(NonNull::slice_from_raw_parts(ptr, new_size)) }, @@ -479,19 +498,6 @@ unsafe fn shrink( } } -/// The allocator for `Box`. -#[cfg(not(no_global_oom_handling))] -#[lang = "exchange_malloc"] -#[inline] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { - let layout = unsafe { Layout::from_size_align_unchecked(size, align) }; - match Global.allocate(layout) { - Ok(ptr) => ptr.as_mut_ptr(), - Err(_) => handle_alloc_error(layout), - } -} - // # Allocation error handler #[cfg(not(no_global_oom_handling))] diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 6391a6977b61..ae16a8401552 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -236,14 +236,34 @@ pub struct Box< #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, >(Unique, A); -/// Constructs a `Box` by calling the `exchange_malloc` lang item and moving the argument into -/// the newly allocated memory. This is an intrinsic to avoid unnecessary copies. +/// Monomorphic function for allocating an uninit `Box`. +#[inline] +// The is a separate function to avoid doing it in every generic version, but it +// looks small to the mir inliner (particularly in panic=abort) so leave it to +// the backend to decide whether pulling it in everywhere is worth doing. +#[rustc_no_mir_inline] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[cfg(not(no_global_oom_handling))] +fn box_new_uninit(layout: Layout) -> *mut u8 { + match Global.allocate(layout) { + Ok(ptr) => ptr.as_mut_ptr(), + Err(_) => handle_alloc_error(layout), + } +} + +/// Helper for `vec!`. /// -/// This is the surface syntax for `box ` expressions. +/// This is unsafe, but has to be marked as safe or else we couldn't use it in `vec!`. #[doc(hidden)] -#[rustc_intrinsic] #[unstable(feature = "liballoc_internals", issue = "none")] -pub fn box_new(x: T) -> Box; +#[inline(always)] +#[cfg(not(no_global_oom_handling))] +#[rustc_diagnostic_item = "box_assume_init_into_vec_unsafe"] +pub fn box_assume_init_into_vec_unsafe( + b: Box>, +) -> crate::vec::Vec { + unsafe { (b.assume_init() as Box<[T]>).into_vec() } +} impl Box { /// Allocates memory on the heap and then places `x` into it. @@ -262,7 +282,13 @@ impl Box { #[rustc_diagnostic_item = "box_new"] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn new(x: T) -> Self { - return box_new(x); + // This is `Box::new_uninit` but inlined to avoid build time regressions. + let ptr = box_new_uninit(::LAYOUT) as *mut T; + // Nothing below can panic so we do not have to worry about deallocating `ptr`. + // SAFETY: we just allocated the box to store `x`. + unsafe { core::intrinsics::write_via_move(ptr, x) }; + // SAFETY: we just initialized `b`. + unsafe { mem::transmute(ptr) } } /// Constructs a new box with uninitialized contents. @@ -280,9 +306,15 @@ pub fn new(x: T) -> Self { #[cfg(not(no_global_oom_handling))] #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use] - #[inline] + #[inline(always)] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn new_uninit() -> Box> { - Self::new_uninit_in(Global) + // This is the same as `Self::new_uninit_in(Global)`, but manually inlined (just like + // `Box::new`). + + // SAFETY: + // - If `allocate` succeeds, the returned pointer exactly matches what `Box` needs. + unsafe { mem::transmute(box_new_uninit(::LAYOUT)) } } /// Constructs a new `Box` with uninitialized contents, with the memory @@ -1142,10 +1174,12 @@ impl Box, A> { /// assert_eq!(*five, 5) /// ``` #[stable(feature = "new_uninit", since = "1.82.0")] - #[inline] + #[inline(always)] pub unsafe fn assume_init(self) -> Box { - let (raw, alloc) = Box::into_raw_with_allocator(self); - unsafe { Box::from_raw_in(raw as *mut T, alloc) } + // This is used in the `vec!` macro, so we optimize for minimal IR generation + // even in debug builds. + // SAFETY: `Box` and `Box>` have the same layout. + unsafe { core::intrinsics::transmute_unchecked(self) } } /// Writes the value and converts to `Box`. diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 426be364a56b..fdeb9e332c7e 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -693,6 +693,7 @@ pub fn clear(&mut self) { /// map.insert(1, "a"); /// ``` #[unstable(feature = "btreemap_alloc", issue = "32838")] + #[must_use] pub const fn new_in(alloc: A) -> BTreeMap { BTreeMap { root: None, length: 0, alloc: ManuallyDrop::new(alloc), _marker: PhantomData } } @@ -1240,6 +1241,162 @@ pub fn append(&mut self, other: &mut Self) ) } + /// Moves all elements from `other` into `self`, leaving `other` empty. + /// + /// If a key from `other` is already present in `self`, then the `conflict` + /// closure is used to return a value to `self`. The `conflict` + /// closure takes in a borrow of `self`'s key, `self`'s value, and `other`'s value + /// in that order. + /// + /// An example of why one might use this method over [`append`] + /// is to combine `self`'s value with `other`'s value when their keys conflict. + /// + /// Similar to [`insert`], though, the key is not overwritten, + /// which matters for types that can be `==` without being identical. + /// + /// [`insert`]: BTreeMap::insert + /// [`append`]: BTreeMap::append + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_merge)] + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(1, String::from("a")); + /// a.insert(2, String::from("b")); + /// a.insert(3, String::from("c")); // Note: Key (3) also present in b. + /// + /// let mut b = BTreeMap::new(); + /// b.insert(3, String::from("d")); // Note: Key (3) also present in a. + /// b.insert(4, String::from("e")); + /// b.insert(5, String::from("f")); + /// + /// // concatenate a's value and b's value + /// a.merge(b, |_, a_val, b_val| { + /// format!("{a_val}{b_val}") + /// }); + /// + /// assert_eq!(a.len(), 5); // all of b's keys in a + /// + /// assert_eq!(a[&1], "a"); + /// assert_eq!(a[&2], "b"); + /// assert_eq!(a[&3], "cd"); // Note: "c" has been combined with "d". + /// assert_eq!(a[&4], "e"); + /// assert_eq!(a[&5], "f"); + /// ``` + #[unstable(feature = "btree_merge", issue = "152152")] + pub fn merge(&mut self, mut other: Self, mut conflict: impl FnMut(&K, V, V) -> V) + where + K: Ord, + A: Clone, + { + // Do we have to append anything at all? + if other.is_empty() { + return; + } + + // We can just swap `self` and `other` if `self` is empty. + if self.is_empty() { + mem::swap(self, &mut other); + return; + } + + let mut other_iter = other.into_iter(); + let (first_other_key, first_other_val) = other_iter.next().unwrap(); + + // find the first gap that has the smallest key greater than or equal to + // the first key from other + let mut self_cursor = self.lower_bound_mut(Bound::Included(&first_other_key)); + + if let Some((self_key, _)) = self_cursor.peek_next() { + match K::cmp(self_key, &first_other_key) { + Ordering::Equal => { + // if `f` unwinds, the next entry is already removed leaving + // the tree in valid state. + // FIXME: Once `MaybeDangling` is implemented, we can optimize + // this through using a drop handler and transmutating CursorMutKey + // to CursorMutKey, ManuallyDrop> (see PR #152418) + if let Some((k, v)) = self_cursor.remove_next() { + // SAFETY: we remove the K, V out of the next entry, + // apply 'f' to get a new (K, V), and insert it back + // into the next entry that the cursor is pointing at + let v = conflict(&k, v, first_other_val); + unsafe { self_cursor.insert_after_unchecked(k, v) }; + } + } + Ordering::Greater => + // SAFETY: we know our other_key's ordering is less than self_key, + // so inserting before will guarantee sorted order + unsafe { + self_cursor.insert_before_unchecked(first_other_key, first_other_val); + }, + Ordering::Less => { + unreachable!("Cursor's peek_next should return None."); + } + } + } else { + // SAFETY: reaching here means our cursor is at the end + // self BTreeMap so we just insert other_key here + unsafe { + self_cursor.insert_before_unchecked(first_other_key, first_other_val); + } + } + + for (other_key, other_val) in other_iter { + loop { + if let Some((self_key, _)) = self_cursor.peek_next() { + match K::cmp(self_key, &other_key) { + Ordering::Equal => { + // if `f` unwinds, the next entry is already removed leaving + // the tree in valid state. + // FIXME: Once `MaybeDangling` is implemented, we can optimize + // this through using a drop handler and transmutating CursorMutKey + // to CursorMutKey, ManuallyDrop> (see PR #152418) + if let Some((k, v)) = self_cursor.remove_next() { + // SAFETY: we remove the K, V out of the next entry, + // apply 'f' to get a new (K, V), and insert it back + // into the next entry that the cursor is pointing at + let v = conflict(&k, v, other_val); + unsafe { self_cursor.insert_after_unchecked(k, v) }; + } + break; + } + Ordering::Greater => { + // SAFETY: we know our self_key's ordering is greater than other_key, + // so inserting before will guarantee sorted order + unsafe { + self_cursor.insert_before_unchecked(other_key, other_val); + } + break; + } + Ordering::Less => { + // FIXME: instead of doing a linear search here, + // this can be optimized to search the tree by starting + // from self_cursor and going towards the root and then + // back down to the proper node -- that should probably + // be a new method on Cursor*. + self_cursor.next(); + } + } + } else { + // FIXME: If we get here, that means all of other's keys are greater than + // self's keys. For performance, this should really do a bulk insertion of items + // from other_iter into the end of self `BTreeMap`. Maybe this should be + // a method for Cursor*? + + // SAFETY: reaching here means our cursor is at the end + // self BTreeMap so we just insert other_key here + unsafe { + self_cursor.insert_before_unchecked(other_key, other_val); + } + break; + } + } + } + } + /// Constructs a double-ended iterator over a sub-range of elements in the map. /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will /// yield elements from min (inclusive) to max (exclusive). diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 938e867b8581..73546caa05ea 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -1,9 +1,9 @@ use core::assert_matches; -use std::iter; use std::ops::Bound::{Excluded, Included, Unbounded}; use std::panic::{AssertUnwindSafe, catch_unwind}; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; +use std::{cmp, iter}; use super::*; use crate::boxed::Box; @@ -2128,6 +2128,86 @@ fn $name() { #[cfg(not(miri))] // Miri is too slow create_append_test!(test_append_1700, 1700); +// a inserts (0, 0)..(8, 8) to its own tree +// b inserts (5, 5 * 2)..($len, 2 * $len) to its own tree +// note that between a and b, there are duplicate keys +// between 5..min($len, 8), so on merge we add the values +// of these keys together +// we check that: +// - the merged tree 'a' has a length of max(8, $len) +// - all keys in 'a' have the correct value associated +// - removing and inserting an element into the merged +// tree 'a' still keeps it in valid tree form +macro_rules! create_merge_test { + ($name:ident, $len:expr) => { + #[test] + fn $name() { + let mut a = BTreeMap::new(); + for i in 0..8 { + a.insert(i, i); + } + + let mut b = BTreeMap::new(); + for i in 5..$len { + b.insert(i, 2 * i); + } + + a.merge(b, |_, a_val, b_val| a_val + b_val); + + assert_eq!(a.len(), cmp::max($len, 8)); + + for i in 0..cmp::max($len, 8) { + if i < 5 { + assert_eq!(a[&i], i); + } else { + if i < cmp::min($len, 8) { + assert_eq!(a[&i], i + 2 * i); + } else if i >= $len { + assert_eq!(a[&i], i); + } else { + assert_eq!(a[&i], 2 * i); + } + } + } + + a.check(); + assert_eq!( + a.remove(&($len - 1)), + if $len >= 5 && $len < 8 { + Some(($len - 1) + 2 * ($len - 1)) + } else { + Some(2 * ($len - 1)) + } + ); + assert_eq!(a.insert($len - 1, 20), None); + a.check(); + } + }; +} + +// These are mostly for testing the algorithm that "fixes" the right edge after insertion. +// Single node, merge conflicting key values. +create_merge_test!(test_merge_7, 7); +// Single node. +create_merge_test!(test_merge_9, 9); +// Two leafs that don't need fixing. +create_merge_test!(test_merge_17, 17); +// Two leafs where the second one ends up underfull and needs stealing at the end. +create_merge_test!(test_merge_14, 14); +// Two leafs where the second one ends up empty because the insertion finished at the root. +create_merge_test!(test_merge_12, 12); +// Three levels; insertion finished at the root. +create_merge_test!(test_merge_144, 144); +// Three levels; insertion finished at leaf while there is an empty node on the second level. +create_merge_test!(test_merge_145, 145); +// Tests for several randomly chosen sizes. +create_merge_test!(test_merge_170, 170); +create_merge_test!(test_merge_181, 181); +#[cfg(not(miri))] // Miri is too slow +create_merge_test!(test_merge_239, 239); +#[cfg(not(miri))] // Miri is too slow +create_merge_test!(test_merge_1700, 1700); + #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_append_drop_leak() { @@ -2169,6 +2249,84 @@ fn test_append_ord_chaos() { map2.check(); } +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_merge_drop_leak() { + let a = CrashTestDummy::new(0); + let b = CrashTestDummy::new(1); + let c = CrashTestDummy::new(2); + let mut left = BTreeMap::new(); + let mut right = BTreeMap::new(); + left.insert(a.spawn(Panic::Never), ()); + left.insert(b.spawn(Panic::Never), ()); + left.insert(c.spawn(Panic::Never), ()); + right.insert(b.spawn(Panic::InDrop), ()); // first duplicate key, dropped during merge + right.insert(c.spawn(Panic::Never), ()); + + catch_unwind(move || left.merge(right, |_, _, _| ())).unwrap_err(); + assert_eq!(a.dropped(), 1); // this should not be dropped + assert_eq!(b.dropped(), 2); // key is dropped on panic + assert_eq!(c.dropped(), 2); // key is dropped on panic +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_merge_conflict_drop_leak() { + let a = CrashTestDummy::new(0); + let a_val_left = CrashTestDummy::new(0); + + let b = CrashTestDummy::new(1); + let b_val_left = CrashTestDummy::new(1); + let b_val_right = CrashTestDummy::new(1); + + let c = CrashTestDummy::new(2); + let c_val_left = CrashTestDummy::new(2); + let c_val_right = CrashTestDummy::new(2); + + let mut left = BTreeMap::new(); + let mut right = BTreeMap::new(); + + left.insert(a.spawn(Panic::Never), a_val_left.spawn(Panic::Never)); + left.insert(b.spawn(Panic::Never), b_val_left.spawn(Panic::Never)); + left.insert(c.spawn(Panic::Never), c_val_left.spawn(Panic::Never)); + right.insert(b.spawn(Panic::Never), b_val_right.spawn(Panic::Never)); + right.insert(c.spawn(Panic::Never), c_val_right.spawn(Panic::Never)); + + // First key that conflicts should + catch_unwind(move || { + left.merge(right, |_, _, _| panic!("Panic in conflict function")); + assert_eq!(left.len(), 1); // only 1 entry should be left + }) + .unwrap_err(); + assert_eq!(a.dropped(), 1); // should not panic + assert_eq!(a_val_left.dropped(), 1); // should not panic + assert_eq!(b.dropped(), 2); // should drop from panic (conflict) + assert_eq!(b_val_left.dropped(), 1); // should be 2 were it not for Rust issue #47949 + assert_eq!(b_val_right.dropped(), 1); // should be 2 were it not for Rust issue #47949 + assert_eq!(c.dropped(), 2); // should drop from panic (conflict) + assert_eq!(c_val_left.dropped(), 1); // should be 2 were it not for Rust issue #47949 + assert_eq!(c_val_right.dropped(), 1); // should be 2 were it not for Rust issue #47949 +} + +#[test] +fn test_merge_ord_chaos() { + let mut map1 = BTreeMap::new(); + map1.insert(Cyclic3::A, ()); + map1.insert(Cyclic3::B, ()); + let mut map2 = BTreeMap::new(); + map2.insert(Cyclic3::A, ()); + map2.insert(Cyclic3::B, ()); + map2.insert(Cyclic3::C, ()); // lands first, before A + map2.insert(Cyclic3::B, ()); // lands first, before C + map1.check(); + map2.check(); // keys are not unique but still strictly ascending + assert_eq!(map1.len(), 2); + assert_eq!(map2.len(), 4); + map1.merge(map2, |_, _, _| ()); + assert_eq!(map1.len(), 5); + map1.check(); +} + fn rand_data(len: usize) -> Vec<(u32, u32)> { let mut rng = DeterministicRng::new(); Vec::from_iter((0..len).map(|_| (rng.next(), rng.next()))) @@ -2615,3 +2773,25 @@ fn test_id_based_append() { assert_eq!(lhs.pop_first().unwrap().0.name, "lhs_k".to_string()); } + +#[test] +fn test_id_based_merge() { + let mut lhs = BTreeMap::new(); + let mut rhs = BTreeMap::new(); + + lhs.insert(IdBased { id: 0, name: "lhs_k".to_string() }, "1".to_string()); + rhs.insert(IdBased { id: 0, name: "rhs_k".to_string() }, "2".to_string()); + + lhs.merge(rhs, |_, mut lhs_val, rhs_val| { + // confirming that lhs_val comes from lhs tree, + // rhs_val comes from rhs tree + assert_eq!(lhs_val, String::from("1")); + assert_eq!(rhs_val, String::from("2")); + lhs_val.push_str(&rhs_val); + lhs_val + }); + + let merged_kv_pair = lhs.pop_first().unwrap(); + assert_eq!(merged_kv_pair.0.id, 0); + assert_eq!(merged_kv_pair.0.name, "lhs_k".to_string()); +} diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index fd27e87b1f47..af6f5c7d7017 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -361,6 +361,7 @@ impl BTreeSet { /// let mut set: BTreeSet = BTreeSet::new_in(Global); /// ``` #[unstable(feature = "btreemap_alloc", issue = "32838")] + #[must_use] pub const fn new_in(alloc: A) -> BTreeSet { BTreeSet { map: BTreeMap::new_in(alloc) } } diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 3889fca30bc8..674828b8e7de 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -862,7 +862,7 @@ pub fn push_front(&mut self, elt: T) { /// *ptr += 4; /// assert_eq!(dl.front().unwrap(), &6); /// ``` - #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "push_mut", since = "1.95.0")] #[must_use = "if you don't need a reference to the value, use `LinkedList::push_front` instead"] pub fn push_front_mut(&mut self, elt: T) -> &mut T { let mut node = @@ -933,7 +933,7 @@ pub fn push_back(&mut self, elt: T) { /// *ptr += 4; /// assert_eq!(dl.back().unwrap(), &6); /// ``` - #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "push_mut", since = "1.95.0")] #[must_use = "if you don't need a reference to the value, use `LinkedList::push_back` instead"] pub fn push_back_mut(&mut self, elt: T) -> &mut T { let mut node = diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index eda29db44257..3185fd56d8c0 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -2175,7 +2175,7 @@ pub fn push_front(&mut self, value: T) { /// *x -= 1; /// assert_eq!(d.front(), Some(&7)); /// ``` - #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "push_mut", since = "1.95.0")] #[must_use = "if you don't need a reference to the value, use `VecDeque::push_front` instead"] pub fn push_front_mut(&mut self, value: T) -> &mut T { if self.is_full() { @@ -2218,7 +2218,7 @@ pub fn push_back(&mut self, value: T) { /// *x += 1; /// assert_eq!(d.back(), Some(&10)); /// ``` - #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "push_mut", since = "1.95.0")] #[must_use = "if you don't need a reference to the value, use `VecDeque::push_back` instead"] pub fn push_back_mut(&mut self, value: T) -> &mut T { if self.is_full() { @@ -2425,7 +2425,7 @@ pub fn insert(&mut self, index: usize, value: T) { /// *x += 7; /// assert_eq!(vec_deque, &[1, 12, 2, 3]); /// ``` - #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "push_mut", since = "1.95.0")] #[must_use = "if you don't need a reference to the value, use `VecDeque::insert` instead"] pub fn insert_mut(&mut self, index: usize, value: T) -> &mut T { assert!(index <= self.len(), "index out of bounds"); diff --git a/library/alloc/src/intrinsics.rs b/library/alloc/src/intrinsics.rs new file mode 100644 index 000000000000..a1e358e077cb --- /dev/null +++ b/library/alloc/src/intrinsics.rs @@ -0,0 +1,15 @@ +//! Intrinsics that cannot be moved to `core` because they depend on `alloc` types. +#![unstable(feature = "liballoc_internals", issue = "none")] + +use core::mem::MaybeUninit; + +use crate::boxed::Box; + +/// Writes `x` into `b`. +/// +/// This is needed for `vec!`, which can't afford any extra copies of the argument (or else debug +/// builds regress), has to be written fully as a call chain without `let` (or else this breaks inference +/// of e.g. unsizing coercions), and can't use an `unsafe` block as that would then also +/// include the user-provided `$x`. +#[rustc_intrinsic] +pub fn write_box_via_move(b: Box>, x: T) -> Box>; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 3d94554281d4..73e93657b02f 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -182,7 +182,6 @@ #![feature(negative_impls)] #![feature(never_type)] #![feature(optimize_attribute)] -#![feature(rustc_allow_const_fn_unstable)] #![feature(rustc_attrs)] #![feature(slice_internals)] #![feature(staged_api)] @@ -224,6 +223,7 @@ #[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))] pub mod ffi; pub mod fmt; +pub mod intrinsics; #[cfg(not(no_rc))] pub mod rc; pub mod slice; diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 1e6e2ae8c367..b99107fb345a 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -47,10 +47,16 @@ macro_rules! vec { $crate::vec::from_elem($elem, $n) ); ($($x:expr),+ $(,)?) => ( - <[_]>::into_vec( - // Using the intrinsic produces a dramatic improvement in stack usage for - // unoptimized programs using this code path to construct large Vecs. - $crate::boxed::box_new([$($x),+]) + // Using `write_box_via_move` produces a dramatic improvement in stack usage for unoptimized + // programs using this code path to construct large Vecs. We can't use `write_via_move` + // because this entire invocation has to remain a call chain without `let` bindings, or else + // inference and temporary lifetimes change and things break (see `vec-macro-rvalue-scope`, + // `vec-macro-coercions`, and `autoderef-vec-box-fn-36786` tests). + // + // `box_assume_init_into_vec_unsafe` isn't actually safe but the way we use it here is. We + // can't use an unsafe block as that would also wrap `$x`. + $crate::boxed::box_assume_init_into_vec_unsafe( + $crate::intrinsics::write_box_via_move($crate::boxed::Box::new_uninit(), [$($x),+]) ) ); } diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index ff996ba93cd7..09150259ce43 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -43,7 +43,7 @@ enum AllocInit { /// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. /// /// # Safety: cap must be <= `isize::MAX`. -unsafe fn new_cap(cap: usize) -> Cap { +const unsafe fn new_cap(cap: usize) -> Cap { if T::IS_ZST { ZERO_CAP } else { unsafe { Cap::new_unchecked(cap) } } } @@ -260,7 +260,7 @@ pub(crate) unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is /// guaranteed. #[inline] - pub(crate) unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { + pub(crate) const unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); @@ -278,7 +278,8 @@ pub(crate) unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) - /// /// See [`RawVec::from_raw_parts_in`]. #[inline] - pub(crate) unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + pub(crate) const unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); @@ -310,7 +311,7 @@ pub(crate) const fn capacity(&self) -> usize { /// Returns a shared reference to the allocator backing this `RawVec`. #[inline] - pub(crate) fn allocator(&self) -> &A { + pub(crate) const fn allocator(&self) -> &A { self.inner.allocator() } @@ -399,6 +400,21 @@ pub(crate) fn shrink_to_fit(&mut self, cap: usize) { // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout unsafe { self.inner.shrink_to_fit(cap, T::LAYOUT) } } + + /// Shrinks the buffer down to the specified capacity. If the given amount + /// is 0, actually completely deallocates. + /// + /// # Errors + /// + /// This function returns an error if the allocator cannot shrink the allocation. + /// + /// # Panics + /// + /// Panics if the given amount is *larger* than the current capacity. + #[inline] + pub(crate) fn try_shrink_to_fit(&mut self, cap: usize) -> Result<(), TryReserveError> { + unsafe { self.inner.try_shrink_to_fit(cap, T::LAYOUT) } + } } unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { @@ -578,12 +594,13 @@ fn with_capacity_zeroed_in(capacity: usize, alloc: A, elem_layout: Layout) -> Se } #[inline] - unsafe fn from_raw_parts_in(ptr: *mut u8, cap: Cap, alloc: A) -> Self { + const unsafe fn from_raw_parts_in(ptr: *mut u8, cap: Cap, alloc: A) -> Self { Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap, alloc } } #[inline] - unsafe fn from_nonnull_in(ptr: NonNull, cap: Cap, alloc: A) -> Self { + #[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 } } @@ -603,7 +620,7 @@ const fn capacity(&self, elem_size: usize) -> usize { } #[inline] - fn allocator(&self) -> &A { + const fn allocator(&self) -> &A { &self.alloc } @@ -731,6 +748,20 @@ unsafe fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) { } } + /// # Safety + /// + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment + /// - `cap` must be less than or equal to `self.capacity(elem_layout.size())` + unsafe fn try_shrink_to_fit( + &mut self, + cap: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + unsafe { self.shrink(cap, elem_layout) } + } + #[inline] const fn needs_to_grow(&self, len: usize, additional: usize, elem_layout: Layout) -> bool { additional > self.capacity(elem_layout.size()).wrapping_sub(len) @@ -778,7 +809,6 @@ unsafe fn grow_exact( /// initially construct `self` /// - `elem_layout`'s size must be a multiple of its alignment /// - `cap` must be less than or equal to `self.capacity(elem_layout.size())` - #[cfg(not(no_global_oom_handling))] #[inline] unsafe fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> { assert!(cap <= self.capacity(elem_layout.size()), "Tried to shrink to a larger capacity"); @@ -796,7 +826,6 @@ unsafe fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryRe /// /// # Safety /// `cap <= self.capacity()` - #[cfg(not(no_global_oom_handling))] unsafe fn shrink_unchecked( &mut self, cap: usize, diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index cc8d80aee456..39e72e383eac 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -477,7 +477,6 @@ fn to_vec(s: &[Self], alloc: A) -> Vec { #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[inline] - #[rustc_diagnostic_item = "slice_into_vec"] pub fn into_vec(self: Box) -> Vec { unsafe { let len = self.len(); diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs index 8705a9c3d267..9a6bfa823f2a 100644 --- a/library/alloc/src/vec/drain.rs +++ b/library/alloc/src/vec/drain.rs @@ -1,8 +1,7 @@ -use core::fmt; use core::iter::{FusedIterator, TrustedLen}; use core::mem::{self, ManuallyDrop, SizedTypeProperties}; use core::ptr::{self, NonNull}; -use core::slice::{self}; +use core::{fmt, slice}; use super::Vec; use crate::alloc::{Allocator, Global}; diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index af1bd5317973..4f67a2c04fef 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -9,8 +9,7 @@ use core::ops::Deref; use core::panic::UnwindSafe; use core::ptr::{self, NonNull}; -use core::slice::{self}; -use core::{array, fmt}; +use core::{array, fmt, slice}; #[cfg(not(no_global_oom_handling))] use super::AsVecIntoIter; diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 11a498d09d3f..7796f3b8b2fb 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -75,8 +75,6 @@ #[cfg(not(no_global_oom_handling))] use core::clone::TrivialClone; -#[cfg(not(no_global_oom_handling))] -use core::cmp; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; #[cfg(not(no_global_oom_handling))] @@ -88,7 +86,7 @@ use core::ops::{self, Index, IndexMut, Range, RangeBounds}; use core::ptr::{self, NonNull}; use core::slice::{self, SliceIndex}; -use core::{fmt, hint, intrinsics, ub_checks}; +use core::{cmp, fmt, hint, intrinsics, ub_checks}; #[stable(feature = "extract_if", since = "1.87.0")] pub use self::extract_if::ExtractIf; @@ -558,7 +556,7 @@ pub fn try_with_capacity(capacity: usize) -> Result { /// (`T` having a less strict alignment is not sufficient, the alignment really /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be /// allocated and deallocated with the same layout.) - /// * The size of `T` times the `capacity` (ie. the allocated size in bytes), if + /// * The size of `T` times the `capacity` (i.e. the allocated size in bytes), if /// nonzero, needs to be the same size as the pointer was allocated with. /// (Because similar to alignment, [`dealloc`] must be called with the same /// layout `size`.) @@ -642,7 +640,8 @@ pub fn try_with_capacity(capacity: usize) -> Result { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Self { + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + pub const unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Self { unsafe { Self::from_raw_parts_in(ptr, length, capacity, Global) } } @@ -660,7 +659,7 @@ pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Sel /// (`T` having a less strict alignment is not sufficient, the alignment really /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be /// allocated and deallocated with the same layout.) - /// * The size of `T` times the `capacity` (ie. the allocated size in bytes) needs + /// * The size of `T` times the `capacity` (i.e. the allocated size in bytes) needs /// to be the same size as the pointer was allocated with. (Because similar to /// alignment, [`dealloc`] must be called with the same layout `size`.) /// * `length` needs to be less than or equal to `capacity`. @@ -744,7 +743,8 @@ pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Sel /// ``` #[inline] #[unstable(feature = "box_vec_non_null", issue = "130364")] - pub unsafe fn from_parts(ptr: NonNull, length: usize, capacity: usize) -> Self { + #[rustc_const_unstable(feature = "box_vec_non_null", issue = "130364")] + pub const unsafe fn from_parts(ptr: NonNull, length: usize, capacity: usize) -> Self { unsafe { Self::from_parts_in(ptr, length, capacity, Global) } } @@ -838,7 +838,8 @@ pub fn from_fn(length: usize, f: F) -> Self /// ``` #[must_use = "losing the pointer will leak memory"] #[stable(feature = "vec_into_raw_parts", since = "1.93.0")] - pub fn into_raw_parts(self) -> (*mut T, usize, usize) { + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + pub const fn into_raw_parts(self) -> (*mut T, usize, usize) { let mut me = ManuallyDrop::new(self); (me.as_mut_ptr(), me.len(), me.capacity()) } @@ -879,7 +880,8 @@ pub fn into_raw_parts(self) -> (*mut T, usize, usize) { /// ``` #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "box_vec_non_null", issue = "130364")] - pub fn into_parts(self) -> (NonNull, usize, usize) { + #[rustc_const_unstable(feature = "box_vec_non_null", issue = "130364")] + pub const fn into_parts(self) -> (NonNull, usize, usize) { let (ptr, len, capacity) = self.into_raw_parts(); // SAFETY: A `Vec` always has a non-null pointer. (unsafe { NonNull::new_unchecked(ptr) }, len, capacity) @@ -1020,7 +1022,7 @@ pub fn push(&mut self, value: T) { /// vector's elements to a larger allocation. This expensive operation is /// offset by the *capacity* *O*(1) insertions it allows. #[inline] - #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "push_mut", since = "1.95.0")] #[must_use = "if you don't need a reference to the value, use `Vec::push` instead"] pub fn push_mut(&mut self, value: T) -> &mut T { // Inform codegen that the length does not change across grow_one(). @@ -1092,7 +1094,7 @@ pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result Result Self { + #[rustc_const_unstable(feature = "allocator_api", issue = "32838")] + pub const unsafe fn from_raw_parts_in( + ptr: *mut T, + length: usize, + capacity: usize, + alloc: A, + ) -> Self { ub_checks::assert_unsafe_precondition!( check_library_ub, "Vec::from_raw_parts_in requires that length <= capacity", @@ -1203,7 +1211,7 @@ pub unsafe fn from_raw_parts_in(ptr: *mut T, length: usize, capacity: usize, all /// (`T` having a less strict alignment is not sufficient, the alignment really /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be /// allocated and deallocated with the same layout.) - /// * The size of `T` times the `capacity` (ie. the allocated size in bytes) needs + /// * The size of `T` times the `capacity` (i.e. the allocated size in bytes) needs /// to be the same size as the pointer was allocated with. (Because similar to /// alignment, [`dealloc`] must be called with the same layout `size`.) /// * `length` needs to be less than or equal to `capacity`. @@ -1289,8 +1297,14 @@ pub unsafe fn from_raw_parts_in(ptr: *mut T, length: usize, capacity: usize, all /// ``` #[inline] #[unstable(feature = "allocator_api", issue = "32838")] + #[rustc_const_unstable(feature = "allocator_api", issue = "32838")] // #[unstable(feature = "box_vec_non_null", issue = "130364")] - pub unsafe fn from_parts_in(ptr: NonNull, length: usize, capacity: usize, alloc: A) -> Self { + pub const unsafe fn from_parts_in( + ptr: NonNull, + length: usize, + capacity: usize, + alloc: A, + ) -> Self { ub_checks::assert_unsafe_precondition!( check_library_ub, "Vec::from_parts_in requires that length <= capacity", @@ -1338,7 +1352,8 @@ pub unsafe fn from_parts_in(ptr: NonNull, length: usize, capacity: usize, all /// ``` #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] - pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) { + #[rustc_const_unstable(feature = "allocator_api", issue = "32838")] + pub const fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) { let mut me = ManuallyDrop::new(self); let len = me.len(); let capacity = me.capacity(); @@ -1387,8 +1402,9 @@ pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) { /// ``` #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] + #[rustc_const_unstable(feature = "allocator_api", issue = "32838")] // #[unstable(feature = "box_vec_non_null", issue = "130364")] - pub fn into_parts_with_alloc(self) -> (NonNull, usize, usize, A) { + pub const fn into_parts_with_alloc(self) -> (NonNull, usize, usize, A) { let (ptr, len, capacity, alloc) = self.into_raw_parts_with_alloc(); // SAFETY: A `Vec` always has a non-null pointer. (unsafe { NonNull::new_unchecked(ptr) }, len, capacity, alloc) @@ -1613,6 +1629,73 @@ pub fn shrink_to(&mut self, min_capacity: usize) { } } + /// Tries to shrink the capacity of the vector as much as possible + /// + /// The behavior of this method depends on the allocator, which may either shrink the vector + /// in-place or reallocate. The resulting vector might still have some excess capacity, just as + /// is the case for [`with_capacity`]. See [`Allocator::shrink`] for more details. + /// + /// [`with_capacity`]: Vec::with_capacity + /// + /// # Errors + /// + /// This function returns an error if the allocator fails to shrink the allocation, + /// the vector thereafter is still safe to use, the capacity remains unchanged + /// however. See [`Allocator::shrink`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_fallible_shrink)] + /// + /// let mut vec = Vec::with_capacity(10); + /// vec.extend([1, 2, 3]); + /// assert!(vec.capacity() >= 10); + /// vec.try_shrink_to_fit().expect("why is the test harness failing to shrink to 12 bytes"); + /// assert!(vec.capacity() >= 3); + /// ``` + #[unstable(feature = "vec_fallible_shrink", issue = "152350")] + #[inline] + pub fn try_shrink_to_fit(&mut self) -> Result<(), TryReserveError> { + if self.capacity() > self.len { self.buf.try_shrink_to_fit(self.len) } else { Ok(()) } + } + + /// Shrinks the capacity of the vector with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// If the current capacity is less than the lower limit, this is a no-op. + /// + /// # Errors + /// + /// This function returns an error if the allocator fails to shrink the allocation, + /// the vector thereafter is still safe to use, the capacity remains unchanged + /// however. See [`Allocator::shrink`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_fallible_shrink)] + /// + /// let mut vec = Vec::with_capacity(10); + /// vec.extend([1, 2, 3]); + /// assert!(vec.capacity() >= 10); + /// vec.try_shrink_to(4).expect("why is the test harness failing to shrink to 12 bytes"); + /// assert!(vec.capacity() >= 4); + /// vec.try_shrink_to(0).expect("this is a no-op and thus the allocator isn't involved."); + /// assert!(vec.capacity() >= 3); + /// ``` + #[unstable(feature = "vec_fallible_shrink", issue = "152350")] + #[inline] + pub fn try_shrink_to(&mut self, min_capacity: usize) -> Result<(), TryReserveError> { + if self.capacity() > min_capacity { + self.buf.try_shrink_to_fit(cmp::max(self.len, min_capacity)) + } else { + Ok(()) + } + } + /// Converts the vector into [`Box<[T]>`][owned slice]. /// /// Before doing the conversion, this method discards excess capacity like [`shrink_to_fit`]. @@ -2000,8 +2083,9 @@ pub const fn as_non_null(&mut self) -> NonNull { /// Returns a reference to the underlying allocator. #[unstable(feature = "allocator_api", issue = "32838")] + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[inline] - pub fn allocator(&self) -> &A { + pub const fn allocator(&self) -> &A { self.buf.allocator() } @@ -2206,7 +2290,7 @@ pub fn insert(&mut self, index: usize, element: T) { /// the insertion index is 0. #[cfg(not(no_global_oom_handling))] #[inline] - #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "push_mut", since = "1.95.0")] #[track_caller] #[must_use = "if you don't need a reference to the value, use `Vec::insert` instead"] pub fn insert_mut(&mut self, index: usize, element: T) -> &mut T { diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs index 7c908841c90e..de6ef3d80326 100644 --- a/library/alloc/src/vec/spec_extend.rs +++ b/library/alloc/src/vec/spec_extend.rs @@ -1,6 +1,6 @@ use core::clone::TrivialClone; use core::iter::TrustedLen; -use core::slice::{self}; +use core::slice; use super::{IntoIter, Vec}; use crate::alloc::Allocator; diff --git a/library/alloc/src/vec/spec_from_iter.rs b/library/alloc/src/vec/spec_from_iter.rs index e1f0b639bdfd..ccbc2936fb4e 100644 --- a/library/alloc/src/vec/spec_from_iter.rs +++ b/library/alloc/src/vec/spec_from_iter.rs @@ -1,5 +1,5 @@ use core::mem::ManuallyDrop; -use core::ptr::{self}; +use core::ptr; use super::{IntoIter, SpecExtend, SpecFromIterNested, Vec}; diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs index 46611f611dc2..3eb8ca44a9d1 100644 --- a/library/alloc/src/vec/splice.rs +++ b/library/alloc/src/vec/splice.rs @@ -1,5 +1,4 @@ -use core::ptr::{self}; -use core::slice::{self}; +use core::{ptr, slice}; use super::{Drain, Vec}; use crate::alloc::{Allocator, Global}; diff --git a/library/alloctests/tests/slice.rs b/library/alloctests/tests/slice.rs index 1e15d54d979a..1194e48f38cf 100644 --- a/library/alloctests/tests/slice.rs +++ b/library/alloctests/tests/slice.rs @@ -909,7 +909,8 @@ fn assert_tight_size_hints(mut it: impl Iterator, which: Bounds, ctx: impl fmt:: // become maximally long, so the size_hint upper bounds are tight ((|_| true) as fn(&_) -> _, Bounds::Upper), ] { - use {assert_tight_size_hints as a, format_args as f}; + use assert_tight_size_hints as a; + use format_args as f; a(v.split(p), b, "split"); a(v.split_mut(p), b, "split_mut"); diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 4bffdd17696f..011a90348226 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -263,8 +263,8 @@ pub const fn for_value(t: &T) -> Self { /// be that of a valid pointer, which means this must not be used /// as a "not yet initialized" sentinel value. /// Types that lazily allocate must track initialization by some other means. - #[stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "alloc_layout_extra", since = "1.95.0")] + #[rustc_const_stable(feature = "alloc_layout_extra", since = "1.95.0")] #[must_use] #[inline] pub const fn dangling_ptr(&self) -> NonNull { @@ -423,8 +423,8 @@ pub const fn pad_to_align(&self) -> Layout { /// let repeated = padding_needed.repeat(0).unwrap(); /// assert_eq!(repeated, (Layout::from_size_align(0, 4).unwrap(), 8)); /// ``` - #[stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "alloc_layout_extra", since = "1.95.0")] + #[rustc_const_stable(feature = "alloc_layout_extra", since = "1.95.0")] #[inline] pub const fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> { // FIXME(const-hack): the following could be way shorter with `?` @@ -520,8 +520,8 @@ pub const fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> { /// aligned. /// /// On arithmetic overflow, returns `LayoutError`. - #[stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "alloc_layout_extra", since = "1.95.0")] + #[rustc_const_stable(feature = "alloc_layout_extra", since = "1.95.0")] #[inline] pub const fn repeat_packed(&self, n: usize) -> Result { if let Some(size) = self.size.checked_mul(n) { @@ -538,8 +538,8 @@ pub const fn repeat_packed(&self, n: usize) -> Result { /// and is not incorporated *at all* into the resulting layout. /// /// On arithmetic overflow, returns `LayoutError`. - #[stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "alloc_layout_extra", since = "1.95.0")] + #[rustc_const_stable(feature = "alloc_layout_extra", since = "1.95.0")] #[inline] pub const fn extend_packed(&self, next: Self) -> Result { // SAFETY: each `size` is at most `isize::MAX == usize::MAX/2`, so the diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 42f332f7d8ba..71a529400511 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -86,7 +86,10 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::{fmt, hash, intrinsics, ptr}; +use crate::intrinsics::{self, type_id_vtable}; +use crate::mem::transmute; +use crate::mem::type_info::{TraitImpl, TypeKind}; +use crate::{fmt, hash, ptr}; /////////////////////////////////////////////////////////////////////////////// // Any trait @@ -788,6 +791,67 @@ pub const fn of() -> TypeId { const { intrinsics::type_id::() } } + /// Checks if the [TypeId] implements the trait. If it does it returns [TraitImpl] which can be used to build a fat pointer. + /// It can only be called at compile time. `self` must be the [TypeId] of a sized type or None will be returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::{TypeId}; + /// + /// pub trait Blah {} + /// impl Blah for u8 {} + /// + /// assert!(const { TypeId::of::().trait_info_of::() }.is_some()); + /// assert!(const { TypeId::of::().trait_info_of::() }.is_none()); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + pub const fn trait_info_of< + T: ptr::Pointee> + ?Sized + 'static, + >( + self, + ) -> Option> { + // SAFETY: The vtable was obtained for `T`, so it is guaranteed to be `DynMetadata`. + // The intrinsic can't infer this because it is designed to work with arbitrary TypeIds. + unsafe { transmute(self.trait_info_of_trait_type_id(const { TypeId::of::() })) } + } + + /// Checks if the [TypeId] implements the trait of `trait_represented_by_type_id`. If it does it returns [TraitImpl] which can be used to build a fat pointer. + /// It can only be called at compile time. `self` must be the [TypeId] of a sized type or None will be returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::{TypeId}; + /// + /// pub trait Blah {} + /// impl Blah for u8 {} + /// + /// assert!(const { TypeId::of::().trait_info_of_trait_type_id(TypeId::of::()) }.is_some()); + /// assert!(const { TypeId::of::().trait_info_of_trait_type_id(TypeId::of::()) }.is_none()); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + pub const fn trait_info_of_trait_type_id( + self, + trait_represented_by_type_id: TypeId, + ) -> Option> { + if self.info().size.is_none() { + return None; + } + + if matches!(trait_represented_by_type_id.info().kind, TypeKind::DynTrait(_)) + && let Some(vtable) = type_id_vtable(self, trait_represented_by_type_id) + { + Some(TraitImpl { vtable }) + } else { + None + } + } + fn as_u128(self) -> u128 { let mut bytes = [0; 16]; @@ -948,7 +1012,8 @@ pub const fn try_as_dyn< >( t: &T, ) -> Option<&U> { - let vtable: Option> = const { intrinsics::vtable_for::() }; + let vtable: Option> = + const { TypeId::of::().trait_info_of::().as_ref().map(TraitImpl::get_vtable) }; match vtable { Some(dyn_metadata) => { let pointer = ptr::from_raw_parts(t, dyn_metadata); @@ -1001,7 +1066,8 @@ pub const fn try_as_dyn_mut< >( t: &mut T, ) -> Option<&mut U> { - let vtable: Option> = const { intrinsics::vtable_for::() }; + let vtable: Option> = + const { TypeId::of::().trait_info_of::().as_ref().map(TraitImpl::get_vtable) }; match vtable { Some(dyn_metadata) => { let pointer = ptr::from_raw_parts_mut(t, dyn_metadata); diff --git a/library/core/src/bstr/mod.rs b/library/core/src/bstr/mod.rs index 34e1ea66c99a..2be7dfc9bfdd 100644 --- a/library/core/src/bstr/mod.rs +++ b/library/core/src/bstr/mod.rs @@ -174,39 +174,38 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[unstable(feature = "bstr", issue = "134915")] impl fmt::Display for ByteStr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fn fmt_nopad(this: &ByteStr, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for chunk in this.utf8_chunks() { - f.write_str(chunk.valid())?; - if !chunk.invalid().is_empty() { - f.write_str("\u{FFFD}")?; - } - } - Ok(()) - } - - let Some(align) = f.align() else { - return fmt_nopad(self, f); - }; let nchars: usize = self .utf8_chunks() .map(|chunk| { chunk.valid().chars().count() + if chunk.invalid().is_empty() { 0 } else { 1 } }) .sum(); + let padding = f.width().unwrap_or(0).saturating_sub(nchars); let fill = f.fill(); - let (lpad, rpad) = match align { - fmt::Alignment::Left => (0, padding), - fmt::Alignment::Right => (padding, 0), - fmt::Alignment::Center => { + + let (lpad, rpad) = match f.align() { + Some(fmt::Alignment::Right) => (padding, 0), + Some(fmt::Alignment::Center) => { let half = padding / 2; (half, half + padding % 2) } + // Either alignment is not specified or it's left aligned + // which behaves the same with padding + _ => (0, padding), }; + for _ in 0..lpad { write!(f, "{fill}")?; } - fmt_nopad(self, f)?; + + for chunk in self.utf8_chunks() { + f.write_str(chunk.valid())?; + if !chunk.invalid().is_empty() { + f.write_str("\u{FFFD}")?; + } + } + for _ in 0..rpad { write!(f, "{fill}")?; } diff --git a/library/core/src/bstr/traits.rs b/library/core/src/bstr/traits.rs index 7da6c5f13cce..bcfffd52d741 100644 --- a/library/core/src/bstr/traits.rs +++ b/library/core/src/bstr/traits.rs @@ -268,4 +268,5 @@ fn index_mut(self, slice: &mut ByteStr) -> &mut Self::Output { impl_slice_index!(ops::RangeInclusive); impl_slice_index!(range::RangeInclusive); impl_slice_index!(ops::RangeToInclusive); +impl_slice_index!(range::RangeToInclusive); impl_slice_index!((ops::Bound, ops::Bound)); diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 28c3db698536..f31cadb546c9 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -689,7 +689,7 @@ impl, U> CoerceUnsized> for Cell {} #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U> DispatchFromDyn> for Cell {} -#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_conversion_trait_impls", since = "1.95.0")] impl AsRef<[Cell; N]> for Cell<[T; N]> { #[inline] fn as_ref(&self) -> &[Cell; N] { @@ -697,7 +697,7 @@ impl, U> DispatchFromDyn> for Cell {} } } -#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_conversion_trait_impls", since = "1.95.0")] impl AsRef<[Cell]> for Cell<[T; N]> { #[inline] fn as_ref(&self) -> &[Cell] { @@ -705,7 +705,7 @@ fn as_ref(&self) -> &[Cell] { } } -#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_conversion_trait_impls", since = "1.95.0")] impl AsRef<[Cell]> for Cell<[T]> { #[inline] fn as_ref(&self) -> &[Cell] { diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 78ea1f111325..b3dc435dda17 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -336,16 +336,24 @@ fn ne(&self, other: &Rhs) -> bool { #[rustc_diagnostic_item = "Eq"] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] pub const trait Eq: [const] PartialEq + PointeeSized { - // this method is used solely by `impl Eq or #[derive(Eq)]` to assert that every component of a - // type implements `Eq` itself. The current deriving infrastructure means doing this assertion - // without using a method on this trait is nearly impossible. + // This method was used solely by `#[derive(Eq)]` to assert that every component of a + // type implements `Eq` itself. // // This should never be implemented by hand. #[doc(hidden)] #[coverage(off)] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_diagnostic_item = "assert_receiver_is_total_eq"] + #[deprecated(since = "1.95.0", note = "implementation detail of `#[derive(Eq)]`")] fn assert_receiver_is_total_eq(&self) {} + + // FIXME (#152504): this method is used solely by `#[derive(Eq)]` to assert that + // every component of a type implements `Eq` itself. It will be removed again soon. + #[doc(hidden)] + #[coverage(off)] + #[unstable(feature = "derive_eq_internals", issue = "none")] + fn assert_fields_are_eq(&self) {} } /// Derive macro generating an impl of the trait [`Eq`]. diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 03650615e25a..40d61de50aaf 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -198,11 +198,11 @@ impl const From for $float { /// # Examples /// ``` $($(#[doc = $doctest_prefix])*)? - #[doc = concat!("let x: ", stringify!($float)," = false.into();")] + #[doc = concat!("let x = ", stringify!($float), "::from(false);")] /// assert_eq!(x, 0.0); /// assert!(x.is_sign_positive()); /// - #[doc = concat!("let y: ", stringify!($float)," = true.into();")] + #[doc = concat!("let y = ", stringify!($float), "::from(true);")] /// assert_eq!(y, 1.0); $($(#[doc = $doctest_suffix])*)? /// ``` @@ -343,11 +343,11 @@ impl const TryFrom<$int> for bool { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!(0_", stringify!($int), ".try_into(), Ok(false));")] + #[doc = concat!("assert_eq!(bool::try_from(0_", stringify!($int), "), Ok(false));")] /// - #[doc = concat!("assert_eq!(1_", stringify!($int), ".try_into(), Ok(true));")] + #[doc = concat!("assert_eq!(bool::try_from(1_", stringify!($int), "), Ok(true));")] /// - #[doc = concat!("assert!(<", stringify!($int), " as TryInto>::try_into(2).is_err());")] + #[doc = concat!("assert!(bool::try_from(2_", stringify!($int), ").is_err());")] /// ``` #[inline] fn try_from(i: $int) -> Result { diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 621277179bb3..5fc97d9a69ef 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -8,7 +8,7 @@ use crate::marker::PhantomData; use crate::ptr::NonNull; use crate::slice::memchr; -use crate::{fmt, ops, slice, str}; +use crate::{fmt, ops, range, slice, str}; // FIXME: because this is doc(inline)d, we *have* to use intra-doc links because the actual link // depends on where the item is being documented. however, since this is libcore, we can't @@ -716,6 +716,16 @@ fn index(&self, index: ops::RangeFrom) -> &CStr { } } +#[unstable(feature = "new_range_api", issue = "125687")] +impl ops::Index> for CStr { + type Output = CStr; + + #[inline] + fn index(&self, index: range::RangeFrom) -> &CStr { + ops::Index::index(self, ops::RangeFrom::from(index)) + } +} + #[stable(feature = "cstring_asref", since = "1.7.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const AsRef for CStr { diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index d0f155316a10..b6dfe3a54184 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -183,7 +183,44 @@ struct VaListInner { } } -/// A variable argument list, equivalent to `va_list` in C. +/// A variable argument list, ABI-compatible with `va_list` in C. +/// +/// This type is created in c-variadic functions when `...` is desugared. A `VaList` +/// is automatically initialized (equivalent to calling `va_start` in C). +/// +/// ``` +/// #![feature(c_variadic)] +/// +/// use std::ffi::VaList; +/// +/// /// # Safety +/// /// Must be passed at least `count` arguments of type `i32`. +/// unsafe extern "C" fn my_func(count: u32, ap: ...) -> i32 { +/// unsafe { vmy_func(count, ap) } +/// } +/// +/// /// # Safety +/// /// Must be passed at least `count` arguments of type `i32`. +/// unsafe fn vmy_func(count: u32, mut ap: VaList<'_>) -> i32 { +/// let mut sum = 0; +/// for _ in 0..count { +/// sum += unsafe { ap.arg::() }; +/// } +/// sum +/// } +/// +/// assert_eq!(unsafe { my_func(1, 42i32) }, 42); +/// assert_eq!(unsafe { my_func(3, 42i32, -7i32, 20i32) }, 55); +/// ``` +/// +/// The [`VaList::arg`] method can be used to read an argument from the list. This method +/// automatically advances the `VaList` to the next argument. The C equivalent is `va_arg`. +/// +/// Cloning a `VaList` performs the equivalent of C `va_copy`, producing an independent cursor +/// that arguments can be read from without affecting the original. Dropping a `VaList` performs +/// the equivalent of C `va_end`. +/// +/// This can be used across an FFI boundary, and fully matches the platform's `va_list`. #[repr(transparent)] #[lang = "va_list"] pub struct VaList<'a> { @@ -200,12 +237,13 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { impl VaList<'_> { // Helper used in the implementation of the `va_copy` intrinsic. - pub(crate) fn duplicate(&self) -> Self { - Self { inner: self.inner.clone(), _marker: self._marker } + pub(crate) const fn duplicate(&self) -> Self { + Self { inner: self.inner, _marker: self._marker } } } -impl Clone for VaList<'_> { +#[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")] +impl<'f> const Clone for VaList<'f> { #[inline] fn clone(&self) -> Self { // We only implement Clone and not Copy because some future target might not be able to @@ -216,7 +254,8 @@ fn clone(&self) -> Self { } } -impl<'f> Drop for VaList<'f> { +#[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")] +impl<'f> const Drop for VaList<'f> { fn drop(&mut self) { // SAFETY: this variable argument list is being dropped, so won't be read from again. unsafe { va_end(self) } @@ -276,22 +315,20 @@ unsafe impl VaArgSafe for *mut T {} unsafe impl VaArgSafe for *const T {} impl<'f> VaList<'f> { - /// Advance to and read the next variable argument. + /// Read an argument from the variable argument list, and advance to the next argument. + /// + /// Only types that implement [`VaArgSafe`] can be read from a variable argument list. /// /// # Safety /// - /// This function is only sound to call when: - /// - /// - there is a next variable argument available. - /// - the next argument's type must be ABI-compatible with the type `T`. - /// - the next argument must have a properly initialized value of type `T`. + /// This function is only sound to call when there is another argument to read, and that + /// argument is a properly initialized value of the type `T`. /// /// Calling this function with an incompatible type, an invalid value, or when there /// are no more variable arguments, is unsound. - /// - /// [valid]: https://doc.rust-lang.org/nightly/nomicon/what-unsafe-does.html #[inline] - pub unsafe fn arg(&mut self) -> T { + #[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")] + pub const unsafe fn arg(&mut self) -> T { // SAFETY: the caller must uphold the safety contract for `va_arg`. unsafe { va_arg(self) } } diff --git a/library/core/src/field.rs b/library/core/src/field.rs new file mode 100644 index 000000000000..e8ef309b9c84 --- /dev/null +++ b/library/core/src/field.rs @@ -0,0 +1,83 @@ +//! Field Reflection + +use crate::marker::PhantomData; + +/// Field Representing Type +#[unstable(feature = "field_representing_type_raw", issue = "none")] +#[lang = "field_representing_type"] +#[expect(missing_debug_implementations)] +#[fundamental] +pub struct FieldRepresentingType { + _phantom: PhantomData, +} + +// SAFETY: `FieldRepresentingType` doesn't contain any `T` +unsafe impl Send + for FieldRepresentingType +{ +} + +// SAFETY: `FieldRepresentingType` doesn't contain any `T` +unsafe impl Sync + for FieldRepresentingType +{ +} + +impl Copy + for FieldRepresentingType +{ +} + +impl Clone + for FieldRepresentingType +{ + fn clone(&self) -> Self { + *self + } +} + +/// Expands to the field representing type of the given field. +/// +/// The container type may be a tuple, `struct`, `union` or `enum`. In the case of an enum, the +/// variant must also be specified. Only a single field is supported. +#[unstable(feature = "field_projections", issue = "145383")] +#[allow_internal_unstable(field_representing_type_raw, builtin_syntax)] +// NOTE: when stabilizing this macro, we can never add new trait impls for `FieldRepresentingType`, +// since it is `#[fundamental]` and thus could break users of this macro, since the compiler expands +// it to `FieldRepresentingType<...>`. Thus stabilizing this requires careful thought about the +// completeness of the trait impls for `FieldRepresentingType`. +pub macro field_of($Container:ty, $($fields:expr)+ $(,)?) { + builtin # field_of($Container, $($fields)+) +} + +/// Type representing a field of a `struct`, `union`, `enum` variant or tuple. +/// +/// # Safety +/// +/// Given a valid value of type `Self::Base`, there exists a valid value of type `Self::Type` at +/// byte offset `OFFSET` +#[lang = "field"] +#[unstable(feature = "field_projections", issue = "145383")] +#[rustc_deny_explicit_impl] +#[rustc_dyn_incompatible_trait] +// NOTE: the compiler provides the impl of `Field` for `FieldRepresentingType` when it can guarantee +// the safety requirements of this trait. It also has to manually add the correct trait bounds on +// associated types (and the `Self` type). Thus any changes to the bounds here must be reflected in +// the old and new trait solver: +// - `fn assemble_candidates_for_field_trait` in +// `compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs`, and +// - `fn consider_builtin_field_candidate` in +// `compiler/rustc_next_trait_solver/src/solve/trait_goals.rs`. +pub unsafe trait Field: Send + Sync + Copy { + /// The type of the base where this field exists in. + #[lang = "field_base"] + type Base; + + /// The type of the field. + #[lang = "field_type"] + type Type; + + /// The offset of the field in bytes. + #[lang = "field_offset"] + const OFFSET: usize = crate::intrinsics::field_offset::(); +} diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 7550dac45cd0..19dd13967fdd 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1227,7 +1227,7 @@ fn is_pretty(&self) -> bool { /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` #[stable(feature = "fmt_from_fn", since = "1.93.0")] -#[rustc_const_stable(feature = "const_fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_fmt_from_fn", since = "1.95.0")] #[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"] pub const fn from_fn) -> fmt::Result>(f: F) -> FromFn { FromFn(f) diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 6a458436726c..87ce27b0d86d 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -1,6 +1,6 @@ use crate::fmt::{Debug, Display, Formatter, LowerExp, Result, UpperExp}; use crate::mem::MaybeUninit; -use crate::num::{flt2dec, fmt as numfmt}; +use crate::num::imp::{flt2dec, fmt as numfmt}; #[doc(hidden)] trait GeneralFormat: PartialOrd { diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index e20109c3cc9a..05d8e0d84f05 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -6,7 +6,7 @@ use crate::char::EscapeDebugExtArgs; use crate::hint::assert_unchecked; use crate::marker::{PhantomData, PointeeSized}; -use crate::num::fmt as numfmt; +use crate::num::imp::fmt as numfmt; use crate::ops::Deref; use crate::ptr::NonNull; use crate::{iter, mem, result, str}; diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 253a7b7587e4..d3d6495e75a2 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -2,7 +2,7 @@ use crate::fmt::NumBuffer; use crate::mem::MaybeUninit; -use crate::num::fmt as numfmt; +use crate::num::imp::fmt as numfmt; use crate::{fmt, str}; /// Formatting of integers with a non-decimal radix. diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 3692420be8fc..03ed04dc5a66 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -775,8 +775,8 @@ pub const fn unlikely(b: bool) -> bool { /// } /// } /// ``` -#[stable(feature = "cold_path", since = "CURRENT_RUSTC_VERSION")] -#[rustc_const_stable(feature = "cold_path", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "cold_path", since = "1.95.0")] +#[rustc_const_stable(feature = "cold_path", since = "1.95.0")] #[inline(always)] pub const fn cold_path() { crate::intrinsics::cold_path() diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 7fded188ddbf..7e9adc1e8d57 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2751,18 +2751,6 @@ pub const fn contract_check_ensures bool + Copy, Ret>( #[rustc_intrinsic] pub unsafe fn vtable_align(ptr: *const ()) -> usize; -/// The intrinsic returns the `U` vtable for `T` if `T` can be coerced to the trait object type `U`. -/// -/// # Compile-time failures -/// Determining whether `T` can be coerced to the trait object type `U` requires trait resolution by the compiler. -/// In some cases, that resolution can exceed the recursion limit, -/// and compilation will fail instead of this function returning `None`. -#[rustc_nounwind] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_intrinsic] -pub const fn vtable_for> + ?Sized>() --> Option>; - /// The size of a type in bytes. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -2824,6 +2812,20 @@ pub const fn vtable_for> + ?Si #[lang = "offset_of"] pub const fn offset_of(variant: u32, field: u32) -> usize; +/// The offset of a field queried by its field representing type. +/// +/// Returns the offset of the field represented by `F`. This function essentially does the same as +/// the [`offset_of`] intrinsic, but expects the field to be represented by a generic rather than +/// the variant and field indices. This also is a safe intrinsic and can only be evaluated at +/// compile-time, so it should only appear in constants or inline const blocks. +/// +/// There should be no need to call this intrinsic manually, as its value is used to define +/// [`Field::OFFSET`](crate::field::Field::OFFSET), which is publicly accessible. +#[rustc_intrinsic] +#[unstable(feature = "field_projections", issue = "145383")] +#[rustc_const_unstable(feature = "field_projections", issue = "145383")] +pub const fn field_offset() -> usize; + /// Returns the number of variants of the type `T` cast to a `usize`; /// if `T` has no variants, returns `0`. Uninhabited variants will be counted. /// @@ -2864,6 +2866,20 @@ pub const fn vtable_for> + ?Si #[rustc_intrinsic_const_stable_indirect] pub const unsafe fn align_of_val(ptr: *const T) -> usize; +#[rustc_intrinsic] +#[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 const fn type_id_vtable( + _id: crate::any::TypeId, + _trait: crate::any::TypeId, +) -> Option> { + panic!( + "`TypeId::trait_info_of` and `trait_info_of_trait_type_id` can only be called at compile-time" + ) +} + /// Compute the type information of a concrete type. /// It can only be called at compile time, the backends do /// not implement it. @@ -3485,7 +3501,7 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize /// #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn va_arg(ap: &mut VaList<'_>) -> T; +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; /// Duplicates a variable argument list. The returned list is initially at the same position as /// the one in `src`, but can be advanced independently. @@ -3497,7 +3513,7 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize /// when a variable argument list is used incorrectly. #[rustc_intrinsic] #[rustc_nounwind] -pub fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> { +pub const fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> { src.duplicate() } @@ -3516,6 +3532,6 @@ pub fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> { /// #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn va_end(ap: &mut VaList<'_>) { +pub const unsafe fn va_end(ap: &mut VaList<'_>) { /* deliberately does nothing */ } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index d919230d094d..2e82f4577182 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -631,8 +631,15 @@ fn zip(self, other: U) -> Zip Zip::new(self, other.into_iter()) } - /// Creates a new iterator which places a copy of `separator` between adjacent - /// items of the original iterator. + /// Creates a new iterator which places a copy of `separator` between items + /// of the original iterator. + /// + /// Specifically on fused iterators, it is guaranteed that the new iterator + /// places a copy of `separator` between adjacent `Some(_)` items. However, + /// for non-fused iterators, [`intersperse`] will create a new iterator that + /// is a fused version of the original iterator and place a copy of `separator` + /// between adjacent `Some(_)` items. This behavior for non-fused iterators + /// is subject to change. /// /// In case `separator` does not implement [`Clone`] or needs to be /// computed every time, use [`intersperse_with`]. @@ -663,6 +670,7 @@ fn zip(self, other: U) -> Zip /// ``` /// /// [`Clone`]: crate::clone::Clone + /// [`intersperse`]: Iterator::intersperse /// [`intersperse_with`]: Iterator::intersperse_with #[inline] #[unstable(feature = "iter_intersperse", issue = "79524")] @@ -676,12 +684,19 @@ fn intersperse(self, separator: Self::Item) -> Intersperse } /// Creates a new iterator which places an item generated by `separator` - /// between adjacent items of the original iterator. + /// between items of the original iterator. /// - /// The closure will be called exactly once each time an item is placed - /// between two adjacent items from the underlying iterator; specifically, - /// the closure is not called if the underlying iterator yields less than - /// two items and after the last item is yielded. + /// Specifically on fused iterators, it is guaranteed that the new iterator + /// places an item generated by `separator` between adjacent `Some(_)` items. + /// However, for non-fused iterators, [`intersperse_with`] will create a new + /// iterator that is a fused version of the original iterator and place an item + /// generated by `separator` between adjacent `Some(_)` items. This + /// behavior for non-fused iterators is subject to change. + /// + /// The `separator` closure will be called exactly once each time an item + /// is placed between two adjacent items from the underlying iterator; + /// specifically, the closure is not called if the underlying iterator yields + /// less than two items and after the last item is yielded. /// /// If the iterator's item implements [`Clone`], it may be easier to use /// [`intersperse`]. @@ -723,6 +738,7 @@ fn intersperse(self, separator: Self::Item) -> Intersperse /// ``` /// [`Clone`]: crate::clone::Clone /// [`intersperse`]: Iterator::intersperse + /// [`intersperse_with`]: Iterator::intersperse_with #[inline] #[unstable(feature = "iter_intersperse", issue = "79524")] #[rustc_non_const_trait_method] @@ -1908,7 +1924,7 @@ fn inspect(self, f: F) -> Inspect /// without giving up ownership of the original iterator, /// so you can use the original iterator afterwards. /// - /// Uses [`impl Iterator for &mut I { type Item = I::Item; ...}`](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#impl-Iterator-for-%26mut+I). + /// Uses [`impl Iterator for &mut I { type Item = I::Item; ...}`](Iterator#impl-Iterator-for-%26mut+I). /// /// # Examples /// diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index d650239a44c6..29869dd91982 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -98,7 +98,6 @@ // tidy-alphabetical-start #![feature(asm_experimental_arch)] #![feature(bstr_internals)] -#![feature(cfg_select)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(const_carrying_mul_add)] #![feature(const_cmp)] @@ -138,10 +137,10 @@ #![feature(extern_types)] #![feature(f16)] #![feature(f128)] +#![feature(field_projections)] #![feature(freeze_impls)] #![feature(fundamental)] #![feature(funnel_shifts)] -#![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(intrinsics)] #![feature(lang_items)] @@ -159,7 +158,6 @@ #![feature(pattern_types)] #![feature(prelude_import)] #![feature(repr_simd)] -#![feature(rustc_allow_const_fn_unstable)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] #![feature(simd_ffi)] @@ -208,7 +206,7 @@ #[macro_use] mod macros; -#[stable(feature = "assert_matches", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "assert_matches", since = "1.95.0")] pub use crate::macros::{assert_matches, debug_assert_matches}; #[unstable(feature = "derive_from", issue = "144889")] @@ -229,7 +227,7 @@ pub mod autodiff { #[unstable(feature = "contracts", issue = "128044")] pub mod contracts; -#[unstable(feature = "cfg_select", issue = "115585")] +#[stable(feature = "cfg_select", since = "1.95.0")] pub use crate::macros::cfg_select; #[macro_use] @@ -277,6 +275,8 @@ pub mod autodiff { pub mod convert; pub mod default; pub mod error; +#[unstable(feature = "field_projections", issue = "145383")] +pub mod field; pub mod index; pub mod marker; pub mod ops; @@ -307,7 +307,7 @@ pub mod autodiff { pub mod pin; #[unstable(feature = "random", issue = "130703")] pub mod random; -#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "new_range_inclusive_api", since = "1.95.0")] pub mod range; pub mod result; pub mod sync; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index d900b4a21b36..49e1acd0b90f 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -164,7 +164,7 @@ macro_rules! assert_ne { /// assert_matches!(a, Some(x) if x > 100); /// // assert_matches!(a, Some(x) if x < 100); // panics /// ``` -#[stable(feature = "assert_matches", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "assert_matches", since = "1.95.0")] #[allow_internal_unstable(panic_internals)] #[rustc_macro_transparency = "semiopaque"] pub macro assert_matches { @@ -206,8 +206,6 @@ macro_rules! assert_ne { /// # Example /// /// ``` -/// #![feature(cfg_select)] -/// /// cfg_select! { /// unix => { /// fn foo() { /* unix specific functionality */ } @@ -225,14 +223,12 @@ macro_rules! assert_ne { /// right-hand side: /// /// ``` -/// #![feature(cfg_select)] -/// /// let _some_string = cfg_select! { /// unix => "With great power comes great electricity bills", /// _ => { "Behind every successful diet is an unwatched pizza" } /// }; /// ``` -#[unstable(feature = "cfg_select", issue = "115585")] +#[stable(feature = "cfg_select", since = "1.95.0")] #[rustc_diagnostic_item = "cfg_select"] #[rustc_builtin_macro] pub macro cfg_select($($tt:tt)*) { @@ -395,7 +391,7 @@ macro_rules! debug_assert_ne { /// debug_assert_matches!(a, Some(x) if x > 100); /// // debug_assert_matches!(a, Some(x) if x < 100); // panics /// ``` -#[stable(feature = "assert_matches", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "assert_matches", since = "1.95.0")] #[allow_internal_unstable(assert_matches)] #[rustc_macro_transparency = "semiopaque"] pub macro debug_assert_matches($($arg:tt)*) { @@ -445,7 +441,7 @@ macro_rules! matches { /// [raw-identifier syntax][ris]: `r#try`. /// /// [propagating-errors]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator -/// [ris]: https://doc.rust-lang.org/nightly/rust-by-example/compatibility/raw_identifiers.html +/// [ris]: ../rust-by-example/compatibility/raw_identifiers.html /// /// `try!` matches the given [`Result`]. In case of the `Ok` variant, the /// expression has the value of the wrapped value. diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 7187c71799b9..818dad6bce2f 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -46,8 +46,8 @@ /// marker_impls! { /// MarkerTrait for /// u8, i8, -/// {T: ?Sized} *const T, -/// {T: ?Sized} *mut T, +/// {T: PointeeSized} *const T, +/// {T: PointeeSized} *mut T, /// {T: MarkerTrait} PhantomData, /// u32, /// } @@ -931,15 +931,15 @@ impl !Freeze for UnsafeCell {} pub unsafe auto trait UnsafeUnpin {} #[unstable(feature = "unsafe_unpin", issue = "125735")] -impl !UnsafeUnpin for UnsafePinned {} +impl !UnsafeUnpin for UnsafePinned {} marker_impls! { #[unstable(feature = "unsafe_unpin", issue = "125735")] unsafe UnsafeUnpin for - {T: ?Sized} PhantomData, - {T: ?Sized} *const T, - {T: ?Sized} *mut T, - {T: ?Sized} &T, - {T: ?Sized} &mut T, + {T: PointeeSized} PhantomData, + {T: PointeeSized} *const T, + {T: PointeeSized} *mut T, + {T: PointeeSized} &T, + {T: PointeeSized} &mut T, } /// Types that do not require any pinning guarantees. diff --git a/library/core/src/mem/maybe_dangling.rs b/library/core/src/mem/maybe_dangling.rs index a5f77e667f97..c85576b5778c 100644 --- a/library/core/src/mem/maybe_dangling.rs +++ b/library/core/src/mem/maybe_dangling.rs @@ -4,10 +4,6 @@ /// Allows wrapped [references] and [boxes] to dangle. /// -///