diff --git a/.gitmodules b/.gitmodules index b5250d493864e..926807336d791 100644 --- a/.gitmodules +++ b/.gitmodules @@ -47,3 +47,7 @@ path = src/tools/rustc-perf url = https://github.com/rust-lang/rustc-perf.git shallow = true +[submodule "src/tools/enzyme"] + path = src/tools/enzyme + url = https://github.com/EnzymeAD/Enzyme.git + shallow = true diff --git a/.mailmap b/.mailmap index 4d93792422c23..9ac7f1a9b494c 100644 --- a/.mailmap +++ b/.mailmap @@ -81,6 +81,7 @@ boolean_coercion Boris Egorov bors bors[bot] <26634292+bors[bot]@users.noreply.github.com> bors bors[bot] +Boxy Braden Nelson Brandon Sanderson Brandon Sanderson Brett Cannon Brett Cannon @@ -146,6 +147,7 @@ David Klein David Manescu David Ross David Wood +David Wood Deadbeef Deadbeef dependabot[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> diff --git a/Cargo.lock b/Cargo.lock index 6ea4cd8f5aca4..81aecf1cea77f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -199,7 +199,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01667f6f40216b9a0b2945e05fed5f1ad0ab6470e69cb9378001e37b1c0668e4" dependencies = [ - "object 0.36.3", + "object 0.36.4", ] [[package]] @@ -2453,9 +2453,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.3" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "crc32fast", "flate2", @@ -3129,7 +3129,7 @@ dependencies = [ "build_helper", "gimli 0.31.0", "libc", - "object 0.36.3", + "object 0.36.4", "regex", "serde_json", "similar", @@ -3408,7 +3408,7 @@ dependencies = [ "itertools", "libc", "measureme", - "object 0.36.3", + "object 0.36.4", "rustc-demangle", "rustc_ast", "rustc_attr", @@ -3447,7 +3447,7 @@ dependencies = [ "itertools", "jobserver", "libc", - "object 0.36.3", + "object 0.36.4", "pathdiff", "regex", "rustc_arena", @@ -3569,6 +3569,7 @@ dependencies = [ "rustc_hir_pretty", "rustc_hir_typeck", "rustc_incremental", + "rustc_index", "rustc_infer", "rustc_interface", "rustc_lint", @@ -4166,6 +4167,7 @@ dependencies = [ "rustc_errors", "rustc_feature", "rustc_fluent_macro", + "rustc_index", "rustc_lexer", "rustc_macros", "rustc_session", @@ -4431,7 +4433,7 @@ name = "rustc_target" version = "0.0.0" dependencies = [ "bitflags 2.6.0", - "object 0.36.3", + "object 0.36.4", "rustc_abi", "rustc_data_structures", "rustc_feature", @@ -4978,15 +4980,15 @@ dependencies = [ [[package]] name = "stacker" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" dependencies = [ "cc", "cfg-if", "libc", "psm", - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -5779,9 +5781,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi-preview1-component-adapter-provider" -version = "23.0.2" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91d3d13afef569b9fc80cfbb807c87c16ef49bd3ac1a93285ea6a264b600d2d" +checksum = "36e6cadfa74538edd5409b6f8c79628436529138e9618b7373bec7aae7805835" [[package]] name = "wasm-bindgen" @@ -5840,9 +5842,9 @@ checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-component-ld" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51449c63d1ce69f92b8465a084ed5b91f1a7eb583fa95796650a6bfcffc4f9cb" +checksum = "13261270d3ac58ffae0219ae34f297a7e24f9ee3b13b29be579132c588a83519" dependencies = [ "anyhow", "clap", @@ -5864,9 +5866,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.215.0" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb56df3e06b8e6b77e37d2969a50ba51281029a9aeb3855e76b7f49b6418847" +checksum = "04c23aebea22c8a75833ae08ed31ccc020835b12a41999e58c31464271b94a88" dependencies = [ "leb128", "wasmparser", @@ -5874,9 +5876,9 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.215.0" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6bb07c5576b608f7a2a9baa2294c1a3584a249965d695a9814a496cb6d232f" +checksum = "47c8154d703a6b0e45acf6bd172fa002fc3c7058a9f7615e517220aeca27c638" dependencies = [ "anyhow", "indexmap", @@ -5890,9 +5892,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.215.0" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fbde0881f24199b81cf49b6ff8f9c145ac8eb1b7fc439adb5c099734f7d90e" +checksum = "bcdee6bea3619d311fb4b299721e89a986c3470f804b6d534340e412589028e3" dependencies = [ "ahash", "bitflags 2.6.0", @@ -5904,9 +5906,9 @@ dependencies = [ [[package]] name = "wast" -version = "215.0.0" +version = "216.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff1d00d893593249e60720be04a7c1f42f1c4dc3806a2869f4e66ab61eb54cb" +checksum = "f7eb1f2eecd913fdde0dc6c3439d0f24530a98ac6db6cb3d14d92a5328554a08" dependencies = [ "bumpalo", "leb128", @@ -5917,9 +5919,9 @@ dependencies = [ [[package]] name = "wat" -version = "1.215.0" +version = "1.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670bf4d9c8cf76ae242d70ded47c546525b6dafaa6871f9bcb065344bf2b4e3d" +checksum = "ac0409090fb5154f95fb5ba3235675fd9e579e731524d63b6a2f653e1280c82a" dependencies = [ "wast", ] @@ -6206,9 +6208,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.215.0" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f725e3885fc5890648be5c5cbc1353b755dc932aa5f1aa7de968b912a3280743" +checksum = "7e2ca3ece38ea2447a9069b43074ba73d96dde1944cba276c54e41371745f9dc" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -6225,9 +6227,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.215.0" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "935a97eaffd57c3b413aa510f8f0b550a4a9fe7d59e79cd8b89a83dcb860321f" +checksum = "a4d108165c1167a4ccc8a803dcf5c28e0a51d6739fd228cc7adce768632c764c" dependencies = [ "anyhow", "id-arena", diff --git a/RELEASES.md b/RELEASES.md index 5e4827be4ecfd..b49470c307569 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,127 @@ +Version 1.81.0 (2024-09-05) +========================== + + + +Language +-------- + +- [Abort on uncaught panics in `extern "C"` functions.](https://github.com/rust-lang/rust/pull/116088/) +- [Fix ambiguous cases of multiple `&` in elided self lifetimes.](https://github.com/rust-lang/rust/pull/117967/) +- [Stabilize `#[expect]` for lints (RFC 2383),](https://github.com/rust-lang/rust/pull/120924/) like `#[allow]` with a warning if the lint is _not_ fulfilled. +- [Change method resolution to constrain hidden types instead of rejecting method candidates.](https://github.com/rust-lang/rust/pull/123962/) +- [Bump `elided_lifetimes_in_associated_constant` to deny.](https://github.com/rust-lang/rust/pull/124211/) +- [`offset_from`: always allow pointers to point to the same address.](https://github.com/rust-lang/rust/pull/124921/) +- [Allow constraining opaque types during subtyping in the trait system.](https://github.com/rust-lang/rust/pull/125447/) +- [Allow constraining opaque types during various unsizing casts.](https://github.com/rust-lang/rust/pull/125610/) +- [Deny keyword lifetimes pre-expansion.](https://github.com/rust-lang/rust/pull/126762/) + + + +Compiler +-------- + +- [Make casts of pointers to trait objects stricter.](https://github.com/rust-lang/rust/pull/120248/) +- [Check alias args for well-formedness even if they have escaping bound vars.](https://github.com/rust-lang/rust/pull/123737/) +- [Deprecate no-op codegen option `-Cinline-threshold=...`.](https://github.com/rust-lang/rust/pull/124712/) +- [Re-implement a type-size based limit.](https://github.com/rust-lang/rust/pull/125507/) +- [Properly account for alignment in `transmute` size checks.](https://github.com/rust-lang/rust/pull/125740/) +- [Remove the `box_pointers` lint.](https://github.com/rust-lang/rust/pull/126018/) +- [Ensure the interpreter checks bool/char for validity when they are used in a cast.](https://github.com/rust-lang/rust/pull/126265/) +- [Improve coverage instrumentation for functions containing nested items.](https://github.com/rust-lang/rust/pull/127199/) +- Target changes: + - [Add Tier 3 `no_std` Xtensa targets:](https://github.com/rust-lang/rust/pull/125141/) `xtensa-esp32-none-elf`, `xtensa-esp32s2-none-elf`, `xtensa-esp32s3-none-elf` + - [Add Tier 3 `std` Xtensa targets:](https://github.com/rust-lang/rust/pull/126380/) `xtensa-esp32-espidf`, `xtensa-esp32s2-espidf`, `xtensa-esp32s3-espidf` + - [Add Tier 3 i686 Redox OS target:](https://github.com/rust-lang/rust/pull/126192/) `i686-unknown-redox` + - [Promote `arm64ec-pc-windows-msvc` to Tier 2.](https://github.com/rust-lang/rust/pull/126039/) + - [Promote `loongarch64-unknown-linux-musl` to Tier 2 with host tools.](https://github.com/rust-lang/rust/pull/126298/) + - [Enable full tools and profiler for LoongArch Linux targets.](https://github.com/rust-lang/rust/pull/127078/) + - [Unconditionally warn on usage of `wasm32-wasi`.](https://github.com/rust-lang/rust/pull/126662/) (see compatibility note below) + - Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. + + + +Libraries +--------- + +- [Split core's `PanicInfo` and std's `PanicInfo`.](https://github.com/rust-lang/rust/pull/115974/) (see compatibility note below) +- [Generalize `{Rc,Arc}::make_mut()` to unsized types.](https://github.com/rust-lang/rust/pull/116113/) +- [Replace sort implementations with stable `driftsort` and unstable `ipnsort`.](https://github.com/rust-lang/rust/pull/124032/) All `slice::sort*` and `slice::select_nth*` methods are expected to see significant performance improvements. See the [research project](https://github.com/Voultapher/sort-research-rs) for more details. +- [Document behavior of `create_dir_all` with respect to empty paths.](https://github.com/rust-lang/rust/pull/125112/) +- [Fix interleaved output in the default panic hook when multiple threads panic simultaneously.](https://github.com/rust-lang/rust/pull/127397/) + + + +Stabilized APIs +--------------- + +- [`core::error`](https://doc.rust-lang.org/stable/core/error/index.html) +- [`hint::assert_unchecked`](https://doc.rust-lang.org/stable/core/hint/fn.assert_unchecked.html) +- [`fs::exists`](https://doc.rust-lang.org/stable/std/fs/fn.exists.html) +- [`AtomicBool::fetch_not`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicBool.html#method.fetch_not) +- [`Duration::abs_diff`](https://doc.rust-lang.org/stable/core/time/struct.Duration.html#method.abs_diff) +- [`IoSlice::advance`](https://doc.rust-lang.org/stable/std/io/struct.IoSlice.html#method.advance) +- [`IoSlice::advance_slices`](https://doc.rust-lang.org/stable/std/io/struct.IoSlice.html#method.advance_slices) +- [`IoSliceMut::advance`](https://doc.rust-lang.org/stable/std/io/struct.IoSliceMut.html#method.advance) +- [`IoSliceMut::advance_slices`](https://doc.rust-lang.org/stable/std/io/struct.IoSliceMut.html#method.advance_slices) +- [`PanicHookInfo`](https://doc.rust-lang.org/stable/std/panic/struct.PanicHookInfo.html) +- [`PanicInfo::message`](https://doc.rust-lang.org/stable/core/panic/struct.PanicInfo.html#method.message) +- [`PanicMessage`](https://doc.rust-lang.org/stable/core/panic/struct.PanicMessage.html) + +These APIs are now stable in const contexts: + +- [`char::from_u32_unchecked`](https://doc.rust-lang.org/stable/core/char/fn.from_u32_unchecked.html) (function) +- [`char::from_u32_unchecked`](https://doc.rust-lang.org/stable/core/primitive.char.html#method.from_u32_unchecked) (method) +- [`CStr::count_bytes`](https://doc.rust-lang.org/stable/core/ffi/c_str/struct.CStr.html#method.count_bytes) +- [`CStr::from_ptr`](https://doc.rust-lang.org/stable/core/ffi/c_str/struct.CStr.html#method.from_ptr) + + + +Cargo +----- + +- [Generated `.cargo_vcs_info.json` is always included, even when `--allow-dirty` is passed.](https://github.com/rust-lang/cargo/pull/13960/) +- [Disallow `package.license-file` and `package.readme` pointing to non-existent files during packaging.](https://github.com/rust-lang/cargo/pull/13921/) +- [Disallow passing `--release`/`--debug` flag along with the `--profile` flag.](https://github.com/rust-lang/cargo/pull/13971/) +- [Remove `lib.plugin` key support in `Cargo.toml`. Rust plugin support has been deprecated for four years and was removed in 1.75.0.](https://github.com/rust-lang/cargo/pull/13902/) + + + +Compatibility Notes +------------------- + +* Usage of the `wasm32-wasi` target will now issue a compiler warning and request users switch to the `wasm32-wasip1` target instead. Both targets are the same, `wasm32-wasi` is only being renamed, and this [change to the WASI target](https://blog.rust-lang.org/2024/04/09/updates-to-rusts-wasi-targets.html) is being done to enable removing `wasm32-wasi` in January 2025. + +* We have renamed `std::panic::PanicInfo` to `std::panic::PanicHookInfo`. The old name will continue to work as an alias, but will result in a deprecation warning starting in Rust 1.82.0. + + `core::panic::PanicInfo` will remain unchanged, however, as this is now a *different type*. + + The reason is that these types have different roles: `std::panic::PanicHookInfo` is the argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in std context (where panics can have an arbitrary payload), while `core::panic::PanicInfo` is the argument to the [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) in no_std context (where panics always carry a formatted *message*). Separating these types allows us to add more useful methods to these types, such as `std::panic::PanicHookInfo::payload_as_str()` and `core::panic::PanicInfo::message()`. + +* The new sort implementations may panic if a type's implementation of [`Ord`](https://doc.rust-lang.org/std/cmp/trait.Ord.html) (or the given comparison function) does not implement a [total order](https://en.wikipedia.org/wiki/Total_order) as the trait requires. `Ord`'s supertraits (`PartialOrd`, `Eq`, and `PartialEq`) must also be consistent. The previous implementations would not "notice" any problem, but the new implementations have a good chance of detecting inconsistencies, throwing a panic rather than returning knowingly unsorted data. +* [In very rare cases, a change in the internal evaluation order of the trait + solver may result in new fatal overflow errors.](https://github.com/rust-lang/rust/pull/126128) + + + + +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. + +- [Add a Rust-for Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/) + +Version 1.80.1 (2024-08-08) +=========================== + + + +- [Fix miscompilation in the jump threading MIR optimization when comparing floats](https://github.com/rust-lang/rust/pull/128271) +- [Revert changes to the `dead_code` lint from 1.80.0](https://github.com/rust-lang/rust/pull/128618) + Version 1.80.0 (2024-07-25) ========================== diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index 5008069542f1f..a2fc9d5c408e3 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -30,5 +30,6 @@ features = ['unprefixed_malloc_on_supported_platforms'] jemalloc = ['dep:jemalloc-sys'] llvm = ['rustc_driver_impl/llvm'] max_level_info = ['rustc_driver_impl/max_level_info'] +rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts'] rustc_use_parallel_compiler = ['rustc_driver_impl/rustc_use_parallel_compiler'] # tidy-alphabetical-end diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 5160b4ed0a2ac..7432768be4a58 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -968,8 +968,8 @@ fn univariant< let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; let mut max_repr_align = repr.align; let mut inverse_memory_index: IndexVec = fields.indices().collect(); - let optimize = !repr.inhibit_struct_field_reordering(); - if optimize && fields.len() > 1 { + let optimize_field_order = !repr.inhibit_struct_field_reordering(); + if optimize_field_order && fields.len() > 1 { let end = if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; let optimizing = &mut inverse_memory_index.raw[..end]; let fields_excluding_tail = &fields.raw[..end]; @@ -1176,7 +1176,7 @@ fn univariant< // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0. // Field 5 would be the first element, so memory_index is i: // Note: if we didn't optimize, it's already right. - let memory_index = if optimize { + let memory_index = if optimize_field_order { inverse_memory_index.invert_bijective_mapping() } else { debug_assert!(inverse_memory_index.iter().copied().eq(fields.indices())); @@ -1189,6 +1189,9 @@ fn univariant< } let mut layout_of_single_non_zst_field = None; let mut abi = Abi::Aggregate { sized }; + + let optimize_abi = !repr.inhibit_newtype_abi_optimization(); + // Try to make this a Scalar/ScalarPair. if sized && size.bytes() > 0 { // We skip *all* ZST here and later check if we are good in terms of alignment. @@ -1205,7 +1208,7 @@ fn univariant< match field.abi { // For plain scalars, or vectors of them, we can't unpack // newtypes for `#[repr(C)]`, as that affects C ABIs. - Abi::Scalar(_) | Abi::Vector { .. } if optimize => { + Abi::Scalar(_) | Abi::Vector { .. } if optimize_abi => { abi = field.abi; } // But scalar pairs are Rust-specific and get diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index df29b3d54f0fb..be42bc8493243 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -43,14 +43,17 @@ bitflags! { const IS_SIMD = 1 << 1; const IS_TRANSPARENT = 1 << 2; // Internal only for now. If true, don't reorder fields. + // On its own it does not prevent ABI optimizations. const IS_LINEAR = 1 << 3; - // If true, the type's layout can be randomized using - // the seed stored in `ReprOptions.field_shuffle_seed` + // If true, the type's crate has opted into layout randomization. + // Other flags can still inhibit reordering and thus randomization. + // The seed stored in `ReprOptions.field_shuffle_seed`. const RANDOMIZE_LAYOUT = 1 << 4; // Any of these flags being set prevent field reordering optimisation. - const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits() + const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits() | ReprFlags::IS_SIMD.bits() | ReprFlags::IS_LINEAR.bits(); + const ABI_UNOPTIMIZABLE = ReprFlags::IS_C.bits() | ReprFlags::IS_SIMD.bits(); } } @@ -139,10 +142,14 @@ impl ReprOptions { self.c() || self.int.is_some() } + pub fn inhibit_newtype_abi_optimization(&self) -> bool { + self.flags.intersects(ReprFlags::ABI_UNOPTIMIZABLE) + } + /// Returns `true` if this `#[repr()]` guarantees a fixed field order, /// e.g. `repr(C)` or `repr()`. pub fn inhibit_struct_field_reordering(&self) -> bool { - self.flags.intersects(ReprFlags::IS_UNOPTIMISABLE) || self.int.is_some() + self.flags.intersects(ReprFlags::FIELD_ORDER_UNOPTIMIZABLE) || self.int.is_some() } /// Returns `true` if this type is valid for reordering and `-Z randomize-layout` diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 8a66894a35603..7c8af6c4a5564 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -752,7 +752,7 @@ fn visit_lazy_tts(vis: &mut T, lazy_tts: &mut Option(vis: &mut T, t: &mut Token) { let Token { kind, span } = t; match kind { - token::Ident(name, _ /*raw*/) | token::Lifetime(name) => { + token::Ident(name, _is_raw) | token::Lifetime(name, _is_raw) => { let mut ident = Ident::new(*name, *span); vis.visit_ident(&mut ident); *name = ident.name; @@ -762,7 +762,7 @@ pub fn visit_token(vis: &mut T, t: &mut Token) { token::NtIdent(ident, _is_raw) => { vis.visit_ident(ident); } - token::NtLifetime(ident) => { + token::NtLifetime(ident, _is_raw) => { vis.visit_ident(ident); } token::Interpolated(nt) => { diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index f1dddb3acacaa..a0082a41713be 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -331,11 +331,11 @@ pub enum TokenKind { /// Do not forget about `NtLifetime` when you want to match on lifetime identifiers. /// It's recommended to use `Token::(lifetime,uninterpolate,uninterpolated_span)` to /// treat regular and interpolated lifetime identifiers in the same way. - Lifetime(Symbol), + Lifetime(Symbol, IdentIsRaw), /// This identifier (and its span) is the lifetime passed to the /// declarative macro. The span in the surrounding `Token` is the span of /// the `lifetime` metavariable in the macro's RHS. - NtLifetime(Ident), + NtLifetime(Ident, IdentIsRaw), /// An embedded AST node, as produced by a macro. This only exists for /// historical reasons. We'd like to get rid of it, for multiple reasons. @@ -458,7 +458,7 @@ impl Token { /// if they keep spans or perform edition checks. pub fn uninterpolated_span(&self) -> Span { match self.kind { - NtIdent(ident, _) | NtLifetime(ident) => ident.span, + NtIdent(ident, _) | NtLifetime(ident, _) => ident.span, Interpolated(ref nt) => nt.use_span(), _ => self.span, } @@ -661,7 +661,9 @@ impl Token { pub fn uninterpolate(&self) -> Cow<'_, Token> { match self.kind { NtIdent(ident, is_raw) => Cow::Owned(Token::new(Ident(ident.name, is_raw), ident.span)), - NtLifetime(ident) => Cow::Owned(Token::new(Lifetime(ident.name), ident.span)), + NtLifetime(ident, is_raw) => { + Cow::Owned(Token::new(Lifetime(ident.name, is_raw), ident.span)) + } _ => Cow::Borrowed(self), } } @@ -679,11 +681,11 @@ impl Token { /// Returns a lifetime identifier if this token is a lifetime. #[inline] - pub fn lifetime(&self) -> Option { + pub fn lifetime(&self) -> Option<(Ident, IdentIsRaw)> { // We avoid using `Token::uninterpolate` here because it's slow. match self.kind { - Lifetime(name) => Some(Ident::new(name, self.span)), - NtLifetime(ident) => Some(ident), + Lifetime(name, is_raw) => Some((Ident::new(name, self.span), is_raw)), + NtLifetime(ident, is_raw) => Some((ident, is_raw)), _ => None, } } @@ -865,7 +867,7 @@ impl Token { _ => return None, }, SingleQuote => match joint.kind { - Ident(name, IdentIsRaw::No) => Lifetime(Symbol::intern(&format!("'{name}"))), + Ident(name, is_raw) => Lifetime(Symbol::intern(&format!("'{name}")), is_raw), _ => return None, }, diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 057b4455dca89..fc1dd2caf681a 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -482,11 +482,11 @@ impl TokenStream { token::NtIdent(ident, is_raw) => { TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span), spacing) } - token::NtLifetime(ident) => TokenTree::Delimited( + token::NtLifetime(ident, is_raw) => TokenTree::Delimited( DelimSpan::from_single(token.span), DelimSpacing::new(Spacing::JointHidden, spacing), Delimiter::Invisible, - TokenStream::token_alone(token::Lifetime(ident.name), ident.span), + TokenStream::token_alone(token::Lifetime(ident.name, is_raw), ident.span), ), token::Interpolated(ref nt) => TokenTree::Delimited( DelimSpan::from_single(token.span), diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index fe07ec48f1f2b..7b041768983e6 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -69,14 +69,14 @@ pub enum FnKind<'a> { Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, &'a Generics, Option<&'a Block>), /// E.g., `|x, y| body`. - Closure(&'a ClosureBinder, &'a FnDecl, &'a Expr), + Closure(&'a ClosureBinder, &'a Option, &'a FnDecl, &'a Expr), } impl<'a> FnKind<'a> { pub fn header(&self) -> Option<&'a FnHeader> { match *self { FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header), - FnKind::Closure(_, _, _) => None, + FnKind::Closure(..) => None, } } @@ -90,7 +90,7 @@ impl<'a> FnKind<'a> { pub fn decl(&self) -> &'a FnDecl { match self { FnKind::Fn(_, _, sig, _, _, _) => &sig.decl, - FnKind::Closure(_, decl, _) => decl, + FnKind::Closure(_, _, decl, _) => decl, } } @@ -839,7 +839,7 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu try_visit!(walk_fn_decl(visitor, decl)); visit_opt!(visitor, visit_block, body); } - FnKind::Closure(binder, decl, body) => { + FnKind::Closure(binder, _coroutine_kind, decl, body) => { try_visit!(visitor.visit_closure_binder(binder)); try_visit!(walk_fn_decl(visitor, decl)); try_visit!(visitor.visit_expr(body)); @@ -1107,7 +1107,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V ExprKind::Closure(box Closure { binder, capture_clause, - coroutine_kind: _, + coroutine_kind, constness: _, movability: _, fn_decl, @@ -1116,7 +1116,11 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V fn_arg_span: _, }) => { try_visit!(visitor.visit_capture_by(capture_clause)); - try_visit!(visitor.visit_fn(FnKind::Closure(binder, fn_decl, body), *span, *id)) + try_visit!(visitor.visit_fn( + FnKind::Closure(binder, coroutine_kind, fn_decl, body), + *span, + *id + )) } ExprKind::Block(block, opt_label) => { visit_opt!(visitor, visit_label, opt_label); diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 800773482041a..ac527df474aff 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -72,7 +72,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn has_self(&self, def_id: DefId, span: Span) -> bool { if let Some(local_sig_id) = def_id.as_local() { // The value may be missing due to recursive delegation. - // Error will be emmited later during HIR ty lowering. + // Error will be emitted later during HIR ty lowering. self.resolver.delegation_fn_sigs.get(&local_sig_id).map_or(false, |sig| sig.has_self) } else { match self.tcx.def_kind(def_id) { @@ -139,7 +139,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn param_count(&self, sig_id: DefId) -> (usize, bool /*c_variadic*/) { if let Some(local_sig_id) = sig_id.as_local() { // Map may be filled incorrectly due to recursive delegation. - // Error will be emmited later during HIR ty lowering. + // Error will be emitted later during HIR ty lowering. match self.resolver.delegation_fn_sigs.get(&local_sig_id) { Some(sig) => (sig.param_count, sig.c_variadic), None => (0, false), diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index bcc2c29a2ff79..754fbae4d023f 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -45,7 +45,6 @@ use std::collections::hash_map::Entry; use rustc_ast::node_id::NodeMap; use rustc_ast::ptr::P; use rustc_ast::{self as ast, *}; -use rustc_ast_pretty::pprust; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexSet; @@ -837,7 +836,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided(kind)) } - LifetimeRes::Static | LifetimeRes::Error => return None, + LifetimeRes::Static { .. } | LifetimeRes::Error => return None, res => panic!( "Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, ident.span @@ -1399,24 +1398,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnApit { span }); } - let span = t.span; - - // HACK: pprust breaks strings with newlines when the type - // gets too long. We don't want these to show up in compiler - // output or built artifacts, so replace them here... - // Perhaps we should instead format APITs more robustly. - let ident = Ident::from_str_and_span( - &pprust::ty_to_string(t).replace('\n', " "), - span, - ); - - self.create_def( - self.current_hir_id_owner.def_id, // FIXME: should this use self.current_def_id_parent? - *def_node_id, - ident.name, - DefKind::TyParam, - span, - ); + let def_id = self.local_def_id(*def_node_id); + let name = self.tcx.item_name(def_id.to_def_id()); + let ident = Ident::new(name, span); let (param, bounds, path) = self.lower_universal_param_and_bounds( *def_node_id, span, @@ -1618,13 +1602,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { opaque_ty_span: Span, lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>], ) -> hir::TyKind<'hir> { - let opaque_ty_def_id = self.create_def( - self.current_hir_id_owner.def_id, // FIXME: should this use self.current_def_id_parent? - opaque_ty_node_id, - kw::Empty, - DefKind::OpaqueTy, - opaque_ty_span, - ); + let opaque_ty_def_id = self.local_def_id(opaque_ty_node_id); debug!(?opaque_ty_def_id); // Map from captured (old) lifetime to synthetic (new) lifetime. @@ -1656,7 +1634,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } // Opaques do not capture `'static` - LifetimeRes::Static | LifetimeRes::Error => { + LifetimeRes::Static { .. } | LifetimeRes::Error => { continue; } @@ -2069,7 +2047,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::LifetimeName::Param(param) } LifetimeRes::Infer => hir::LifetimeName::Infer, - LifetimeRes::Static => hir::LifetimeName::Static, + LifetimeRes::Static { .. } => hir::LifetimeName::Static, LifetimeRes::Error => hir::LifetimeName::Error, res => panic!( "Unexpected lifetime resolution {:?} for {:?} at {:?}", diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs index 77cc2a36a531d..76c957afa5461 100644 --- a/compiler/rustc_ast_lowering/src/lifetime_collector.rs +++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs @@ -27,7 +27,7 @@ impl<'ast> LifetimeCollectVisitor<'ast> { self.collected_lifetimes.insert(lifetime); } } - LifetimeRes::Static | LifetimeRes::Error => { + LifetimeRes::Static { .. } | LifetimeRes::Error => { self.collected_lifetimes.insert(lifetime); } LifetimeRes::Infer => {} diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a9b65456b8c63..ed9672a9e79b3 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1485,7 +1485,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { let disallowed = (!tilde_const_allowed).then(|| match fk { FnKind::Fn(_, ident, _, _, _, _) => TildeConstReason::Function { ident: ident.span }, - FnKind::Closure(_, _, _) => TildeConstReason::Closure, + FnKind::Closure(..) => TildeConstReason::Closure, }); self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk)); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index c7ff39d23ed26..3b1449d9a9113 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -11,7 +11,9 @@ use std::borrow::Cow; use ast::TraitBoundModifiers; use rustc_ast::attr::AttrIdGenerator; use rustc_ast::ptr::P; -use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, Token, TokenKind}; +use rustc_ast::token::{ + self, BinOpToken, CommentKind, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind, +}; use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{Comment, CommentStyle}; @@ -947,8 +949,13 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere token::NtIdent(ident, is_raw) => { IdentPrinter::for_ast_ident(ident, is_raw.into()).to_string().into() } - token::Lifetime(name) => name.to_string().into(), - token::NtLifetime(ident) => ident.name.to_string().into(), + + token::Lifetime(name, IdentIsRaw::No) + | token::NtLifetime(Ident { name, .. }, IdentIsRaw::No) => name.to_string().into(), + token::Lifetime(name, IdentIsRaw::Yes) + | token::NtLifetime(Ident { name, .. }, IdentIsRaw::Yes) => { + format!("'r#{}", &name.as_str()[1..]).into() + } /* Other */ token::DocComment(comment_kind, attr_style, data) => { diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 51b420c441a7c..fbda44550a19c 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -8,6 +8,7 @@ use rustc_middle::mir::{self, traversal, Body, Local, Location}; use rustc_middle::span_bug; use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::move_paths::MoveData; +use tracing::debug; use crate::path_utils::allow_two_phase_borrow; use crate::place_ext::PlaceExt; diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 2c672dbf8c4ae..3a7e407f3e7a6 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -8,7 +8,7 @@ use rustc_middle::span_bug; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; -impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { +impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> { pub(crate) fn dcx(&self) -> DiagCtxtHandle<'infcx> { self.infcx.dcx() } diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index 7062632de6645..ca435ee08650a 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -5,6 +5,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo}; use rustc_span::Span; +use tracing::{debug, instrument}; use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker}; use crate::type_check::Locations; diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 2795bc7162fc8..39994ad784a95 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -10,28 +10,29 @@ use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::fmt::DebugWithContext; use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces}; use rustc_mir_dataflow::{Analysis, AnalysisDomain, GenKill, Results, ResultsVisitable}; +use tracing::debug; use crate::{places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext}; /// The results of the dataflow analyses used by the borrow checker. -pub(crate) struct BorrowckResults<'a, 'mir, 'tcx> { - pub(crate) borrows: Results<'tcx, Borrows<'a, 'mir, 'tcx>>, - pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'a, 'mir, 'tcx>>, - pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'a, 'mir, 'tcx>>, +pub(crate) struct BorrowckResults<'a, 'tcx> { + pub(crate) borrows: Results<'tcx, Borrows<'a, 'tcx>>, + pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'a, 'tcx>>, + pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'a, 'tcx>>, } /// The transient state of the dataflow analyses used by the borrow checker. #[derive(Debug)] -pub(crate) struct BorrowckFlowState<'a, 'mir, 'tcx> { - pub(crate) borrows: as AnalysisDomain<'tcx>>::Domain, - pub(crate) uninits: as AnalysisDomain<'tcx>>::Domain, - pub(crate) ever_inits: as AnalysisDomain<'tcx>>::Domain, +pub(crate) struct BorrowckFlowState<'a, 'tcx> { + pub(crate) borrows: as AnalysisDomain<'tcx>>::Domain, + pub(crate) uninits: as AnalysisDomain<'tcx>>::Domain, + pub(crate) ever_inits: as AnalysisDomain<'tcx>>::Domain, } -impl<'a, 'mir, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'a, 'mir, 'tcx> { +impl<'a, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'a, 'tcx> { // All three analyses are forward, but we have to use just one here. - type Direction = as AnalysisDomain<'tcx>>::Direction; - type FlowState = BorrowckFlowState<'a, 'mir, 'tcx>; + type Direction = as AnalysisDomain<'tcx>>::Direction; + type FlowState = BorrowckFlowState<'a, 'tcx>; fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { BorrowckFlowState { @@ -105,10 +106,9 @@ rustc_index::newtype_index! { /// `BorrowIndex`, and maps each such index to a `BorrowData` /// describing the borrow. These indexes are used for representing the /// borrows in compact bitvectors. -pub struct Borrows<'a, 'mir, 'tcx> { +pub struct Borrows<'a, 'tcx> { tcx: TyCtxt<'tcx>, - body: &'mir Body<'tcx>, - + body: &'a Body<'tcx>, borrow_set: &'a BorrowSet<'tcx>, borrows_out_of_scope_at_location: FxIndexMap>, } @@ -388,10 +388,10 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { } } -impl<'a, 'mir, 'tcx> Borrows<'a, 'mir, 'tcx> { +impl<'a, 'tcx> Borrows<'a, 'tcx> { pub fn new( tcx: TyCtxt<'tcx>, - body: &'mir Body<'tcx>, + body: &'a Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, borrow_set: &'a BorrowSet<'tcx>, ) -> Self { @@ -493,7 +493,7 @@ impl<'a, 'mir, 'tcx> Borrows<'a, 'mir, 'tcx> { } } -impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, '_, 'tcx> { +impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> { type Domain = BitSet; const NAME: &'static str = "borrows"; @@ -516,7 +516,7 @@ impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, '_, 'tcx> { /// region stops containing the CFG points reachable from the issuing location. /// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of /// `a.b.c` when `a` is overwritten. -impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, '_, 'tcx> { +impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { type Idx = BorrowIndex; fn domain_size(&self, _: &mir::Body<'tcx>) -> usize { @@ -616,8 +616,8 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, '_, 'tcx> { } } -impl DebugWithContext> for BorrowIndex { - fn fmt_with(&self, ctxt: &Borrows<'_, '_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl DebugWithContext> for BorrowIndex { + fn fmt_with(&self, ctxt: &Borrows<'_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", ctxt.location(*self)) } } diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 52eda72177392..ee28e556cbc34 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -19,6 +19,7 @@ use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::query::type_op; use rustc_trait_selection::traits::ObligationCtxt; use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_cause}; +use tracing::{debug, instrument}; use crate::region_infer::values::RegionElement; use crate::session_diagnostics::{ @@ -51,7 +52,7 @@ impl<'tcx> UniverseInfo<'tcx> { pub(crate) fn report_error( &self, - mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>, + mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion, error_element: RegionElement, cause: ObligationCause<'tcx>, @@ -150,7 +151,7 @@ trait TypeOpInfo<'tcx> { fn nice_error<'infcx>( &self, - mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>, + mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>, cause: ObligationCause<'tcx>, placeholder_region: ty::Region<'tcx>, error_region: Option>, @@ -159,7 +160,7 @@ trait TypeOpInfo<'tcx> { #[instrument(level = "debug", skip(self, mbcx))] fn report_error( &self, - mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>, + mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion, error_element: RegionElement, cause: ObligationCause<'tcx>, @@ -232,7 +233,7 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> { fn nice_error<'infcx>( &self, - mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>, + mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>, cause: ObligationCause<'tcx>, placeholder_region: ty::Region<'tcx>, error_region: Option>, @@ -276,7 +277,7 @@ where fn nice_error<'infcx>( &self, - mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>, + mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>, cause: ObligationCause<'tcx>, placeholder_region: ty::Region<'tcx>, error_region: Option>, @@ -323,7 +324,7 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> { fn nice_error<'infcx>( &self, - mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>, + mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>, cause: ObligationCause<'tcx>, placeholder_region: ty::Region<'tcx>, error_region: Option>, @@ -356,7 +357,7 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> { fn nice_error<'infcx>( &self, - mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>, + mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>, _cause: ObligationCause<'tcx>, placeholder_region: ty::Region<'tcx>, error_region: Option>, diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index aaeedde2bedc3..a47518fca3f9b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -40,6 +40,7 @@ use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; +use tracing::{debug, instrument}; use super::explain_borrow::{BorrowExplanation, LaterUseKind}; use super::{DescribePlaceOpt, RegionName, RegionNameSource, UseSpans}; @@ -68,7 +69,7 @@ enum StorageDeadOrDrop<'tcx> { Destructor(Ty<'tcx>), } -impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { pub(crate) fn report_use_of_moved_or_uninitialized( &mut self, location: Location, @@ -1178,7 +1179,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { for field in &variant.fields { // In practice unless there are more than one field with the same type, we'll be // suggesting a single field at a type, because we don't aggregate multiple borrow - // checker errors involving the functional record update sytnax into a single one. + // checker errors involving the functional record update syntax into a single one. let field_ty = field.ty(self.infcx.tcx, args); let ident = field.ident(self.infcx.tcx); if field_ty == ty && fields.iter().all(|field| field.ident.name != ident.name) { @@ -4357,11 +4358,7 @@ enum AnnotatedBorrowFnSignature<'tcx> { impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { /// Annotate the provided diagnostic with information about borrow from the fn signature that /// helps explain. - pub(crate) fn emit( - &self, - cx: &MirBorrowckCtxt<'_, '_, '_, 'tcx>, - diag: &mut Diag<'_>, - ) -> String { + pub(crate) fn emit(&self, cx: &MirBorrowckCtxt<'_, '_, 'tcx>, diag: &mut Diag<'_>) -> String { match self { &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => { diag.span_label( diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index d85959c9a291e..419e72df00b02 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -20,6 +20,7 @@ use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, DesugaringKind, Span}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; +use tracing::{debug, instrument}; use super::{find_use, RegionName, UseSpans}; use crate::borrow_set::BorrowData; @@ -389,7 +390,7 @@ impl<'tcx> BorrowExplanation<'tcx> { } } -impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { +impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { fn free_region_constraint_info( &self, borrow_region: RegionVid, @@ -661,9 +662,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { // `&dyn Trait` ty::Ref(_, ty, _) if ty.is_trait() => true, // `Box` - _ if ty.is_box() && ty.boxed_ty().is_trait() => { + _ if ty.boxed_ty().is_some_and(Ty::is_trait) => { true } + // `dyn Trait` _ if ty.is_trait() => true, // Anything else. diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 33f91d7ad3043..e52ddfa3eb543 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -27,6 +27,7 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{ type_known_to_meet_bound_modulo_regions, FulfillmentErrorCode, }; +use tracing::debug; use super::borrow_set::BorrowData; use super::MirBorrowckCtxt; @@ -67,7 +68,7 @@ pub(super) struct DescribePlaceOpt { pub(super) struct IncludingTupleField(pub(super) bool); -impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure /// is moved after being invoked. /// @@ -344,9 +345,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { variant_index: Option, including_tuple_field: IncludingTupleField, ) -> Option { - if ty.is_box() { + if let Some(boxed_ty) = ty.boxed_ty() { // If the type is a box, the field is described from the boxed type - self.describe_field_from_ty(ty.boxed_ty(), field, variant_index, including_tuple_field) + self.describe_field_from_ty(boxed_ty, field, variant_index, including_tuple_field) } else { match *ty.kind() { ty::Adt(def, _) => { @@ -771,7 +772,7 @@ struct CapturedMessageOpt { maybe_reinitialized_locations_is_empty: bool, } -impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// Finds the spans associated to a move or copy of move_place at location. pub(super) fn move_spans( &self, diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 42b1ffd58ad3e..5a9eba34d07f1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; use rustc_span::{BytePos, ExpnKind, MacroKind, Span}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; +use tracing::debug; use crate::diagnostics::{CapturedMessageOpt, DescribePlaceOpt, UseSpans}; use crate::prefixes::PrefixSet; @@ -92,7 +93,7 @@ enum GroupedMoveError<'tcx> { }, } -impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { pub(crate) fn report_move_errors(&mut self) { let grouped_errors = self.group_move_errors(); for error in grouped_errors { diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 7b791928689c9..6e3fac1e68075 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -20,6 +20,7 @@ use rustc_target::abi::FieldIdx; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits; +use tracing::debug; use crate::diagnostics::BorrowedContentSource; use crate::util::FindAssignments; @@ -31,7 +32,7 @@ pub(crate) enum AccessKind { Mutate, } -impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { pub(crate) fn report_mutability_error( &mut self, access_place: Place<'tcx>, diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index a59b7b3cde914..bd0cf3578c2bb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -10,6 +10,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::Diag; use rustc_middle::ty::RegionVid; use smallvec::SmallVec; +use tracing::debug; use super::{ErrorConstraintInfo, RegionName, RegionNameSource}; use crate::MirBorrowckCtxt; @@ -75,7 +76,7 @@ impl OutlivesSuggestionBuilder { /// Returns a name for the region if it is suggestable. See `region_name_is_suggestable`. fn region_vid_to_name( &self, - mbcx: &MirBorrowckCtxt<'_, '_, '_, '_>, + mbcx: &MirBorrowckCtxt<'_, '_, '_>, region: RegionVid, ) -> Option { mbcx.give_region_a_name(region).filter(Self::region_name_is_suggestable) @@ -84,7 +85,7 @@ impl OutlivesSuggestionBuilder { /// Compiles a list of all suggestions to be printed in the final big suggestion. fn compile_all_suggestions( &self, - mbcx: &MirBorrowckCtxt<'_, '_, '_, '_>, + mbcx: &MirBorrowckCtxt<'_, '_, '_>, ) -> SmallVec<[SuggestedConstraint; 2]> { let mut suggested = SmallVec::new(); @@ -160,7 +161,7 @@ impl OutlivesSuggestionBuilder { /// Emit an intermediate note on the given `Diag` if the involved regions are suggestable. pub(crate) fn intermediate_suggestion( &mut self, - mbcx: &MirBorrowckCtxt<'_, '_, '_, '_>, + mbcx: &MirBorrowckCtxt<'_, '_, '_>, errci: &ErrorConstraintInfo<'_>, diag: &mut Diag<'_>, ) { @@ -179,7 +180,7 @@ impl OutlivesSuggestionBuilder { /// If there is a suggestion to emit, add a diagnostic to the buffer. This is the final /// suggestion including all collected constraints. - pub(crate) fn add_suggestion(&self, mbcx: &mut MirBorrowckCtxt<'_, '_, '_, '_>) { + pub(crate) fn add_suggestion(&self, mbcx: &mut MirBorrowckCtxt<'_, '_, '_>) { // No constraints to add? Done. if self.constraints_to_add.is_empty() { debug!("No constraints to suggest."); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index ab48a09cfa4ce..57c3a0843a694 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -25,6 +25,7 @@ use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_reg use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; +use tracing::{debug, instrument, trace}; use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource}; use crate::nll::ConstraintDescription; @@ -155,7 +156,7 @@ pub(crate) struct ErrorConstraintInfo<'tcx> { pub(super) span: Span, } -impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// Converts a region inference variable into a `ty::Region` that /// we can use for error reporting. If `r` is universally bound, /// then we use the name that we have on record for it. If `r` is diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 12aedf6fe088b..58fcda2571cc0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -14,6 +14,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; +use tracing::{debug, instrument}; use crate::universal_regions::DefiningTy; use crate::MirBorrowckCtxt; @@ -199,7 +200,7 @@ impl rustc_errors::IntoDiagArg for RegionName { } } -impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { +impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { pub(crate) fn mir_def_id(&self) -> hir::def_id::LocalDefId { self.body.source.def_id().expect_local() } diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index 3e9f975b66bb6..2a4fa1e00191c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -3,6 +3,7 @@ use rustc_middle::mir::{Body, Local}; use rustc_middle::ty::{self, RegionVid, TyCtxt}; use rustc_span::symbol::Symbol; use rustc_span::Span; +use tracing::debug; use crate::region_infer::RegionInferenceContext; diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index bb1aea14693e9..d5f297a591382 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -15,9 +15,6 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end -#[macro_use] -extern crate tracing; - use std::cell::RefCell; use std::collections::BTreeMap; use std::marker::PhantomData; @@ -51,6 +48,7 @@ use rustc_session::lint::builtin::UNUSED_MUT; use rustc_span::{Span, Symbol}; use rustc_target::abi::FieldIdx; use smallvec::SmallVec; +use tracing::{debug, instrument}; use self::diagnostics::{AccessKind, IllegalMoveOriginKind, MoveError, RegionName}; use self::location::LocationTable; @@ -229,7 +227,7 @@ fn do_mir_borrowck<'tcx>( // Dump MIR results into a file, if that is enabled. This let us // write unit-tests, as well as helping with debugging. - nll::dump_mir_results(&infcx, body, ®ioncx, &opt_closure_req); + nll::dump_nll_mir(&infcx, body, ®ioncx, &opt_closure_req, &borrow_set); // We also have a `#[rustc_regions]` annotation that causes us to dump // information. @@ -306,11 +304,11 @@ fn do_mir_borrowck<'tcx>( promoted_mbcx.report_move_errors(); diags = promoted_mbcx.diags; - struct MoveVisitor<'a, 'b, 'mir, 'infcx, 'tcx> { - ctxt: &'a mut MirBorrowckCtxt<'b, 'mir, 'infcx, 'tcx>, + struct MoveVisitor<'a, 'b, 'infcx, 'tcx> { + ctxt: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>, } - impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, '_, '_, 'tcx> { + impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, '_, 'tcx> { fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { if let Operand::Move(place) = operand { self.ctxt.check_movable_place(location, *place); @@ -524,10 +522,10 @@ impl<'tcx> Deref for BorrowckInferCtxt<'tcx> { } } -struct MirBorrowckCtxt<'a, 'mir, 'infcx, 'tcx> { +struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { infcx: &'infcx BorrowckInferCtxt<'tcx>, param_env: ParamEnv<'tcx>, - body: &'mir Body<'tcx>, + body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>, /// Map from MIR `Location` to `LocationIndex`; created @@ -601,16 +599,16 @@ struct MirBorrowckCtxt<'a, 'mir, 'infcx, 'tcx> { // 2. loans made in overlapping scopes do not conflict // 3. assignments do not affect things loaned out as immutable // 4. moves do not affect things loaned out in any way -impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> - for MirBorrowckCtxt<'a, 'mir, '_, 'tcx> +impl<'a, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'a, 'tcx, R> + for MirBorrowckCtxt<'a, '_, 'tcx> { - type FlowState = Flows<'a, 'mir, 'tcx>; + type FlowState = Flows<'a, 'tcx>; fn visit_statement_before_primary_effect( &mut self, _results: &mut R, - flow_state: &Flows<'_, 'mir, 'tcx>, - stmt: &'mir Statement<'tcx>, + flow_state: &Flows<'a, 'tcx>, + stmt: &'a Statement<'tcx>, location: Location, ) { debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {:?}", location, stmt, flow_state); @@ -679,8 +677,8 @@ impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> fn visit_terminator_before_primary_effect( &mut self, _results: &mut R, - flow_state: &Flows<'_, 'mir, 'tcx>, - term: &'mir Terminator<'tcx>, + flow_state: &Flows<'a, 'tcx>, + term: &'a Terminator<'tcx>, loc: Location, ) { debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {:?}", loc, term, flow_state); @@ -796,8 +794,8 @@ impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> fn visit_terminator_after_primary_effect( &mut self, _results: &mut R, - flow_state: &Flows<'_, 'mir, 'tcx>, - term: &'mir Terminator<'tcx>, + flow_state: &Flows<'a, 'tcx>, + term: &'a Terminator<'tcx>, loc: Location, ) { let span = term.source_info.span; @@ -974,8 +972,8 @@ impl InitializationRequiringAction { } } -impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { - fn body(&self) -> &'mir Body<'tcx> { +impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { + fn body(&self) -> &'a Body<'tcx> { self.body } @@ -991,7 +989,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { place_span: (Place<'tcx>, Span), kind: (AccessDepth, ReadOrWrite), is_local_mutation_allowed: LocalMutationIsAllowed, - flow_state: &Flows<'_, 'mir, 'tcx>, + flow_state: &Flows<'a, 'tcx>, ) { let (sd, rw) = kind; @@ -1041,7 +1039,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { place_span: (Place<'tcx>, Span), sd: AccessDepth, rw: ReadOrWrite, - flow_state: &Flows<'_, 'mir, 'tcx>, + flow_state: &Flows<'a, 'tcx>, ) -> bool { let mut error_reported = false; let borrow_set = Rc::clone(&self.borrow_set); @@ -1182,7 +1180,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { location: Location, place_span: (Place<'tcx>, Span), kind: AccessDepth, - flow_state: &Flows<'_, 'mir, 'tcx>, + flow_state: &Flows<'a, 'tcx>, ) { // Write of P[i] or *P requires P init'd. self.check_if_assigned_path_is_moved(location, place_span, flow_state); @@ -1199,8 +1197,8 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { fn consume_rvalue( &mut self, location: Location, - (rvalue, span): (&'mir Rvalue<'tcx>, Span), - flow_state: &Flows<'_, 'mir, 'tcx>, + (rvalue, span): (&'a Rvalue<'tcx>, Span), + flow_state: &Flows<'a, 'tcx>, ) { match rvalue { &Rvalue::Ref(_ /*rgn*/, bk, place) => { @@ -1457,8 +1455,8 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { fn consume_operand( &mut self, location: Location, - (operand, span): (&'mir Operand<'tcx>, Span), - flow_state: &Flows<'_, 'mir, 'tcx>, + (operand, span): (&'a Operand<'tcx>, Span), + flow_state: &Flows<'a, 'tcx>, ) { match *operand { Operand::Copy(place) => { @@ -1578,12 +1576,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { } } - fn check_activations( - &mut self, - location: Location, - span: Span, - flow_state: &Flows<'_, 'mir, 'tcx>, - ) { + fn check_activations(&mut self, location: Location, span: Span, flow_state: &Flows<'a, 'tcx>) { // Two-phase borrow support: For each activation that is newly // generated at this statement, check if it interferes with // another borrow. @@ -1746,7 +1739,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { location: Location, desired_action: InitializationRequiringAction, place_span: (PlaceRef<'tcx>, Span), - flow_state: &Flows<'_, 'mir, 'tcx>, + flow_state: &Flows<'a, 'tcx>, ) { let maybe_uninits = &flow_state.uninits; @@ -1851,7 +1844,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { location: Location, desired_action: InitializationRequiringAction, place_span: (PlaceRef<'tcx>, Span), - flow_state: &Flows<'_, 'mir, 'tcx>, + flow_state: &Flows<'a, 'tcx>, ) { let maybe_uninits = &flow_state.uninits; @@ -1950,7 +1943,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { &mut self, location: Location, (place, span): (Place<'tcx>, Span), - flow_state: &Flows<'_, 'mir, 'tcx>, + flow_state: &Flows<'a, 'tcx>, ) { debug!("check_if_assigned_path_is_moved place: {:?}", place); @@ -2011,12 +2004,12 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { } } - fn check_parent_of_field<'mir, 'tcx>( - this: &mut MirBorrowckCtxt<'_, 'mir, '_, 'tcx>, + fn check_parent_of_field<'a, 'tcx>( + this: &mut MirBorrowckCtxt<'a, '_, 'tcx>, location: Location, base: PlaceRef<'tcx>, span: Span, - flow_state: &Flows<'_, 'mir, 'tcx>, + flow_state: &Flows<'a, 'tcx>, ) { // rust-lang/rust#21232: Until Rust allows reads from the // initialized parts of partially initialized structs, we @@ -2107,7 +2100,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { (place, span): (Place<'tcx>, Span), kind: ReadOrWrite, is_local_mutation_allowed: LocalMutationIsAllowed, - flow_state: &Flows<'_, 'mir, 'tcx>, + flow_state: &Flows<'a, 'tcx>, location: Location, ) -> bool { debug!( @@ -2223,7 +2216,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { fn is_local_ever_initialized( &self, local: Local, - flow_state: &Flows<'_, 'mir, 'tcx>, + flow_state: &Flows<'a, 'tcx>, ) -> Option { let mpi = self.move_data.rev_lookup.find_local(local)?; let ii = &self.move_data.init_path_map[mpi]; @@ -2231,7 +2224,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { } /// Adds the place into the used mutable variables set - fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'_, 'mir, 'tcx>) { + fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'a, 'tcx>) { match root_place { RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => { // If the local may have been initialized, and it is now currently being @@ -2486,7 +2479,7 @@ mod diags { } } - impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { + impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) { self.diags.buffer_error(diag); } @@ -2524,7 +2517,7 @@ mod diags { } pub(crate) fn emit_errors(&mut self) -> Option { - let mut res = None; + let mut res = self.infcx.tainted_by_errors(); // Buffer any move errors that we collected and de-duplicated. for (_, (_, diag)) in std::mem::take(&mut self.diags.buffered_move_errors) { diff --git a/compiler/rustc_borrowck/src/location.rs b/compiler/rustc_borrowck/src/location.rs index 63e59217e5d7d..fbe5f94ed42ca 100644 --- a/compiler/rustc_borrowck/src/location.rs +++ b/compiler/rustc_borrowck/src/location.rs @@ -1,5 +1,6 @@ use rustc_index::IndexVec; use rustc_middle::mir::{BasicBlock, Body, Location}; +use tracing::debug; /// Maps between a MIR Location, which identifies a particular /// statement within a basic block, to a "rich location", which diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs index 499c32396d0ae..fc621a3b8283d 100644 --- a/compiler/rustc_borrowck/src/member_constraints.rs +++ b/compiler/rustc_borrowck/src/member_constraints.rs @@ -7,6 +7,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::infer::MemberConstraint; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; +use tracing::debug; /// Compactly stores a set of `R0 member of [R1...Rn]` constraints, /// indexed by the region `R0`. diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index af37c028879d0..8575eb9e9a52d 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -9,6 +9,7 @@ use polonius_engine::{Algorithm, Output}; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::LocalDefId; use rustc_index::IndexSlice; +use rustc_middle::mir::pretty::{dump_mir_with_options, PrettyPrintMirOptions}; use rustc_middle::mir::{ create_dump_file, dump_enabled, dump_mir, Body, ClosureOutlivesSubject, ClosureRegionRequirements, PassWhere, Promoted, @@ -19,7 +20,9 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::ResultsCursor; +use rustc_session::config::MirIncludeSpans; use rustc_span::symbol::sym; +use tracing::{debug, instrument}; use crate::borrow_set::BorrowSet; use crate::consumers::ConsumerOptions; @@ -72,14 +75,14 @@ pub(crate) fn replace_regions_in_mir<'tcx>( /// Computes the (non-lexical) regions from the input MIR. /// /// This may result in errors being reported. -pub(crate) fn compute_regions<'cx, 'tcx>( +pub(crate) fn compute_regions<'a, 'tcx>( infcx: &BorrowckInferCtxt<'tcx>, universal_regions: UniversalRegions<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice>, location_table: &LocationTable, param_env: ty::ParamEnv<'tcx>, - flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'_, 'cx, 'tcx>>, + flow_inits: &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, upvars: &[&ty::CapturedPlace<'tcx>], @@ -208,65 +211,103 @@ pub(crate) fn compute_regions<'cx, 'tcx>( } } -pub(super) fn dump_mir_results<'tcx>( +/// `-Zdump-mir=nll` dumps MIR annotated with NLL specific information: +/// - free regions +/// - inferred region values +/// - region liveness +/// - inference constraints and their causes +/// +/// As well as graphviz `.dot` visualizations of: +/// - the region constraints graph +/// - the region SCC graph +pub(super) fn dump_nll_mir<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option>, + borrow_set: &BorrowSet<'tcx>, ) { - if !dump_enabled(infcx.tcx, "nll", body.source.def_id()) { + let tcx = infcx.tcx; + if !dump_enabled(tcx, "nll", body.source.def_id()) { return; } - dump_mir(infcx.tcx, false, "nll", &0, body, |pass_where, out| { - match pass_where { - // Before the CFG, dump out the values for each region variable. - PassWhere::BeforeCFG => { - regioncx.dump_mir(infcx.tcx, out)?; - writeln!(out, "|")?; - - if let Some(closure_region_requirements) = closure_region_requirements { - writeln!(out, "| Free Region Constraints")?; - for_each_region_constraint( - infcx.tcx, - closure_region_requirements, - &mut |msg| writeln!(out, "| {msg}"), - )?; + // We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in + // #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example, + // they're always disabled in mir-opt tests to make working with blessed dumps easier. + let options = PrettyPrintMirOptions { + include_extra_comments: matches!( + infcx.tcx.sess.opts.unstable_opts.mir_include_spans, + MirIncludeSpans::On | MirIncludeSpans::Nll + ), + }; + dump_mir_with_options( + tcx, + false, + "nll", + &0, + body, + |pass_where, out| { + match pass_where { + // Before the CFG, dump out the values for each region variable. + PassWhere::BeforeCFG => { + regioncx.dump_mir(tcx, out)?; writeln!(out, "|")?; + + if let Some(closure_region_requirements) = closure_region_requirements { + writeln!(out, "| Free Region Constraints")?; + for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| { + writeln!(out, "| {msg}") + })?; + writeln!(out, "|")?; + } + + if borrow_set.len() > 0 { + writeln!(out, "| Borrows")?; + for (borrow_idx, borrow_data) in borrow_set.iter_enumerated() { + writeln!( + out, + "| {:?}: issued at {:?} in {:?}", + borrow_idx, borrow_data.reserve_location, borrow_data.region + )?; + } + writeln!(out, "|")?; + } } - } - PassWhere::BeforeLocation(_) => {} + PassWhere::BeforeLocation(_) => {} - PassWhere::AfterTerminator(_) => {} + PassWhere::AfterTerminator(_) => {} - PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {} - } - Ok(()) - }); + PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {} + } + Ok(()) + }, + options, + ); - // Also dump the inference graph constraints as a graphviz file. + // Also dump the region constraint graph as a graphviz file. let _: io::Result<()> = try { - let mut file = create_dump_file(infcx.tcx, "regioncx.all.dot", false, "nll", &0, body)?; + let mut file = create_dump_file(tcx, "regioncx.all.dot", false, "nll", &0, body)?; regioncx.dump_graphviz_raw_constraints(&mut file)?; }; - // Also dump the inference graph constraints as a graphviz file. + // Also dump the region constraint SCC graph as a graphviz file. let _: io::Result<()> = try { - let mut file = create_dump_file(infcx.tcx, "regioncx.scc.dot", false, "nll", &0, body)?; + let mut file = create_dump_file(tcx, "regioncx.scc.dot", false, "nll", &0, body)?; regioncx.dump_graphviz_scc_constraints(&mut file)?; }; } #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] -pub(super) fn dump_annotation<'tcx, 'cx>( - infcx: &'cx BorrowckInferCtxt<'tcx>, +pub(super) fn dump_annotation<'tcx, 'infcx>( + infcx: &'infcx BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option>, opaque_type_values: &FxIndexMap>, - diags: &mut crate::diags::BorrowckDiags<'cx, 'tcx>, + diags: &mut crate::diags::BorrowckDiags<'infcx, 'tcx>, ) { let tcx = infcx.tcx; let base_def_id = tcx.typeck_root_def_id(body.source.def_id()); diff --git a/compiler/rustc_borrowck/src/path_utils.rs b/compiler/rustc_borrowck/src/path_utils.rs index 4afb41be18f10..f387d5cfedcb9 100644 --- a/compiler/rustc_borrowck/src/path_utils.rs +++ b/compiler/rustc_borrowck/src/path_utils.rs @@ -2,6 +2,7 @@ use rustc_data_structures::graph::dominators::Dominators; use rustc_middle::mir::{BasicBlock, Body, BorrowKind, Location, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::TyCtxt; use rustc_target::abi::FieldIdx; +use tracing::debug; use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation}; use crate::{places_conflict, AccessDepth, BorrowIndex}; diff --git a/compiler/rustc_borrowck/src/place_ext.rs b/compiler/rustc_borrowck/src/place_ext.rs index ce63d51682e5b..7f65301986ff6 100644 --- a/compiler/rustc_borrowck/src/place_ext.rs +++ b/compiler/rustc_borrowck/src/place_ext.rs @@ -2,6 +2,7 @@ use rustc_hir as hir; use rustc_macros::extension; use rustc_middle::mir::{Body, Mutability, Place, ProjectionElem}; use rustc_middle::ty::{self, TyCtxt}; +use tracing::debug; use crate::borrow_set::LocalsStateAtExit; diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 311f17f15b9ee..519ba0b9e0c9f 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -59,6 +59,7 @@ use rustc_middle::mir::{ Body, BorrowKind, FakeBorrowKind, MutBorrowKind, Place, PlaceElem, PlaceRef, ProjectionElem, }; use rustc_middle::ty::{self, TyCtxt}; +use tracing::{debug, instrument}; use crate::{AccessDepth, ArtificialField, Deep, Overlap, Shallow}; diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index a57041cd04c52..0b9b8768b5600 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -7,6 +7,7 @@ use rustc_middle::mir::{ TerminatorKind, }; use rustc_middle::ty::TyCtxt; +use tracing::debug; use crate::borrow_set::BorrowSet; use crate::facts::AllFacts; diff --git a/compiler/rustc_borrowck/src/polonius/loan_kills.rs b/compiler/rustc_borrowck/src/polonius/loan_kills.rs index d85c2175bed59..ed9f714e50062 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_kills.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_kills.rs @@ -4,6 +4,7 @@ use rustc_middle::mir::{ Terminator, TerminatorKind, }; use rustc_middle::ty::TyCtxt; +use tracing::debug; use crate::borrow_set::BorrowSet; use crate::facts::AllFacts; diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index c590104978c2d..6862eb134272c 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -6,6 +6,7 @@ use rustc_middle::mir::{Body, LocalKind, Location, START_BLOCK}; use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData}; +use tracing::debug; use crate::borrow_set::BorrowSet; use crate::facts::{AllFacts, PoloniusRegionVid}; diff --git a/compiler/rustc_borrowck/src/prefixes.rs b/compiler/rustc_borrowck/src/prefixes.rs index 39d831378cde6..aeb8a6c014a4f 100644 --- a/compiler/rustc_borrowck/src/prefixes.rs +++ b/compiler/rustc_borrowck/src/prefixes.rs @@ -34,7 +34,7 @@ pub(super) enum PrefixSet { Shallow, } -impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { +impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { /// Returns an iterator over the prefixes of `place` /// (inclusive) from longest to smallest, potentially /// terminating the iteration early based on `kind`. diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index 743864dd53505..1936752b63c6e 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -46,7 +46,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { dot::render(&RawConstraints { regioncx: self }, &mut w) } - /// Write out the region constraint graph. + /// Write out the region constraint SCC graph. pub(crate) fn dump_graphviz_scc_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> { let mut nodes_per_scc: IndexVec = self.constraint_sccs.all_sccs().map(|_| Vec::new()).collect(); diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 6cbdd890b5e8b..d6bb006cd7e00 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -21,6 +21,7 @@ use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex}; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_span::Span; +use tracing::{debug, instrument, trace}; use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph}; use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet}; diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index cd66acd0a8f21..f97f3dfe29f6a 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::{ use rustc_span::Span; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::ObligationCtxt; +use tracing::{debug, instrument}; use super::RegionInferenceContext; use crate::session_diagnostics::{LifetimeMismatchOpaqueParam, NonGenericOpaqueTypeParam}; diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 1e91130bdc54e..d62f2067729df 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -8,6 +8,7 @@ use rustc_index::Idx; use rustc_middle::mir::{BasicBlock, Location}; use rustc_middle::ty::{self, RegionVid}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; +use tracing::debug; use crate::BorrowIndex; diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index 2a3b51532e542..0a375c7fae8e2 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -4,6 +4,7 @@ use rustc_middle::mir::visit::{MutVisitor, TyContext}; use rustc_middle::mir::{Body, ConstOperand, Location, Promoted}; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeFoldable}; use rustc_span::Symbol; +use tracing::{debug, instrument}; use crate::BorrowckInferCtxt; diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index b58691fbeae3a..4b15ce8873d37 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -10,6 +10,7 @@ use rustc_span::Span; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput}; use rustc_trait_selection::traits::ObligationCause; +use tracing::{debug, instrument}; use super::{Locations, NormalizeLocation, TypeChecker}; use crate::diagnostics::ToUniverseInfo; diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 9876f44c002d4..0f1d79a2c3530 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -13,6 +13,7 @@ use rustc_span::Span; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; use rustc_trait_selection::traits::ScrubbedTraitError; +use tracing::{debug, instrument}; use crate::constraints::OutlivesConstraint; use crate::region_infer::TypeTest; 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 b7fb9964ce74f..33cdb1b1f3774 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -15,6 +15,7 @@ use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::solve::deeply_normalize; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; +use tracing::{debug, instrument}; use type_op::TypeOpOutput; use crate::type_check::{constraint_conversion, Locations, MirTypeckRegionConstraints}; diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index ba6030bdff772..56f8464628c7b 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -15,6 +15,7 @@ use rustc_infer::infer::{BoundRegionConversionTime, RegionVariableOrigin}; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; +use tracing::{debug, instrument}; use super::{Locations, TypeChecker}; use crate::renumber::RegionCtxt; diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index a320add0636af..79db8f4252b55 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -11,6 +11,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::ResultsCursor; +use tracing::debug; use super::TypeChecker; use crate::constraints::OutlivesConstraintSet; @@ -29,11 +30,11 @@ mod trace; /// /// N.B., this computation requires normalization; therefore, it must be /// performed before -pub(super) fn generate<'mir, 'tcx>( +pub(super) fn generate<'a, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, body: &Body<'tcx>, elements: &Rc, - flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>, + flow_inits: &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, ) { debug!("liveness::generate"); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs index 8c13b166c0541..7f6aabf8841dd 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs @@ -2,6 +2,7 @@ use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{Body, Local, Location, Place}; use rustc_middle::ty::GenericArg; use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; +use tracing::debug; use super::TypeChecker; use crate::def_use::{self, DefUse}; diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index f0c521cdcfc5b..4d47863ae76ad 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -15,6 +15,7 @@ use rustc_mir_dataflow::ResultsCursor; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; +use tracing::debug; use crate::location::RichLocation; use crate::region_infer::values::{self, LiveLoans}; @@ -36,11 +37,11 @@ use crate::type_check::{NormalizeLocation, TypeChecker}; /// DROP-LIVE set are to the liveness sets for regions found in the /// `dropck_outlives` result of the variable's type (in particular, /// this respects `#[may_dangle]` annotations). -pub(super) fn trace<'mir, 'tcx>( +pub(super) fn trace<'a, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, body: &Body<'tcx>, elements: &Rc, - flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>, + flow_inits: &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, relevant_live_locals: Vec, boring_locals: Vec, @@ -98,29 +99,29 @@ pub(super) fn trace<'mir, 'tcx>( } /// Contextual state for the type-liveness coroutine. -struct LivenessContext<'a, 'me, 'typeck, 'flow, 'tcx> { +struct LivenessContext<'a, 'typeck, 'b, 'tcx> { /// Current type-checker, giving us our inference context etc. - typeck: &'me mut TypeChecker<'typeck, 'tcx>, + typeck: &'a mut TypeChecker<'typeck, 'tcx>, /// Defines the `PointIndex` mapping - elements: &'me DenseLocationMap, + elements: &'a DenseLocationMap, /// MIR we are analyzing. - body: &'me Body<'tcx>, + body: &'a Body<'tcx>, /// Mapping to/from the various indices used for initialization tracking. - move_data: &'me MoveData<'tcx>, + move_data: &'a MoveData<'tcx>, /// Cache for the results of `dropck_outlives` query. drop_data: FxIndexMap, DropData<'tcx>>, /// Results of dataflow tracking which variables (and paths) have been /// initialized. - flow_inits: &'me mut ResultsCursor<'flow, 'tcx, MaybeInitializedPlaces<'a, 'flow, 'tcx>>, + flow_inits: &'a mut ResultsCursor<'b, 'tcx, MaybeInitializedPlaces<'b, 'tcx>>, /// Index indicating where each variable is assigned, used, or /// dropped. - local_use_map: &'me LocalUseMap, + local_use_map: &'a LocalUseMap, } struct DropData<'tcx> { @@ -128,8 +129,8 @@ struct DropData<'tcx> { region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>, } -struct LivenessResults<'a, 'me, 'typeck, 'flow, 'tcx> { - cx: LivenessContext<'a, 'me, 'typeck, 'flow, 'tcx>, +struct LivenessResults<'a, 'typeck, 'b, 'tcx> { + cx: LivenessContext<'a, 'typeck, 'b, 'tcx>, /// Set of points that define the current local. defs: BitSet, @@ -150,8 +151,8 @@ struct LivenessResults<'a, 'me, 'typeck, 'flow, 'tcx> { stack: Vec, } -impl<'a, 'me, 'typeck, 'flow, 'tcx> LivenessResults<'a, 'me, 'typeck, 'flow, 'tcx> { - fn new(cx: LivenessContext<'a, 'me, 'typeck, 'flow, 'tcx>) -> Self { +impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { + fn new(cx: LivenessContext<'a, 'typeck, 'b, 'tcx>) -> Self { let num_points = cx.elements.num_points(); LivenessResults { cx, @@ -217,7 +218,7 @@ impl<'a, 'me, 'typeck, 'flow, 'tcx> LivenessResults<'a, 'me, 'typeck, 'flow, 'tc // This collect is more necessary than immediately apparent // because these facts go into `add_drop_live_facts_for()`, // which also writes to `all_facts`, and so this is genuinely - // a simulatneous overlapping mutable borrow. + // a simultaneous overlapping mutable borrow. // FIXME for future hackers: investigate whether this is // actually necessary; these facts come from Polonius // and probably maybe plausibly does not need to go back in. @@ -504,7 +505,7 @@ impl<'a, 'me, 'typeck, 'flow, 'tcx> LivenessResults<'a, 'me, 'typeck, 'flow, 'tc } } -impl<'tcx> LivenessContext<'_, '_, '_, '_, 'tcx> { +impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { /// Returns `true` if the local variable (or some part of it) is initialized at the current /// cursor position. Callers should call one of the `seek` methods immediately before to point /// the cursor to the desired location. diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index d8f74840eb52f..3e5a6ad85fa56 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -45,6 +45,7 @@ use rustc_trait_selection::traits::query::type_op::custom::{ }; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; use rustc_trait_selection::traits::PredicateObligation; +use tracing::{debug, instrument, trace}; use crate::borrow_set::BorrowSet; use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet}; @@ -115,7 +116,7 @@ mod relate_tys; /// - `flow_inits` -- results of a maybe-init dataflow analysis /// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis /// - `elements` -- MIR region map -pub(crate) fn type_check<'mir, 'tcx>( +pub(crate) fn type_check<'a, 'tcx>( infcx: &BorrowckInferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, @@ -124,7 +125,7 @@ pub(crate) fn type_check<'mir, 'tcx>( location_table: &LocationTable, borrow_set: &BorrowSet<'tcx>, all_facts: &mut Option, - flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>, + flow_inits: &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, elements: &Rc, upvars: &[&ty::CapturedPlace<'tcx>], @@ -1978,19 +1979,76 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { match cast_kind { CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => { - let fn_sig = op.ty(body, tcx).fn_sig(tcx); + let src_sig = op.ty(body, tcx).fn_sig(tcx); + + // HACK: This shouldn't be necessary... We can remove this when we actually + // get binders with where clauses, then elaborate implied bounds into that + // binder, and implement a higher-ranked subtyping algorithm that actually + // respects these implied bounds. + // + // This protects against the case where we are casting from a higher-ranked + // fn item to a non-higher-ranked fn pointer, where the cast throws away + // implied bounds that would've needed to be checked at the call site. This + // only works when we're casting to a non-higher-ranked fn ptr, since + // placeholders in the target signature could have untracked implied + // bounds, resulting in incorrect errors. + // + // We check that this signature is WF before subtyping the signature with + // the target fn sig. + if src_sig.has_bound_regions() + && let ty::FnPtr(target_fn_tys, target_hdr) = *ty.kind() + && let target_sig = target_fn_tys.with(target_hdr) + && let Some(target_sig) = target_sig.no_bound_vars() + { + let src_sig = self.infcx.instantiate_binder_with_fresh_vars( + span, + BoundRegionConversionTime::HigherRankedType, + src_sig, + ); + let src_ty = Ty::new_fn_ptr(self.tcx(), ty::Binder::dummy(src_sig)); + self.prove_predicate( + ty::ClauseKind::WellFormed(src_ty.into()), + location.to_locations(), + ConstraintCategory::Cast { unsize_to: None }, + ); + + let src_ty = self.normalize(src_ty, location); + if let Err(terr) = self.sub_types( + src_ty, + *ty, + location.to_locations(), + ConstraintCategory::Cast { unsize_to: None }, + ) { + span_mirbug!( + self, + rvalue, + "equating {:?} with {:?} yields {:?}", + target_sig, + src_sig, + terr + ); + }; + } + + let src_ty = Ty::new_fn_ptr(tcx, src_sig); + // HACK: We want to assert that the signature of the source fn is + // well-formed, because we don't enforce that via the WF of FnDef + // types normally. This should be removed when we improve the tracking + // of implied bounds of fn signatures. + self.prove_predicate( + ty::ClauseKind::WellFormed(src_ty.into()), + location.to_locations(), + ConstraintCategory::Cast { unsize_to: None }, + ); // The type that we see in the fcx is like // `foo::<'a, 'b>`, where `foo` is the path to a // function definition. When we extract the // signature, it comes from the `fn_sig` query, // and hence may contain unnormalized results. - let fn_sig = self.normalize(fn_sig, location); - - let ty_fn_ptr_from = Ty::new_fn_ptr(tcx, fn_sig); - + let src_ty = self.normalize(src_ty, location); if let Err(terr) = self.sub_types( - ty_fn_ptr_from, + src_ty, *ty, location.to_locations(), ConstraintCategory::Cast { unsize_to: None }, @@ -1999,7 +2057,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self, rvalue, "equating {:?} with {:?} yields {:?}", - ty_fn_ptr_from, + src_ty, ty, terr ); diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 35d8e2573fe17..bb4a58930e1bb 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -14,6 +14,7 @@ use rustc_middle::ty::fold::FnMutDelegate; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; +use tracing::{debug, instrument}; use crate::constraints::OutlivesConstraint; use crate::diagnostics::UniverseInfo; diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 2e41c91877431..c3edbcb50cc3d 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -29,11 +29,13 @@ use rustc_macros::extension; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, RegionVid, Ty, TyCtxt, + self, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, RegionVid, Ty, + TyCtxt, TypeVisitableExt, }; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::{kw, sym}; use rustc_span::{ErrorGuaranteed, Symbol}; +use tracing::{debug, instrument}; use crate::renumber::RegionCtxt; use crate::BorrowckInferCtxt; @@ -421,8 +423,8 @@ impl<'tcx> UniversalRegions<'tcx> { } } -struct UniversalRegionsBuilder<'cx, 'tcx> { - infcx: &'cx BorrowckInferCtxt<'tcx>, +struct UniversalRegionsBuilder<'infcx, 'tcx> { + infcx: &'infcx BorrowckInferCtxt<'tcx>, mir_def: LocalDefId, param_env: ty::ParamEnv<'tcx>, } @@ -687,7 +689,8 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { defining_ty: DefiningTy<'tcx>, ) -> ty::Binder<'tcx, &'tcx ty::List>> { let tcx = self.infcx.tcx; - match defining_ty { + + let inputs_and_output = match defining_ty { DefiningTy::Closure(def_id, args) => { assert_eq!(self.mir_def.to_def_id(), def_id); let closure_sig = args.as_closure().sig(); @@ -797,6 +800,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { // "output" (the type of the constant). assert_eq!(self.mir_def.to_def_id(), def_id); let ty = tcx.type_of(self.mir_def).instantiate_identity(); + let ty = indices.fold_to_region_vids(tcx, ty); ty::Binder::dummy(tcx.mk_type_list(&[ty])) } @@ -806,7 +810,14 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let ty = args.as_inline_const().ty(); ty::Binder::dummy(tcx.mk_type_list(&[ty])) } + }; + + // FIXME(#129952): We probably want a more principled approach here. + if let Err(terr) = inputs_and_output.skip_binder().error_reported() { + self.infcx.set_tainted_by_errors(terr); } + + inputs_and_output } } diff --git a/compiler/rustc_borrowck/src/used_muts.rs b/compiler/rustc_borrowck/src/used_muts.rs index e2de6b8b4a929..bde07c05c0e16 100644 --- a/compiler/rustc_borrowck/src/used_muts.rs +++ b/compiler/rustc_borrowck/src/used_muts.rs @@ -3,10 +3,11 @@ use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::{ Local, Location, Place, Statement, StatementKind, Terminator, TerminatorKind, }; +use tracing::debug; use crate::MirBorrowckCtxt; -impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { +impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { /// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes /// of the `unused_mut` lint. /// @@ -45,13 +46,13 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { /// MIR visitor for collecting used mutable variables. /// The 'visit lifetime represents the duration of the MIR walk. -struct GatherUsedMutsVisitor<'visit, 'a, 'mir, 'infcx, 'tcx> { +struct GatherUsedMutsVisitor<'a, 'b, 'infcx, 'tcx> { temporary_used_locals: FxIndexSet, - never_initialized_mut_locals: &'visit mut FxIndexSet, - mbcx: &'visit mut MirBorrowckCtxt<'a, 'mir, 'infcx, 'tcx>, + never_initialized_mut_locals: &'a mut FxIndexSet, + mbcx: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>, } -impl GatherUsedMutsVisitor<'_, '_, '_, '_, '_> { +impl GatherUsedMutsVisitor<'_, '_, '_, '_> { fn remove_never_initialized_mut_locals(&mut self, into: Place<'_>) { // Remove any locals that we found were initialized from the // `never_initialized_mut_locals` set. At the end, the only remaining locals will @@ -63,7 +64,7 @@ impl GatherUsedMutsVisitor<'_, '_, '_, '_, '_> { } } -impl<'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'_, '_, '_, '_, 'tcx> { +impl<'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'_, '_, '_, 'tcx> { fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { debug!("visit_terminator: terminator={:?}", terminator); match &terminator.kind { diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index ae2627d693867..e313016e3d863 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -812,6 +812,44 @@ pub(super) fn expand_asm<'cx>( }) } +pub(super) fn expand_naked_asm<'cx>( + ecx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> MacroExpanderResult<'cx> { + ExpandResult::Ready(match parse_args(ecx, sp, tts, false) { + Ok(args) => { + let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, args) else { + return ExpandResult::Retry(()); + }; + let expr = match mac { + Ok(mut inline_asm) => { + // for future compatibility, we always set the NORETURN option. + // + // When we turn `asm!` into `naked_asm!` with this implementation, we can drop + // the `options(noreturn)`, which makes the upgrade smooth when `naked_asm!` + // starts disallowing the `noreturn` option in the future + inline_asm.options |= ast::InlineAsmOptions::NORETURN; + + P(ast::Expr { + id: ast::DUMMY_NODE_ID, + kind: ast::ExprKind::InlineAsm(P(inline_asm)), + span: sp, + attrs: ast::AttrVec::new(), + tokens: None, + }) + } + Err(guar) => DummyResult::raw_expr(sp, Some(guar)), + }; + MacEager::expr(expr) + } + Err(err) => { + let guar = err.emit(); + DummyResult::any(sp, guar) + } + }) +} + pub(super) fn expand_global_asm<'cx>( ecx: &'cx mut ExtCtxt<'_>, sp: Span, diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 30e1c8d262216..ebe5e2b544292 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -94,6 +94,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { line: source_util::expand_line, log_syntax: log_syntax::expand_log_syntax, module_path: source_util::expand_mod, + naked_asm: asm::expand_naked_asm, option_env: env::expand_option_env, pattern_type: pattern_type::expand, std_panic: edition_panic::expand_panic, diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index a9e4434581163..a694d3b8c2857 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -326,6 +326,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { let main_attr = ecx.attr_word(sym::rustc_main, sp); // #[coverage(off)] let coverage_attr = ecx.attr_nested_word(sym::coverage, sym::off, sp); + // #[allow(missing_docs)] + let missing_docs_attr = ecx.attr_nested_word(sym::allow, sym::missing_docs, sp); // pub fn main() { ... } let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(ThinVec::new())); @@ -355,7 +357,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { let main = P(ast::Item { ident: main_id, - attrs: thin_vec![main_attr, coverage_attr], + attrs: thin_vec![main_attr, coverage_attr, missing_docs_attr], id: ast::DUMMY_NODE_ID, kind: main, vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None }, diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index 6766e2f844d6d..18a840f8a50e0 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -70,7 +70,7 @@ For more docs on how to build and test see [build_system/usage.txt](build_system |AIX|❌[^xcoff]|N/A|N/A|❌[^xcoff]| |Other unixes|❓|❓|❓|❓| |macOS|✅|✅|N/A|N/A| -|Windows|✅[^no-rustup]|❌|N/A|N/A| +|Windows|✅|❌|N/A|N/A| ✅: Fully supported and tested ❓: Maybe supported, not tested diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index f1d885bf1bce8..4af4b39cc5b9e 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -44,8 +44,9 @@ pub(crate) fn codegen_fn<'tcx>( let _mir_guard = crate::PrintOnPanic(|| { let mut buf = Vec::new(); with_no_trimmed_paths!({ - rustc_middle::mir::pretty::write_mir_fn(tcx, mir, &mut |_, _| Ok(()), &mut buf) - .unwrap(); + use rustc_middle::mir::pretty; + let options = pretty::PrettyPrintMirOptions::from_cli(tcx); + pretty::write_mir_fn(tcx, mir, &mut |_, _| Ok(()), &mut buf, options).unwrap(); }); String::from_utf8_lossy(&buf).into_owned() }); diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index 915229f7e7eef..0eb264f99eb08 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -11,63 +11,12 @@ dependencies = [ "memchr", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" - [[package]] name = "boml" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85fdb93f04c73bff54305fa437ffea5449c41edcaadfe882f35836206b166ac5" -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "fastrand" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" - [[package]] name = "fm" version = "0.2.2" @@ -132,12 +81,6 @@ version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" -[[package]] -name = "linux-raw-sys" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" - [[package]] name = "memchr" version = "2.5.0" @@ -154,24 +97,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.30.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" -dependencies = [ - "memchr", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "regex" version = "1.8.4" @@ -196,22 +121,6 @@ dependencies = [ "boml", "gccjit", "lang_tester", - "object", - "smallvec", - "tempfile", -] - -[[package]] -name = "rustix" -version = "0.38.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" -dependencies = [ - "bitflags 2.4.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", ] [[package]] @@ -223,25 +132,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "tempfile" -version = "3.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys", -] - [[package]] name = "termcolor" version = "1.2.0" @@ -315,69 +205,3 @@ 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-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml index 5caca63f63480..44fb5eef04a57 100644 --- a/compiler/rustc_codegen_gcc/Cargo.toml +++ b/compiler/rustc_codegen_gcc/Cargo.toml @@ -23,21 +23,11 @@ default = ["master"] [dependencies] gccjit = "2.1" - # Local copy. #gccjit = { path = "../gccjit.rs" } -object = { version = "0.30.1", default-features = false, features = [ - "std", - "read", -] } -smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } -# TODO(antoyo): make tempfile optional. -tempfile = "3.7.1" - [dev-dependencies] lang_tester = "0.8.0" -tempfile = "3.1.0" boml = "0.3.1" [profile.dev] diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index 6b2dbbbed6771..c2adab7137f62 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -272,7 +272,6 @@ fn fat_lto( }*/ } }; - let mut serialized_bitcode = Vec::new(); { info!("using {:?} as a base module", module.name); @@ -317,7 +316,6 @@ fn fat_lto( unimplemented!("from uncompressed file") } } - serialized_bitcode.push(bc_decoded); } save_temp_bitcode(cgcx, &module, "lto.input"); @@ -337,7 +335,7 @@ fn fat_lto( // of now. module.module_llvm.temp_dir = Some(tmp_path); - Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: serialized_bitcode }) + Ok(LtoModuleCodegen::Fat(module)) } pub struct ModuleBuffer(PathBuf); diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 94f016234f921..4de671ac4a062 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -24,6 +24,14 @@ #![deny(clippy::pattern_type_mismatch)] #![allow(clippy::needless_lifetimes)] +// Some "regular" crates we want to share with rustc +extern crate object; +extern crate smallvec; +extern crate tempfile; +#[macro_use] +extern crate tracing; + +// The rustc crates we need extern crate rustc_apfloat; extern crate rustc_ast; extern crate rustc_attr; @@ -42,8 +50,6 @@ extern crate rustc_middle; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; -#[macro_use] -extern crate tracing; // This prevents duplicating functions and statics that are already part of the host rustc process. #[allow(unused_extern_crates)] diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 92a857c2adcf4..9d4497d73a8ae 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -411,26 +411,31 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( // the string "false". Now it is disabled by absence of the attribute. to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false")); } - } else if llvm_util::get_version() >= (19, 0, 0) { - // For non-naked functions, set branch protection attributes on aarch64. - if let Some(BranchProtection { bti, pac_ret }) = - cx.sess().opts.unstable_opts.branch_protection - { - assert!(cx.sess().target.arch == "aarch64"); - if bti { - to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement")); - } - if let Some(PacRet { leaf, key }) = pac_ret { - to_add.push(llvm::CreateAttrStringValue( - cx.llcx, - "sign-return-address", - if leaf { "all" } else { "non-leaf" }, - )); - to_add.push(llvm::CreateAttrStringValue( - cx.llcx, - "sign-return-address-key", - if key == PAuthKey::A { "a_key" } else { "b_key" }, - )); + } else { + // Do not set sanitizer attributes for naked functions. + to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); + + if llvm_util::get_version() >= (19, 0, 0) { + // For non-naked functions, set branch protection attributes on aarch64. + if let Some(BranchProtection { bti, pac_ret }) = + cx.sess().opts.unstable_opts.branch_protection + { + assert!(cx.sess().target.arch == "aarch64"); + if bti { + to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement")); + } + if let Some(PacRet { leaf, key }) = pac_ret { + to_add.push(llvm::CreateAttrStringValue( + cx.llcx, + "sign-return-address", + if leaf { "all" } else { "non-leaf" }, + )); + to_add.push(llvm::CreateAttrStringValue( + cx.llcx, + "sign-return-address-key", + if key == PAuthKey::A { "a_key" } else { "b_key" }, + )); + } } } } @@ -485,7 +490,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( if let Some(backchain) = backchain_attr(cx) { to_add.push(backchain); } - to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); to_add.extend(patchable_function_entry_attrs(cx, codegen_fn_attrs.patchable_function_entry)); // Always annotate functions with the target-cpu they are compiled for. diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index aa6842c75cec7..09896b89ebf42 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -314,7 +314,6 @@ fn fat_lto( } } }; - let mut serialized_bitcode = Vec::new(); { let (llcx, llmod) = { let llvm = &module.module_llvm; @@ -342,9 +341,7 @@ fn fat_lto( serialized_modules.sort_by(|module1, module2| module1.1.cmp(&module2.1)); // For all serialized bitcode files we parse them and link them in as we did - // above, this is all mostly handled in C++. Like above, though, we don't - // know much about the memory management here so we err on the side of being - // save and persist everything with the original module. + // above, this is all mostly handled in C++. let mut linker = Linker::new(llmod); for (bc_decoded, name) in serialized_modules { let _timer = cgcx @@ -355,7 +352,6 @@ fn fat_lto( info!("linking {:?}", name); let data = bc_decoded.data(); linker.add(data).map_err(|()| write::llvm_err(dcx, LlvmError::LoadBitcode { name }))?; - serialized_bitcode.push(bc_decoded); } drop(linker); save_temp_bitcode(cgcx, &module, "lto.input"); @@ -372,7 +368,7 @@ fn fat_lto( } } - Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: serialized_bitcode }) + Ok(LtoModuleCodegen::Fat(module)) } pub(crate) struct Linker<'a>(&'a mut llvm::Linker<'a>); diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index b143e28c5f9cc..d60122fccee21 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -390,7 +390,7 @@ impl<'ll> CodegenCx<'ll, '_> { let val_llty = self.val_ty(v); let g = self.get_static_inner(def_id, val_llty); - let llty = self.val_ty(g); + let llty = llvm::LLVMGlobalGetValueType(g); let g = if val_llty == llty { g diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 069b62af5e748..b5acfabfde25e 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -422,7 +422,7 @@ fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> { (instance.def_id(), body) }); - // Functions whose coverage statments were found inlined into other functions. + // Functions whose coverage statements were found inlined into other functions. let mut used_via_inlining = FxHashSet::default(); // Functions that were instrumented, but had all of their coverage statements // removed by later MIR transforms (e.g. UnreachablePropagation). diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index dc59e9349b057..d231b103964b6 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -216,6 +216,8 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( // need to make sure that we don't break existing debuginfo consumers // by doing that (at least not without a warning period). let layout_type = if ptr_type.is_box() { + // The assertion at the start of this function ensures we have a ZST allocator. + // We'll make debuginfo "skip" all ZST allocators, not just the default allocator. Ty::new_mut_ptr(cx.tcx, pointee_type) } else { ptr_type @@ -454,7 +456,7 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> if def.is_box() && args.get(1).map_or(true, |arg| cx.layout_of(arg.expect_ty()).is_1zst()) => { - build_pointer_or_reference_di_node(cx, t, t.boxed_ty(), unique_type_id) + build_pointer_or_reference_di_node(cx, t, t.expect_boxed_ty(), unique_type_id) } ty::FnDef(..) | ty::FnPtr(..) => build_subroutine_type_di_node(cx, unique_type_id), ty::Closure(..) => build_closure_env_di_node(cx, unique_type_id), diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index a300f5f707a88..05fb77a193af3 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -187,9 +187,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { Some(instance), ) } - sym::likely => { - self.call_intrinsic("llvm.expect.i1", &[args[0].immediate(), self.const_bool(true)]) - } + sym::likely => self.expect(args[0].immediate(), true), sym::is_val_statically_known => { let intrinsic_type = args[0].layout.immediate_llvm_type(self.cx); let kind = self.type_kind(intrinsic_type); @@ -210,8 +208,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.const_bool(false) } } - sym::unlikely => self - .call_intrinsic("llvm.expect.i1", &[args[0].immediate(), self.const_bool(false)]), + sym::unlikely => self.expect(args[0].immediate(), false), sym::select_unpredictable => { let cond = args[0].immediate(); assert_eq!(args[1].layout, args[2].layout); @@ -604,11 +601,17 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { } fn assume(&mut self, val: Self::Value) { - self.call_intrinsic("llvm.assume", &[val]); + if self.cx.sess().opts.optimize != rustc_session::config::OptLevel::No { + self.call_intrinsic("llvm.assume", &[val]); + } } fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value { - self.call_intrinsic("llvm.expect.i1", &[cond, self.const_bool(expected)]) + if self.cx.sess().opts.optimize != rustc_session::config::OptLevel::No { + self.call_intrinsic("llvm.expect.i1", &[cond, self.const_bool(expected)]) + } else { + cond + } } fn type_test(&mut self, pointer: Self::Value, typeid: Self::Value) -> Self::Value { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 6a303e1e6024b..7f26bbd7f8757 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -6,7 +6,6 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![cfg_attr(bootstrap, feature(unsafe_extern_blocks))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 138cc3219aa08..3bf4d4964082f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -974,6 +974,7 @@ unsafe extern "C" { pub fn LLVMGetAlignment(Global: &Value) -> c_uint; pub fn LLVMSetAlignment(Global: &Value, Bytes: c_uint); pub fn LLVMSetDLLStorageClass(V: &Value, C: DLLStorageClass); + pub fn LLVMGlobalGetValueType(Global: &Value) -> &Type; // Operations on global variables pub fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>; diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 618602ed70f44..d55220ba5c3a4 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -353,9 +353,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { None } }) - .filter(|feature| { - RUSTC_SPECIAL_FEATURES.contains(feature) || features.contains(&Symbol::intern(feature)) - }) + .filter(|feature| features.contains(&Symbol::intern(feature))) .map(|feature| Symbol::intern(feature)) .collect() } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index e78039bafd8dd..3ab4cd0a0f53c 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -41,7 +41,7 @@ tempfile = "3.2" thin-vec = "0.2.12" thorin-dwp = "0.7" tracing = "0.1" -wasm-encoder = "0.215.0" +wasm-encoder = "0.216.0" # tidy-alphabetical-end [target.'cfg(unix)'.dependencies] diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index 8b6f6b5a220a4..1e1e039882be0 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -41,18 +41,14 @@ pub struct ThinShared { } pub enum LtoModuleCodegen { - Fat { - module: ModuleCodegen, - _serialized_bitcode: Vec>, - }, - + Fat(ModuleCodegen), Thin(ThinModule), } impl LtoModuleCodegen { pub fn name(&self) -> &str { match *self { - LtoModuleCodegen::Fat { .. } => "everything", + LtoModuleCodegen::Fat(_) => "everything", LtoModuleCodegen::Thin(ref m) => m.name(), } } @@ -68,7 +64,7 @@ impl LtoModuleCodegen { cgcx: &CodegenContext, ) -> Result, FatalError> { match self { - LtoModuleCodegen::Fat { mut module, .. } => { + LtoModuleCodegen::Fat(mut module) => { B::optimize_fat(cgcx, &mut module)?; Ok(module) } @@ -81,7 +77,7 @@ impl LtoModuleCodegen { pub fn cost(&self) -> u64 { match *self { // Only one module with fat LTO, so the cost doesn't matter. - LtoModuleCodegen::Fat { .. } => 0, + LtoModuleCodegen::Fat(_) => 0, LtoModuleCodegen::Thin(ref m) => m.cost(), } } diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 9b5a797ad5106..0fd9d7fffe8f9 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -372,27 +372,42 @@ pub(crate) fn create_object_file(sess: &Session) -> Option object::write::MachOBuildVersion { /// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz" /// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200 - fn pack_version((major, minor): (u32, u32)) -> u32 { - (major << 16) | (minor << 8) + fn pack_version((major, minor, patch): (u16, u8, u8)) -> u32 { + let (major, minor, patch) = (major as u32, minor as u32, patch as u32); + (major << 16) | (minor << 8) | patch } let platform = rustc_target::spec::current_apple_platform(target).expect("unknown Apple target OS"); - let min_os = rustc_target::spec::current_apple_deployment_target(target) - .expect("unknown Apple target OS"); - let sdk = + let min_os = rustc_target::spec::current_apple_deployment_target(target); + let (sdk_major, sdk_minor) = rustc_target::spec::current_apple_sdk_version(platform).expect("unknown Apple target OS"); let mut build_version = object::write::MachOBuildVersion::default(); build_version.platform = platform; build_version.minos = pack_version(min_os); - build_version.sdk = pack_version(sdk); + build_version.sdk = pack_version((sdk_major, sdk_minor, 0)); build_version } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 209750d6ba612..4ab20c154ccd0 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,6 +1,5 @@ use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem}; use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; -use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; use rustc_errors::{struct_span_code_err, DiagMessage, SubdiagMessage}; use rustc_hir as hir; @@ -9,7 +8,7 @@ use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; use rustc_hir::{lang_items, LangItem}; use rustc_middle::middle::codegen_fn_attrs::{ - CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, TargetFeature, + CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, }; use rustc_middle::mir::mono::Linkage; use rustc_middle::query::Providers; @@ -18,7 +17,6 @@ use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::symbol::Ident; use rustc_span::{sym, Span}; -use rustc_target::abi::VariantIdx; use rustc_target::spec::{abi, SanitizerSet}; use crate::errors; @@ -80,13 +78,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let mut link_ordinal_span = None; let mut no_sanitize_span = None; - let fn_sig_outer = || { - use DefKind::*; - - let def_kind = tcx.def_kind(did); - if let Fn | AssocFn | Variant | Ctor(..) = def_kind { Some(tcx.fn_sig(did)) } else { None } - }; - for attr in attrs.iter() { // In some cases, attribute are only valid on functions, but it's the `check_attr` // pass that check that they aren't used anywhere else, rather this module. @@ -94,12 +85,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also // report a delayed bug, just in case `check_attr` isn't doing its job. let fn_sig = || { - let sig = fn_sig_outer(); - if sig.is_none() { + use DefKind::*; + + let def_kind = tcx.def_kind(did); + if let Fn | AssocFn | Variant | Ctor(..) = def_kind { + Some(tcx.fn_sig(did)) + } else { tcx.dcx() .span_delayed_bug(attr.span, "this attribute can only be applied to functions"); + None } - sig }; let Some(Ident { name, .. }) = attr.ident() else { @@ -618,93 +613,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } - if let Some(sig) = fn_sig_outer() { - // Collect target features from types reachable from arguments. - // We define a type as "reachable" if: - // - it is a function argument - // - it is a field of a reachable struct - // - there is a reachable reference to it - // FIXME(struct_target_features): we may want to cache the result of this computation. - let mut visited_types = FxHashSet::default(); - let mut reachable_types: Vec<_> = sig.skip_binder().inputs().skip_binder().to_owned(); - let mut additional_tf = vec![]; - - while let Some(ty) = reachable_types.pop() { - if visited_types.contains(&ty) { - continue; - } - visited_types.insert(ty); - match ty.kind() { - ty::Alias(..) => { - if let Ok(t) = - tcx.try_normalize_erasing_regions(tcx.param_env(did.to_def_id()), ty) - { - reachable_types.push(t) - } - } - - ty::Ref(_, inner, _) => reachable_types.push(*inner), - ty::Tuple(tys) => reachable_types.extend(tys.iter()), - ty::Adt(adt_def, args) => { - additional_tf.extend_from_slice(tcx.struct_target_features(adt_def.did())); - // This only recurses into structs as i.e. an Option is an ADT - // that doesn't actually always contain a TargetFeature. - if adt_def.is_struct() { - reachable_types.extend( - adt_def - .variant(VariantIdx::from_usize(0)) - .fields - .iter() - .map(|field| field.ty(tcx, args)), - ); - } - } - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Foreign(..) - | ty::Str - | ty::Array(..) - | ty::Pat(..) - | ty::Slice(..) - | ty::RawPtr(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Dynamic(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Never - | ty::Param(..) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Infer(..) - | ty::Error(..) => (), - } - } - - // FIXME(struct_target_features): is this really necessary? - if !additional_tf.is_empty() && sig.skip_binder().abi() != abi::Abi::Rust { - tcx.dcx().span_err( - tcx.hir().span(tcx.local_def_id_to_hir_id(did)), - "cannot use a struct with target features in a function with non-Rust ABI", - ); - } - if !additional_tf.is_empty() && codegen_fn_attrs.inline == InlineAttr::Always { - tcx.dcx().span_err( - tcx.hir().span(tcx.local_def_id_to_hir_id(did)), - "cannot use a struct with target features in a #[inline(always)] function", - ); - } - codegen_fn_attrs - .target_features - .extend(additional_tf.iter().map(|tf| TargetFeature { implied: true, ..*tf })); - } - - // If a function uses non-default target_features it can't be inlined into general + // If a function uses #[target_feature] it can't be inlined into general // purpose functions as they wouldn't have the right target features // enabled. For that reason we also forbid #[inline(always)] as it can't be // respected. @@ -849,20 +758,6 @@ fn check_link_name_xor_ordinal( } } -fn struct_target_features(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[TargetFeature] { - let mut features = vec![]; - let supported_features = tcx.supported_target_features(LOCAL_CRATE); - for attr in tcx.get_attrs(def_id, sym::target_feature) { - from_target_feature(tcx, attr, supported_features, &mut features); - } - tcx.arena.alloc_slice(&features) -} - pub fn provide(providers: &mut Providers) { - *providers = Providers { - codegen_fn_attrs, - should_inherit_track_caller, - struct_target_features, - ..*providers - }; + *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers }; } diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index f0bc4354f9a75..5103b2f31582b 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -701,7 +701,7 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S match ty.kind() { ty::Int(ity) => { // FIXME: directly extract the bits from a valtree instead of evaluating an - // alreay evaluated `Const` in order to get the bits. + // already evaluated `Const` in order to get the bits. let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all()); let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; write!(output, "{val}") diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index d94c6f8ddcec3..91fd9905f63cc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -382,7 +382,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { scalar: abi::Scalar, backend_ty: Bx::Type, ) { - if matches!(self.cx.sess().opts.optimize, OptLevel::No | OptLevel::Less) + if matches!(self.cx.sess().opts.optimize, OptLevel::No) // For now, the critical niches are all over `Int`eger values. // Should floating-point values or pointers ever get more complex // niches, then this code will probably want to handle them too. diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 2ef860fc336d6..73283cafb4947 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -1,6 +1,5 @@ use rustc_middle::mir::{self, NonDivergingIntrinsic}; use rustc_middle::span_bug; -use rustc_session::config::OptLevel; use tracing::instrument; use super::{FunctionCx, LocalRef}; @@ -68,10 +67,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.codegen_coverage(bx, kind, statement.source_info.scope); } mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(ref op)) => { - if !matches!(bx.tcx().sess.opts.optimize, OptLevel::No | OptLevel::Less) { - let op_val = self.codegen_operand(bx, op); - bx.assume(op_val.immediate()); - } + let op_val = self.codegen_operand(bx, op); + bx.assume(op_val.immediate()); } mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping( mir::CopyNonOverlapping { ref count, ref src, ref dst }, diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index db788b6b15199..8a6dfb892490a 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -415,8 +415,8 @@ const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn const_eval_unstable_in_stable = const-stable function cannot use `#[feature({$gate})]` - .unstable_sugg = if it is not part of the public API, make this function unstably const - .bypass_sugg = otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (but requires team approval) + .unstable_sugg = if the function is not (yet) meant to be stable, make this function unstably const + .bypass_sugg = otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (but requires team approval) const_eval_unterminated_c_string = reading a null-terminated string starting at {$pointer} with no null found before end of allocation diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 6a086a3a7e5a0..8be327a8b5647 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -46,7 +46,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { /// Returns `true` if `local` is `NeedsDrop` at the given `Location`. /// /// Only updates the cursor if absolutely necessary - pub fn needs_drop( + fn needs_drop( &mut self, ccx: &'mir ConstCx<'mir, 'tcx>, local: Local, @@ -76,7 +76,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { /// Returns `true` if `local` is `NeedsNonConstDrop` at the given `Location`. /// /// Only updates the cursor if absolutely necessary - pub fn needs_non_const_drop( + pub(crate) fn needs_non_const_drop( &mut self, ccx: &'mir ConstCx<'mir, 'tcx>, local: Local, @@ -106,7 +106,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { /// Returns `true` if `local` is `HasMutInterior` at the given `Location`. /// /// Only updates the cursor if absolutely necessary. - pub fn has_mut_interior( + fn has_mut_interior( &mut self, ccx: &'mir ConstCx<'mir, 'tcx>, local: Local, @@ -868,9 +868,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Calling an unstable function *always* requires that the corresponding gate // (or implied gate) be enabled, even if the function has // `#[rustc_allow_const_fn_unstable(the_gate)]`. - let gate_declared = |gate| { - tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == gate) - }; + let gate_declared = |gate| tcx.features().declared(gate); let feature_gate_declared = gate_declared(gate); let implied_gate_declared = implied_by.is_some_and(gate_declared); if !feature_gate_declared && !implied_gate_declared { diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 93fafa6055773..e8f10c88408ba 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -57,7 +57,7 @@ pub trait NonConstOp<'tcx>: std::fmt::Debug { /// A function call where the callee is a pointer. #[derive(Debug)] -pub struct FnCallIndirect; +pub(crate) struct FnCallIndirect; impl<'tcx> NonConstOp<'tcx> for FnCallIndirect { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { ccx.dcx().create_err(errors::UnallowedFnPointerCall { span, kind: ccx.const_kind() }) @@ -66,7 +66,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect { /// A function call where the callee is not marked as `const`. #[derive(Debug, Clone, Copy)] -pub struct FnCallNonConst<'tcx> { +pub(crate) struct FnCallNonConst<'tcx> { pub caller: LocalDefId, pub callee: DefId, pub args: GenericArgsRef<'tcx>, @@ -299,7 +299,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { /// /// Contains the name of the feature that would allow the use of this function. #[derive(Debug)] -pub struct FnCallUnstable(pub DefId, pub Option); +pub(crate) struct FnCallUnstable(pub DefId, pub Option); impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { @@ -324,7 +324,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { } #[derive(Debug)] -pub struct Coroutine(pub hir::CoroutineKind); +pub(crate) struct Coroutine(pub hir::CoroutineKind); impl<'tcx> NonConstOp<'tcx> for Coroutine { fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { if let hir::CoroutineKind::Desugared( @@ -356,7 +356,7 @@ impl<'tcx> NonConstOp<'tcx> for Coroutine { } #[derive(Debug)] -pub struct HeapAllocation; +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 { @@ -368,7 +368,7 @@ impl<'tcx> NonConstOp<'tcx> for HeapAllocation { } #[derive(Debug)] -pub struct InlineAsm; +pub(crate) struct InlineAsm; impl<'tcx> NonConstOp<'tcx> for InlineAsm { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { ccx.dcx().create_err(errors::UnallowedInlineAsm { span, kind: ccx.const_kind() }) @@ -376,7 +376,7 @@ impl<'tcx> NonConstOp<'tcx> for InlineAsm { } #[derive(Debug)] -pub struct LiveDrop<'tcx> { +pub(crate) struct LiveDrop<'tcx> { pub dropped_at: Option, pub dropped_ty: Ty<'tcx>, } @@ -394,7 +394,7 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> { #[derive(Debug)] /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to /// the final value of the constant. -pub struct TransientCellBorrow; +pub(crate) struct TransientCellBorrow; impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow { fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_refs_to_cell) @@ -410,7 +410,7 @@ impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow { /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to /// the final value of the constant, and thus we cannot allow this (for now). We may allow /// it in the future for static items. -pub struct CellBorrow; +pub(crate) struct CellBorrow; impl<'tcx> NonConstOp<'tcx> for CellBorrow { fn importance(&self) -> DiagImportance { // Most likely the code will try to do mutation with these borrows, which @@ -431,7 +431,7 @@ impl<'tcx> NonConstOp<'tcx> for CellBorrow { /// This op is for `&mut` borrows in the trailing expression of a constant /// which uses the "enclosing scopes rule" to leak its locals into anonymous /// static or const items. -pub struct MutBorrow(pub hir::BorrowKind); +pub(crate) struct MutBorrow(pub hir::BorrowKind); impl<'tcx> NonConstOp<'tcx> for MutBorrow { fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { @@ -461,7 +461,7 @@ impl<'tcx> NonConstOp<'tcx> for MutBorrow { } #[derive(Debug)] -pub struct TransientMutBorrow(pub hir::BorrowKind); +pub(crate) struct TransientMutBorrow(pub hir::BorrowKind); impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow { fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { @@ -484,7 +484,7 @@ impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow { } #[derive(Debug)] -pub struct MutDeref; +pub(crate) struct MutDeref; impl<'tcx> NonConstOp<'tcx> for MutDeref { fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_mut_refs) @@ -505,7 +505,7 @@ impl<'tcx> NonConstOp<'tcx> for MutDeref { /// A call to a `panic()` lang item where the first argument is _not_ a `&str`. #[derive(Debug)] -pub struct PanicNonStr; +pub(crate) struct PanicNonStr; impl<'tcx> NonConstOp<'tcx> for PanicNonStr { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { ccx.dcx().create_err(errors::PanicNonStrErr { span }) @@ -516,7 +516,7 @@ impl<'tcx> NonConstOp<'tcx> for PanicNonStr { /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on /// allocation base addresses that are not known at compile-time. #[derive(Debug)] -pub struct RawPtrComparison; +pub(crate) struct RawPtrComparison; impl<'tcx> NonConstOp<'tcx> for RawPtrComparison { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { // FIXME(const_trait_impl): revert to span_bug? @@ -525,7 +525,7 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrComparison { } #[derive(Debug)] -pub struct RawMutPtrDeref; +pub(crate) struct RawMutPtrDeref; impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref { fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { Status::Unstable(sym::const_mut_refs) @@ -546,7 +546,7 @@ impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref { /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on /// allocation base addresses that are not known at compile-time. #[derive(Debug)] -pub struct RawPtrToIntCast; +pub(crate) struct RawPtrToIntCast; impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { ccx.dcx().create_err(errors::RawPtrToIntErr { span }) @@ -555,7 +555,7 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast { /// An access to a (non-thread-local) `static`. #[derive(Debug)] -pub struct StaticAccess; +pub(crate) struct StaticAccess; impl<'tcx> NonConstOp<'tcx> for StaticAccess { fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if let hir::ConstContext::Static(_) = ccx.const_kind() { @@ -582,7 +582,7 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess { /// An access to a thread-local `static`. #[derive(Debug)] -pub struct ThreadLocalAccess; +pub(crate) struct ThreadLocalAccess; impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { ccx.dcx().create_err(errors::ThreadLocalAccessErr { span }) @@ -590,11 +590,11 @@ impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess { } /// Types that cannot appear in the signature or locals of a `const fn`. -pub mod mut_ref { +pub(crate) mod mut_ref { use super::*; #[derive(Debug)] - pub struct MutRef(pub mir::LocalKind); + pub(crate) struct MutRef(pub mir::LocalKind); impl<'tcx> NonConstOp<'tcx> for MutRef { fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_mut_refs) 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 96b3ec6f18728..7ccebd83f24f7 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -94,7 +94,7 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( let intern_result = intern_const_alloc_recursive(ecx, intern_kind, &ret); // Since evaluation had no errors, validate the resulting constant. - const_validate_mplace(&ecx, &ret, cid)?; + const_validate_mplace(ecx, &ret, cid)?; // Only report this after validation, as validaiton produces much better diagnostics. // FIXME: ensure validation always reports this and stop making interning care about it. @@ -391,7 +391,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( #[inline(always)] fn const_validate_mplace<'tcx>( - ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>, + ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>, mplace: &MPlaceTy<'tcx>, cid: GlobalId<'tcx>, ) -> Result<(), ErrorHandled> { diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 9c1fef095f552..7405ca09342da 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -1,16 +1,16 @@ -use std::borrow::Borrow; +use std::borrow::{Borrow, Cow}; use std::fmt; use std::hash::Hash; use std::ops::ControlFlow; use rustc_ast::Mutability; -use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, LangItem, CRATE_HIR_ID}; use rustc_middle::mir::AssertMessage; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; @@ -24,8 +24,8 @@ use crate::fluent_generated as fluent; use crate::interpret::{ self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, throw_unsup, throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, - GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar, - StackPopCleanup, + GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, + RangeSet, Scalar, StackPopCleanup, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -65,6 +65,9 @@ pub struct CompileTimeMachine<'tcx> { /// storing the result in the given `AllocId`. /// Used to prevent reads from a static's base allocation, as that may allow for self-initialization loops. pub(crate) static_root_ids: Option<(AllocId, LocalDefId)>, + + /// A cache of "data range" computations for unions (i.e., the offsets of non-padding bytes). + union_data_ranges: FxHashMap, RangeSet>, } #[derive(Copy, Clone)] @@ -99,6 +102,7 @@ impl<'tcx> CompileTimeMachine<'tcx> { can_access_mut_global, check_alignment, static_root_ids: None, + union_data_ranges: FxHashMap::default(), } } } @@ -766,6 +770,19 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } Ok(()) } + + fn cached_union_data_range<'e>( + ecx: &'e mut InterpCx<'tcx, Self>, + ty: Ty<'tcx>, + compute_range: impl FnOnce() -> RangeSet, + ) -> Cow<'e, RangeSet> { + if ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks { + Cow::Borrowed(ecx.machine.union_data_ranges.entry(ty).or_insert_with(compute_range)) + } else { + // Don't bother caching, we're only doing one validation at the end anyway. + Cow::Owned(compute_range()) + } + } } // Please do not add any code below the above `Machine` trait impl. I (oli-obk) plan more cleanups diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 61e8007e10ecf..568a9a3a637be 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -20,7 +20,7 @@ use super::{ }; use crate::fluent_generated as fluent; -/// An argment passed to a function. +/// An argument passed to a function. #[derive(Clone, Debug)] pub enum FnArg<'tcx, Prov: Provenance = CtfeProvenance> { /// Pass a copy of the given operand. @@ -123,7 +123,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed) }; let inner = self.unfold_transparent(inner, /* may_unfold */ |def| { - // Stop at NPO tpyes so that we don't miss that attribute in the check below! + // Stop at NPO types so that we don't miss that attribute in the check below! def.is_struct() && !is_npo(def) }); Ok(match inner.ty.kind() { @@ -189,7 +189,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ty::Ref(_, ty, _) => *ty, ty::RawPtr(ty, _) => *ty, // We only accept `Box` with the default allocator. - _ if ty.is_box_global(*self.tcx) => ty.boxed_ty(), + _ if ty.is_box_global(*self.tcx) => ty.expect_boxed_ty(), _ => return Ok(None), })) }; diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 0008a15722bde..de93ed85704b5 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -7,7 +7,7 @@ use rustc_target::abi::{self, TagEncoding, VariantIdx, Variants}; use tracing::{instrument, trace}; use super::{ - err_ub, throw_ub, ImmTy, InterpCx, InterpResult, Machine, Readable, Scalar, Writeable, + err_ub, throw_ub, ImmTy, InterpCx, InterpResult, Machine, Projectable, Scalar, Writeable, }; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { @@ -60,7 +60,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { #[instrument(skip(self), level = "trace")] pub fn read_discriminant( &self, - op: &impl Readable<'tcx, M::Provenance>, + op: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, VariantIdx> { let ty = op.layout().ty; trace!("read_discriminant_value {:#?}", op.layout()); diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 7a6bbdfdcb5e5..dd744c51f23be 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -574,7 +574,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // be computed as the type references non-existing names. // See . } else { - // Looks like the const is not captued by `required_consts`, that's bad. + // Looks like the const is not captured by `required_consts`, that's bad. span_bug!(span, "interpret const eval failure of {val:?} which is not in required_consts"); } } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 88453245b8444..8cab3c34eedfb 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -10,6 +10,7 @@ use rustc_apfloat::{Float, FloatConvert}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::Ty; use rustc_middle::{mir, ty}; use rustc_span::def_id::DefId; use rustc_span::Span; @@ -19,7 +20,7 @@ use rustc_target::spec::abi::Abi as CallAbi; use super::{ throw_unsup, throw_unsup_format, AllocBytes, AllocId, AllocKind, AllocRange, Allocation, ConstAllocation, CtfeProvenance, FnArg, Frame, ImmTy, InterpCx, InterpResult, MPlaceTy, - MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance, CTFE_ALLOC_SALT, + MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, CTFE_ALLOC_SALT, }; /// Data returned by [`Machine::after_stack_pop`], and consumed by @@ -362,17 +363,7 @@ pub trait Machine<'tcx>: Sized { ecx: &InterpCx<'tcx, Self>, id: AllocId, alloc: &'b Allocation, - ) -> InterpResult<'tcx, Cow<'b, Allocation>> - { - // The default implementation does a copy; CTFE machines have a more efficient implementation - // based on their particular choice for `Provenance`, `AllocExtra`, and `Bytes`. - let kind = Self::GLOBAL_KIND - .expect("if GLOBAL_KIND is None, adjust_global_allocation must be overwritten"); - let alloc = alloc.adjust_from_tcx(&ecx.tcx, |ptr| ecx.global_root_pointer(ptr))?; - let extra = - Self::init_alloc_extra(ecx, id, MemoryKind::Machine(kind), alloc.size(), alloc.align)?; - Ok(Cow::Owned(alloc.with_extra(extra))) - } + ) -> InterpResult<'tcx, Cow<'b, Allocation>>; /// Initialize the extra state of an allocation. /// @@ -588,6 +579,15 @@ pub trait Machine<'tcx>: Sized { ecx: &InterpCx<'tcx, Self>, instance: Option>, ) -> usize; + + fn cached_union_data_range<'e>( + _ecx: &'e mut InterpCx<'tcx, Self>, + _ty: Ty<'tcx>, + compute_range: impl FnOnce() -> RangeSet, + ) -> Cow<'e, RangeSet> { + // Default to no caching. + Cow::Owned(compute_range()) + } } /// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 97326fe99a21a..d87588496c0bd 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -8,9 +8,8 @@ use std::assert_matches::assert_matches; use std::borrow::Cow; -use std::cell::Cell; use std::collections::VecDeque; -use std::{fmt, ptr}; +use std::{fmt, mem, ptr}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; @@ -118,7 +117,7 @@ pub struct Memory<'tcx, M: Machine<'tcx>> { /// This stores whether we are currently doing reads purely for the purpose of validation. /// Those reads do not trigger the machine's hooks for memory reads. /// Needless to say, this must only be set with great care! - validation_in_progress: Cell, + validation_in_progress: bool, } /// A reference to some allocation that was already bounds-checked for the given region @@ -145,7 +144,7 @@ impl<'tcx, M: Machine<'tcx>> Memory<'tcx, M> { alloc_map: M::MemoryMap::default(), extra_fn_ptr_map: FxIndexMap::default(), dead_alloc_map: FxIndexMap::default(), - validation_in_progress: Cell::new(false), + validation_in_progress: false, } } @@ -682,7 +681,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized // accesses. That means we cannot rely on the closure above or the `Some` branch below. We // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked. - if !self.memory.validation_in_progress.get() { + if !self.memory.validation_in_progress { if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr, size_i64) { M::before_alloc_read(self, alloc_id)?; } @@ -690,7 +689,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc { let range = alloc_range(offset, size); - if !self.memory.validation_in_progress.get() { + if !self.memory.validation_in_progress { M::before_memory_read( self.tcx, &self.machine, @@ -766,11 +765,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let parts = self.get_ptr_access(ptr, size)?; if let Some((alloc_id, offset, prov)) = parts { let tcx = self.tcx; + let validation_in_progress = self.memory.validation_in_progress; // FIXME: can we somehow avoid looking up the allocation twice here? // We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`. let (alloc, machine) = self.get_alloc_raw_mut(alloc_id)?; let range = alloc_range(offset, size); - M::before_memory_write(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?; + if !validation_in_progress { + M::before_memory_write(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?; + } Ok(Some(AllocRefMut { alloc, range, tcx: *tcx, alloc_id })) } else { Ok(None) @@ -827,7 +829,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let (size, align) = if nested { // Nested anonymous statics are untyped, so let's get their - // size and alignment from the allocaiton itself. This always + // size and alignment from the allocation itself. This always // succeeds, as the query is fed at DefId creation time, so no // evaluation actually occurs. let alloc = self.tcx.eval_static_initializer(def_id).unwrap(); @@ -1014,16 +1016,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// /// We do this so Miri's allocation access tracking does not show the validation /// reads as spurious accesses. - pub fn run_for_validation(&self, f: impl FnOnce() -> R) -> R { + pub fn run_for_validation(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { // This deliberately uses `==` on `bool` to follow the pattern // `assert!(val.replace(new) == old)`. assert!( - self.memory.validation_in_progress.replace(true) == false, + mem::replace(&mut self.memory.validation_in_progress, true) == false, "`validation_in_progress` was already set" ); - let res = f(); + let res = f(self); assert!( - self.memory.validation_in_progress.replace(false) == true, + mem::replace(&mut self.memory.validation_in_progress, false) == true, "`validation_in_progress` was unset by someone else" ); res @@ -1115,6 +1117,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> { impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRefMut<'a, 'tcx, Prov, Extra, Bytes> { + pub fn as_ref<'b>(&'b self) -> AllocRef<'b, 'tcx, Prov, Extra, Bytes> { + AllocRef { alloc: self.alloc, range: self.range, tcx: self.tcx, alloc_id: self.alloc_id } + } + /// `range` is relative to this allocation reference, not the base of the allocation. pub fn write_scalar(&mut self, range: AllocRange, val: Scalar) -> InterpResult<'tcx> { let range = self.range.subrange(range); @@ -1130,13 +1136,30 @@ impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes> self.write_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size), val) } + /// Mark the given sub-range (relative to this allocation reference) as uninitialized. + pub fn write_uninit(&mut self, range: AllocRange) -> InterpResult<'tcx> { + let range = self.range.subrange(range); + Ok(self + .alloc + .write_uninit(&self.tcx, range) + .map_err(|e| e.to_interp_error(self.alloc_id))?) + } + /// Mark the entire referenced range as uninitialized - pub fn write_uninit(&mut self) -> InterpResult<'tcx> { + pub fn write_uninit_full(&mut self) -> InterpResult<'tcx> { Ok(self .alloc .write_uninit(&self.tcx, self.range) .map_err(|e| e.to_interp_error(self.alloc_id))?) } + + /// Remove all provenance in the reference range. + pub fn clear_provenance(&mut self) -> InterpResult<'tcx> { + Ok(self + .alloc + .clear_provenance(&self.tcx, self.range) + .map_err(|e| e.to_interp_error(self.alloc_id))?) + } } impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Prov, Extra, Bytes> { @@ -1278,7 +1301,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; let src_alloc = self.get_alloc_raw(src_alloc_id)?; let src_range = alloc_range(src_offset, size); - assert!(!self.memory.validation_in_progress.get(), "we can't be copying during validation"); + assert!(!self.memory.validation_in_progress, "we can't be copying during validation"); M::before_memory_read( tcx, &self.machine, diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 511756e3f86c9..561d681f804a1 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -33,11 +33,11 @@ pub(crate) use self::intrinsics::eval_nullary_intrinsic; pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, ReturnAction}; pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind}; use self::operand::Operand; -pub use self::operand::{ImmTy, Immediate, OpTy, Readable}; +pub use self::operand::{ImmTy, Immediate, OpTy}; 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, StackPopCleanup, StackPopInfo}; pub(crate) use self::util::create_static_alloc; -pub use self::validity::{CtfeValidationMode, RefTracking}; +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 9a8ccaa7cc5ca..b906e3422dba5 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -111,6 +111,46 @@ impl Immediate { Immediate::Uninit => bug!("Got uninit where a scalar or scalar pair was expected"), } } + + /// Assert that this immediate is a valid value for the given ABI. + pub fn assert_matches_abi(self, abi: Abi, cx: &impl HasDataLayout) { + match (self, abi) { + (Immediate::Scalar(scalar), Abi::Scalar(s)) => { + assert_eq!(scalar.size(), s.size(cx)); + if !matches!(s.primitive(), abi::Pointer(..)) { + assert!(matches!(scalar, Scalar::Int(..))); + } + } + (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { + assert_eq!(a_val.size(), a.size(cx)); + if !matches!(a.primitive(), abi::Pointer(..)) { + assert!(matches!(a_val, Scalar::Int(..))); + } + assert_eq!(b_val.size(), b.size(cx)); + if !matches!(b.primitive(), abi::Pointer(..)) { + assert!(matches!(b_val, Scalar::Int(..))); + } + } + (Immediate::Uninit, _) => {} + _ => { + bug!("value {self:?} does not match ABI {abi:?})",) + } + } + } + + pub fn clear_provenance<'tcx>(&mut self) -> InterpResult<'tcx> { + match self { + Immediate::Scalar(s) => { + s.clear_provenance()?; + } + Immediate::ScalarPair(a, b) => { + a.clear_provenance()?; + b.clear_provenance()?; + } + Immediate::Uninit => {} + } + Ok(()) + } } // ScalarPair needs a type to interpret, so we often have an immediate and a type together @@ -490,32 +530,6 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> { } } -/// The `Readable` trait describes interpreter values that one can read from. -pub trait Readable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> { - fn as_mplace_or_imm(&self) -> Either, ImmTy<'tcx, Prov>>; -} - -impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for OpTy<'tcx, Prov> { - #[inline(always)] - fn as_mplace_or_imm(&self) -> Either, ImmTy<'tcx, Prov>> { - self.as_mplace_or_imm() - } -} - -impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { - #[inline(always)] - fn as_mplace_or_imm(&self) -> Either, ImmTy<'tcx, Prov>> { - Left(self.clone()) - } -} - -impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for ImmTy<'tcx, Prov> { - #[inline(always)] - fn as_mplace_or_imm(&self) -> Either, ImmTy<'tcx, Prov>> { - Right(self.clone()) - } -} - impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`. /// Returns `None` if the layout does not permit loading this as a value. @@ -588,9 +602,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// ConstProp needs it, though. pub fn read_immediate_raw( &self, - src: &impl Readable<'tcx, M::Provenance>, + src: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Either, ImmTy<'tcx, M::Provenance>>> { - Ok(match src.as_mplace_or_imm() { + Ok(match src.to_op(self)?.as_mplace_or_imm() { Left(ref mplace) => { if let Some(val) = self.read_immediate_from_mplace_raw(mplace)? { Right(val) @@ -608,7 +622,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { #[inline(always)] pub fn read_immediate( &self, - op: &impl Readable<'tcx, M::Provenance>, + op: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { if !matches!( op.layout().abi, @@ -627,7 +641,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Read a scalar from a place pub fn read_scalar( &self, - op: &impl Readable<'tcx, M::Provenance>, + op: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Scalar> { Ok(self.read_immediate(op)?.to_scalar()) } @@ -638,21 +652,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Read a pointer from a place. pub fn read_pointer( &self, - op: &impl Readable<'tcx, M::Provenance>, + op: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Pointer>> { self.read_scalar(op)?.to_pointer(self) } /// Read a pointer-sized unsigned integer from a place. pub fn read_target_usize( &self, - op: &impl Readable<'tcx, M::Provenance>, + op: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, u64> { self.read_scalar(op)?.to_target_usize(self) } /// Read a pointer-sized signed integer from a place. pub fn read_target_isize( &self, - op: &impl Readable<'tcx, M::Provenance>, + op: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, i64> { self.read_scalar(op)?.to_target_isize(self) } @@ -717,7 +731,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { match place.as_mplace_or_local() { Left(mplace) => Ok(mplace.into()), - Right((local, offset, locals_addr)) => { + Right((local, offset, locals_addr, _)) => { debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`. debug_assert_eq!(locals_addr, self.frame().locals_addr()); let base = self.local_to_op(local, None)?; diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 2afdd02c88035..3b14142da02ed 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -15,7 +15,7 @@ use tracing::{instrument, trace}; use super::{ alloc_range, mir_assign_valid_types, AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, - Operand, Pointer, Projectable, Provenance, Readable, Scalar, + Operand, Pointer, Projectable, Provenance, Scalar, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -61,13 +61,13 @@ pub(super) struct MemPlace { impl MemPlace { /// Adjust the provenance of the main pointer (metadata is unaffected). - pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self { + fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self { MemPlace { ptr: self.ptr.map_provenance(|p| p.map(f)), ..self } } /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space. #[inline] - pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate { + fn to_ref(self, cx: &impl HasDataLayout) -> Immediate { Immediate::new_pointer_with_meta(self.ptr, self.meta, cx) } @@ -180,13 +180,14 @@ pub(super) enum Place { Ptr(MemPlace), /// To support alloc-free locals, we are able to write directly to a local. The offset indicates - /// where in the local this place is located; if it is `None`, no projection has been applied. + /// where in the local this place is located; if it is `None`, no projection has been applied + /// and the type of the place is exactly the type of the local. /// Such projections are meaningful even if the offset is 0, since they can change layouts. /// (Without that optimization, we'd just always be a `MemPlace`.) /// `Local` places always refer to the current stack frame, so they are unstable under /// function calls/returns and switching betweens stacks of different threads! /// We carry around the address of the `locals` buffer of the correct stack frame as a sanity - /// chec to be able to catch some cases of using a dangling `Place`. + /// check to be able to catch some cases of using a dangling `Place`. /// /// This variant shall not be used for unsized types -- those must always live in memory. Local { local: mir::Local, offset: Option, locals_addr: usize }, @@ -231,10 +232,12 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { #[inline(always)] pub fn as_mplace_or_local( &self, - ) -> Either, (mir::Local, Option, usize)> { + ) -> Either, (mir::Local, Option, usize, TyAndLayout<'tcx>)> { match self.place { Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout }), - Place::Local { local, offset, locals_addr } => Right((local, offset, locals_addr)), + Place::Local { local, offset, locals_addr } => { + Right((local, offset, locals_addr, self.layout)) + } } } @@ -277,7 +280,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> { ) -> InterpResult<'tcx, Self> { Ok(match self.as_mplace_or_local() { Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(), - Right((local, old_offset, locals_addr)) => { + Right((local, old_offset, locals_addr, _)) => { debug_assert!(layout.is_sized(), "unsized locals should live in memory"); assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway... // `Place::Local` are always in-bounds of their surrounding local, so we can just @@ -328,9 +331,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { /// The `Weiteable` trait describes interpreter values that can be written to. pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> { - fn as_mplace_or_local( - &self, - ) -> Either, (mir::Local, Option, usize, TyAndLayout<'tcx>)>; + fn to_place(&self) -> PlaceTy<'tcx, Prov>; fn force_mplace>( &self, @@ -340,11 +341,8 @@ pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> { impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> { #[inline(always)] - fn as_mplace_or_local( - &self, - ) -> Either, (mir::Local, Option, usize, TyAndLayout<'tcx>)> { - self.as_mplace_or_local() - .map_right(|(local, offset, locals_addr)| (local, offset, locals_addr, self.layout)) + fn to_place(&self) -> PlaceTy<'tcx, Prov> { + self.clone() } #[inline(always)] @@ -358,10 +356,8 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> { impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { #[inline(always)] - fn as_mplace_or_local( - &self, - ) -> Either, (mir::Local, Option, usize, TyAndLayout<'tcx>)> { - Left(self.clone()) + fn to_place(&self) -> PlaceTy<'tcx, Prov> { + self.clone().into() } #[inline(always)] @@ -436,16 +432,18 @@ where #[instrument(skip(self), level = "trace")] pub fn deref_pointer( &self, - src: &impl Readable<'tcx, M::Provenance>, + src: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + if src.layout().ty.is_box() { + // Derefer should have removed all Box derefs. + // Some `Box` are not immediates (if they have a custom allocator) + // so the code below would fail. + bug!("dereferencing {}", src.layout().ty); + } + let val = self.read_immediate(src)?; trace!("deref to {} on {:?}", val.layout.ty, *val); - if val.layout.ty.is_box() { - // Derefer should have removed all Box derefs - bug!("dereferencing {}", val.layout.ty); - } - let mplace = self.ref_to_mplace(&val)?; Ok(mplace) } @@ -560,6 +558,40 @@ where Ok(place) } + /// Given a place, returns either the underlying mplace or a reference to where the value of + /// this place is stored. + fn as_mplace_or_mutable_local( + &mut self, + place: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult< + 'tcx, + Either, (&mut Immediate, TyAndLayout<'tcx>)>, + > { + Ok(match place.to_place().as_mplace_or_local() { + Left(mplace) => Left(mplace), + Right((local, offset, locals_addr, layout)) => { + if offset.is_some() { + // This has been projected to a part of this local, or had the type changed. + // FIMXE: there are cases where we could still avoid allocating an mplace. + Left(place.force_mplace(self)?) + } else { + debug_assert_eq!(locals_addr, self.frame().locals_addr()); + debug_assert_eq!(self.layout_of_local(self.frame(), local, None)?, layout); + match self.frame_mut().locals[local].access_mut()? { + Operand::Indirect(mplace) => { + // The local is in memory. + Left(MPlaceTy { mplace: *mplace, layout }) + } + Operand::Immediate(local_val) => { + // The local still has the optimized representation. + Right((local_val, layout)) + } + } + } + } + }) + } + /// Write an immediate to a place #[inline(always)] #[instrument(skip(self), level = "trace")] @@ -572,9 +604,11 @@ where if M::enforce_validity(self, dest.layout()) { // Data got changed, better make sure it matches the type! + // Also needed to reset padding. self.validate_operand( - &dest.to_op(self)?, + &dest.to_place(), M::enforce_validity_recursively(self, dest.layout()), + /*reset_provenance_and_padding*/ true, )?; } @@ -604,67 +638,27 @@ where /// Write an immediate to a place. /// If you use this you are responsible for validating that things got copied at the /// right type. - fn write_immediate_no_validate( + pub(super) fn write_immediate_no_validate( &mut self, src: Immediate, dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { assert!(dest.layout().is_sized(), "Cannot write unsized immediate data"); - // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`, - // but not factored as a separate function. - let mplace = match dest.as_mplace_or_local() { - Right((local, offset, locals_addr, layout)) => { - if offset.is_some() { - // This has been projected to a part of this local. We could have complicated - // logic to still keep this local as an `Operand`... but it's much easier to - // just fall back to the indirect path. - dest.force_mplace(self)? - } else { - debug_assert_eq!(locals_addr, self.frame().locals_addr()); - match self.frame_mut().locals[local].access_mut()? { - Operand::Immediate(local_val) => { - // Local can be updated in-place. - *local_val = src; - // Double-check that the value we are storing and the local fit to each other. - // (*After* doing the update for borrow checker reasons.) - if cfg!(debug_assertions) { - let local_layout = - self.layout_of_local(&self.frame(), local, None)?; - match (src, local_layout.abi) { - (Immediate::Scalar(scalar), Abi::Scalar(s)) => { - assert_eq!(scalar.size(), s.size(self)) - } - ( - Immediate::ScalarPair(a_val, b_val), - Abi::ScalarPair(a, b), - ) => { - assert_eq!(a_val.size(), a.size(self)); - assert_eq!(b_val.size(), b.size(self)); - } - (Immediate::Uninit, _) => {} - (src, abi) => { - bug!( - "value {src:?} cannot be written into local with type {} (ABI {abi:?})", - local_layout.ty - ) - } - }; - } - return Ok(()); - } - Operand::Indirect(mplace) => { - // The local is in memory, go on below. - MPlaceTy { mplace: *mplace, layout } - } - } + match self.as_mplace_or_mutable_local(&dest.to_place())? { + Right((local_val, local_layout)) => { + // Local can be updated in-place. + *local_val = src; + // Double-check that the value we are storing and the local fit to each other. + if cfg!(debug_assertions) { + src.assert_matches_abi(local_layout.abi, self); } } - Left(mplace) => mplace, // already referring to memory - }; - - // This is already in memory, write there. - self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.mplace) + Left(mplace) => { + self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.mplace)?; + } + } + Ok(()) } /// Write an immediate to memory. @@ -676,6 +670,9 @@ where layout: TyAndLayout<'tcx>, dest: MemPlace, ) -> InterpResult<'tcx> { + if cfg!(debug_assertions) { + value.assert_matches_abi(layout.abi, self); + } // Note that it is really important that the type here is the right one, and matches the // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here // to handle padding properly, which is only correct if we never look at this data with the @@ -689,15 +686,7 @@ where match value { Immediate::Scalar(scalar) => { - let Abi::Scalar(s) = layout.abi else { - span_bug!( - self.cur_span(), - "write_immediate_to_mplace: invalid Scalar layout: {layout:#?}", - ) - }; - let size = s.size(&tcx); - assert_eq!(size, layout.size, "abi::Scalar size does not match layout size"); - alloc.write_scalar(alloc_range(Size::ZERO, size), scalar) + alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar) } Immediate::ScalarPair(a_val, b_val) => { let Abi::ScalarPair(a, b) = layout.abi else { @@ -707,18 +696,19 @@ where layout ) }; - let (a_size, b_size) = (a.size(&tcx), b.size(&tcx)); - let b_offset = a_size.align_to(b.align(&tcx).abi); + let b_offset = a.size(&tcx).align_to(b.align(&tcx).abi); assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields // It is tempting to verify `b_offset` against `layout.fields.offset(1)`, // but that does not work: We could be a newtype around a pair, then the // fields do not match the `ScalarPair` components. - alloc.write_scalar(alloc_range(Size::ZERO, a_size), a_val)?; - alloc.write_scalar(alloc_range(b_offset, b_size), b_val) + alloc.write_scalar(alloc_range(Size::ZERO, a_val.size()), a_val)?; + alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?; + // We don't have to reset padding here, `write_immediate` will anyway do a validation run. + Ok(()) } - Immediate::Uninit => alloc.write_uninit(), + Immediate::Uninit => alloc.write_uninit_full(), } } @@ -726,35 +716,38 @@ where &mut self, dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - let mplace = match dest.as_mplace_or_local() { - Left(mplace) => mplace, - Right((local, offset, locals_addr, layout)) => { - if offset.is_some() { - // This has been projected to a part of this local. We could have complicated - // logic to still keep this local as an `Operand`... but it's much easier to - // just fall back to the indirect path. - // FIXME: share the logic with `write_immediate_no_validate`. - dest.force_mplace(self)? - } else { - debug_assert_eq!(locals_addr, self.frame().locals_addr()); - match self.frame_mut().locals[local].access_mut()? { - Operand::Immediate(local) => { - *local = Immediate::Uninit; - return Ok(()); - } - Operand::Indirect(mplace) => { - // The local is in memory, go on below. - MPlaceTy { mplace: *mplace, layout } - } - } - } + match self.as_mplace_or_mutable_local(&dest.to_place())? { + Right((local_val, _local_layout)) => { + *local_val = Immediate::Uninit; } - }; - let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else { - // Zero-sized access - return Ok(()); - }; - alloc.write_uninit()?; + Left(mplace) => { + let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else { + // Zero-sized access + return Ok(()); + }; + alloc.write_uninit_full()?; + } + } + Ok(()) + } + + /// Remove all provenance in the given place. + pub fn clear_provenance( + &mut self, + dest: &impl Writeable<'tcx, M::Provenance>, + ) -> InterpResult<'tcx> { + match self.as_mplace_or_mutable_local(&dest.to_place())? { + Right((local_val, _local_layout)) => { + local_val.clear_provenance()?; + } + Left(mplace) => { + let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else { + // Zero-sized access + return Ok(()); + }; + alloc.clear_provenance()?; + } + } Ok(()) } @@ -766,7 +759,7 @@ where #[inline(always)] pub(super) fn copy_op_no_dest_validation( &mut self, - src: &impl Readable<'tcx, M::Provenance>, + src: &impl Projectable<'tcx, M::Provenance>, dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.copy_op_inner( @@ -779,7 +772,7 @@ where #[inline(always)] pub fn copy_op_allow_transmute( &mut self, - src: &impl Readable<'tcx, M::Provenance>, + src: &impl Projectable<'tcx, M::Provenance>, dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.copy_op_inner( @@ -792,7 +785,7 @@ where #[inline(always)] pub fn copy_op( &mut self, - src: &impl Readable<'tcx, M::Provenance>, + src: &impl Projectable<'tcx, M::Provenance>, dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.copy_op_inner( @@ -806,28 +799,35 @@ where #[instrument(skip(self), level = "trace")] fn copy_op_inner( &mut self, - src: &impl Readable<'tcx, M::Provenance>, + src: &impl Projectable<'tcx, M::Provenance>, dest: &impl Writeable<'tcx, M::Provenance>, allow_transmute: bool, validate_dest: bool, ) -> InterpResult<'tcx> { - // Generally for transmutation, data must be valid both at the old and new type. - // But if the types are the same, the 2nd validation below suffices. - if src.layout().ty != dest.layout().ty && M::enforce_validity(self, src.layout()) { - self.validate_operand( - &src.to_op(self)?, - M::enforce_validity_recursively(self, src.layout()), - )?; - } + // These are technically *two* typed copies: `src` is a not-yet-loaded value, + // so we're going a typed copy at `src` type from there to some intermediate storage. + // And then we're doing a second typed copy from that intermediate storage to `dest`. + // But as an optimization, we only make a single direct copy here. // Do the actual copy. self.copy_op_no_validate(src, dest, allow_transmute)?; if validate_dest && M::enforce_validity(self, dest.layout()) { - // Data got changed, better make sure it matches the type! + let dest = dest.to_place(); + // Given that there were two typed copies, we have to ensure this is valid at both types, + // and we have to ensure this loses provenance and padding according to both types. + // But if the types are identical, we only do one pass. + if allow_transmute && src.layout().ty != dest.layout().ty { + self.validate_operand( + &dest.transmute(src.layout(), self)?, + M::enforce_validity_recursively(self, src.layout()), + /*reset_provenance_and_padding*/ true, + )?; + } self.validate_operand( - &dest.to_op(self)?, + &dest, M::enforce_validity_recursively(self, dest.layout()), + /*reset_provenance_and_padding*/ true, )?; } @@ -841,7 +841,7 @@ where #[instrument(skip(self), level = "trace")] fn copy_op_no_validate( &mut self, - src: &impl Readable<'tcx, M::Provenance>, + src: &impl Projectable<'tcx, M::Provenance>, dest: &impl Writeable<'tcx, M::Provenance>, allow_transmute: bool, ) -> InterpResult<'tcx> { diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 0f6bf5c03364c..b6e83715e3989 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -1,5 +1,5 @@ //! Manages the low-level pushing and popping of stack frames and the (de)allocation of local variables. -//! For hadling of argument passing and return values, see the `call` module. +//! For handling of argument passing and return values, see the `call` module. use std::cell::Cell; use std::{fmt, mem}; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 26b7251f6dbc5..fb24f983ca9c3 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -4,6 +4,7 @@ //! That's useful because it means other passes (e.g. promotion) can rely on `const`s //! to be const-safe. +use std::borrow::Cow; use std::fmt::Write; use std::hash::Hash; use std::num::NonZero; @@ -16,22 +17,22 @@ use rustc_hir as hir; use rustc_middle::bug; use rustc_middle::mir::interpret::ValidationErrorKind::{self, *}; use rustc_middle::mir::interpret::{ - ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance, + alloc_range, ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance, UnsupportedOpInfo, ValidationErrorInfo, }; -use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::{ - Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange, + Abi, FieldIdx, FieldsShape, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange, }; use tracing::trace; use super::machine::AllocMap; use super::{ err_ub, format_interp_error, throw_ub, AllocId, AllocKind, CheckInAllocMsg, GlobalAlloc, ImmTy, - Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Pointer, Projectable, - Scalar, ValueVisitor, + Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, PlaceTy, Pointer, + Projectable, Scalar, ValueVisitor, }; // for the validation errors @@ -125,6 +126,7 @@ pub enum PathElem { EnumTag, CoroutineTag, DynDowncast, + Vtable, } /// Extra things to check for during validation of CTFE results. @@ -163,22 +165,22 @@ impl RefTracking pub fn empty() -> Self { RefTracking { seen: FxHashSet::default(), todo: vec![] } } - pub fn new(op: T) -> Self { + pub fn new(val: T) -> Self { let mut ref_tracking_for_consts = - RefTracking { seen: FxHashSet::default(), todo: vec![(op.clone(), PATH::default())] }; - ref_tracking_for_consts.seen.insert(op); + RefTracking { seen: FxHashSet::default(), todo: vec![(val.clone(), PATH::default())] }; + ref_tracking_for_consts.seen.insert(val); ref_tracking_for_consts } pub fn next(&mut self) -> Option<(T, PATH)> { self.todo.pop() } - fn track(&mut self, op: T, path: impl FnOnce() -> PATH) { - if self.seen.insert(op.clone()) { - trace!("Recursing below ptr {:#?}", op); + fn track(&mut self, val: T, path: impl FnOnce() -> PATH) { + if self.seen.insert(val.clone()) { + trace!("Recursing below ptr {:#?}", val); let path = path(); // Remember to come back to this later. - self.todo.push((op, path)); + self.todo.push((val, path)); } } } @@ -204,11 +206,62 @@ fn write_path(out: &mut String, path: &[PathElem]) { // not the root. Deref => write!(out, "."), DynDowncast => write!(out, "."), + Vtable => write!(out, "."), } .unwrap() } } +/// Represents a set of `Size` values as a sorted list of ranges. +// These are (offset, length) pairs, and they are sorted and mutually disjoint, +// and never adjacent (i.e. there's always a gap between two of them). +#[derive(Debug, Clone)] +pub struct RangeSet(Vec<(Size, Size)>); + +impl RangeSet { + fn add_range(&mut self, offset: Size, size: Size) { + if size.bytes() == 0 { + // No need to track empty ranges. + return; + } + let v = &mut self.0; + // We scan for a partition point where the left partition is all the elements that end + // strictly before we start. Those are elements that are too "low" to merge with us. + let idx = + v.partition_point(|&(other_offset, other_size)| other_offset + other_size < offset); + // Now we want to either merge with the first element of the second partition, or insert ourselves before that. + if let Some(&(other_offset, other_size)) = v.get(idx) + && offset + size >= other_offset + { + // Their end is >= our start (otherwise it would not be in the 2nd partition) and + // our end is >= their start. This means we can merge the ranges. + let new_start = other_offset.min(offset); + let mut new_end = (other_offset + other_size).max(offset + size); + // We grew to the right, so merge with overlapping/adjacent elements. + // (We also may have grown to the left, but that can never make us adjacent with + // anything there since we selected the first such candidate via `partition_point`.) + let mut scan_right = 1; + while let Some(&(next_offset, next_size)) = v.get(idx + scan_right) + && new_end >= next_offset + { + // Increase our size to absorb the next element. + new_end = new_end.max(next_offset + next_size); + // Look at the next element. + scan_right += 1; + } + // Update the element we grew. + v[idx] = (new_start, new_end - new_start); + // Remove the elements we absorbed (if any). + if scan_right > 1 { + drop(v.drain((idx + 1)..(idx + scan_right))); + } + } else { + // Insert new element. + v.insert(idx, (offset, size)); + } + } +} + struct ValidityVisitor<'rt, 'tcx, M: Machine<'tcx>> { /// The `path` may be pushed to, but the part that is present when a function /// starts must not be changed! `visit_fields` and `visit_array` rely on @@ -217,7 +270,17 @@ struct ValidityVisitor<'rt, 'tcx, M: Machine<'tcx>> { ref_tracking: Option<&'rt mut RefTracking, Vec>>, /// `None` indicates this is not validating for CTFE (but for runtime). ctfe_mode: Option, - ecx: &'rt InterpCx<'tcx, M>, + ecx: &'rt mut InterpCx<'tcx, M>, + /// Whether provenance should be reset outside of pointers (emulating the effect of a typed + /// copy). + reset_provenance_and_padding: bool, + /// This tracks which byte ranges in this value contain data; the remaining bytes are padding. + /// The ideal representation here would be pointer-length pairs, but to keep things more compact + /// we only store a (range) set of offsets -- the base pointer is the same throughout the entire + /// visit, after all. + /// If this is `Some`, then `reset_provenance_and_padding` must be true (but not vice versa: + /// we might not track data vs padding bytes if the operand isn't stored in memory anyway). + data_bytes: Option, } impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { @@ -287,8 +350,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // arrays/slices ty::Array(..) | ty::Slice(..) => PathElem::ArrayElem(field), + // dyn* vtables + ty::Dynamic(_, _, ty::DynKind::DynStar) if field == 1 => PathElem::Vtable, + // dyn traits - ty::Dynamic(..) => PathElem::DynDowncast, + ty::Dynamic(..) => { + assert_eq!(field, 0); + PathElem::DynDowncast + } // nothing else has an aggregate layout _ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty), @@ -314,11 +383,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { fn read_immediate( &self, - op: &OpTy<'tcx, M::Provenance>, + val: &PlaceTy<'tcx, M::Provenance>, expected: ExpectedKind, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { Ok(try_validation!( - self.ecx.read_immediate(op), + self.ecx.read_immediate(val), self.path, Ub(InvalidUninitBytes(None)) => Uninit { expected }, @@ -332,10 +401,40 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { fn read_scalar( &self, - op: &OpTy<'tcx, M::Provenance>, + val: &PlaceTy<'tcx, M::Provenance>, expected: ExpectedKind, ) -> InterpResult<'tcx, Scalar> { - Ok(self.read_immediate(op, expected)?.to_scalar()) + Ok(self.read_immediate(val, expected)?.to_scalar()) + } + + fn deref_pointer( + &mut self, + val: &PlaceTy<'tcx, M::Provenance>, + expected: ExpectedKind, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + // Not using `ecx.deref_pointer` since we want to use our `read_immediate` wrapper. + let imm = self.read_immediate(val, expected)?; + // Reset provenance: ensure slice tail metadata does not preserve provenance, + // and ensure all pointers do not preserve partial provenance. + if self.reset_provenance_and_padding { + if matches!(imm.layout.abi, Abi::Scalar(..)) { + // A thin pointer. If it has provenance, we don't have to do anything. + // If it does not, ensure we clear the provenance in memory. + if matches!(imm.to_scalar(), Scalar::Int(..)) { + self.ecx.clear_provenance(val)?; + } + } else { + // A wide pointer. This means we have to worry both about the pointer itself and the + // metadata. We do the lazy thing and just write back the value we got. Just + // clearing provenance in a targeted manner would be more efficient, but unless this + // is a perf hotspot it's just not worth the effort. + self.ecx.write_immediate_no_validate(*imm, val)?; + } + // The entire thing is data, not padding. + self.add_data_range_place(val); + } + // Now turn it into a place. + self.ecx.ref_to_mplace(&imm) } fn check_wide_ptr_meta( @@ -376,11 +475,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { /// Check a reference or `Box`. fn check_safe_pointer( &mut self, - value: &OpTy<'tcx, M::Provenance>, + value: &PlaceTy<'tcx, M::Provenance>, ptr_kind: PointerKind, ) -> InterpResult<'tcx> { - // Not using `deref_pointer` since we want to use our `read_immediate` wrapper. - let place = self.ecx.ref_to_mplace(&self.read_immediate(value, ptr_kind.into())?)?; + let place = self.deref_pointer(value, ptr_kind.into())?; // Handle wide pointers. // Check metadata early, for better diagnostics if place.layout.is_unsized() { @@ -564,31 +662,39 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { /// Note that not all of these have `FieldsShape::Primitive`, e.g. wide references. fn try_visit_primitive( &mut self, - value: &OpTy<'tcx, M::Provenance>, + value: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, bool> { // Go over all the primitive types let ty = value.layout.ty; match ty.kind() { ty::Bool => { - let value = self.read_scalar(value, ExpectedKind::Bool)?; + let scalar = self.read_scalar(value, ExpectedKind::Bool)?; try_validation!( - value.to_bool(), + scalar.to_bool(), self.path, Ub(InvalidBool(..)) => ValidationErrorKind::InvalidBool { - value: format!("{value:x}"), + value: format!("{scalar:x}"), } ); + if self.reset_provenance_and_padding { + self.ecx.clear_provenance(value)?; + self.add_data_range_place(value); + } Ok(true) } ty::Char => { - let value = self.read_scalar(value, ExpectedKind::Char)?; + let scalar = self.read_scalar(value, ExpectedKind::Char)?; try_validation!( - value.to_char(), + scalar.to_char(), self.path, Ub(InvalidChar(..)) => ValidationErrorKind::InvalidChar { - value: format!("{value:x}"), + value: format!("{scalar:x}"), } ); + if self.reset_provenance_and_padding { + self.ecx.clear_provenance(value)?; + self.add_data_range_place(value); + } Ok(true) } ty::Float(_) | ty::Int(_) | ty::Uint(_) => { @@ -602,11 +708,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { ExpectedKind::Int }, )?; + if self.reset_provenance_and_padding { + self.ecx.clear_provenance(value)?; + self.add_data_range_place(value); + } Ok(true) } ty::RawPtr(..) => { - let place = - self.ecx.ref_to_mplace(&self.read_immediate(value, ExpectedKind::RawPtr)?)?; + let place = self.deref_pointer(value, ExpectedKind::RawPtr)?; if place.layout.is_unsized() { self.check_wide_ptr_meta(place.meta(), place.layout)?; } @@ -617,11 +726,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { Ok(true) } ty::FnPtr(..) => { - let value = self.read_scalar(value, ExpectedKind::FnPtr)?; + let scalar = self.read_scalar(value, ExpectedKind::FnPtr)?; // If we check references recursively, also check that this points to a function. if let Some(_) = self.ref_tracking { - let ptr = value.to_pointer(self.ecx)?; + let ptr = scalar.to_pointer(self.ecx)?; let _fn = try_validation!( self.ecx.get_ptr_fn(ptr), self.path, @@ -631,10 +740,18 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // FIXME: Check if the signature matches } else { // Otherwise (for standalone Miri), we have to still check it to be non-null. - if self.ecx.scalar_may_be_null(value)? { + if self.ecx.scalar_may_be_null(scalar)? { throw_validation_failure!(self.path, NullFnPtr); } } + if self.reset_provenance_and_padding { + // Make sure we do not preserve partial provenance. This matches the thin + // pointer handling in `deref_pointer`. + if matches!(scalar, Scalar::Int(..)) { + self.ecx.clear_provenance(value)?; + } + self.add_data_range_place(value); + } Ok(true) } ty::Never => throw_validation_failure!(self.path, NeverVal), @@ -716,13 +833,178 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } } - fn in_mutable_memory(&self, op: &OpTy<'tcx, M::Provenance>) -> bool { - if let Some(mplace) = op.as_mplace_or_imm().left() { + fn in_mutable_memory(&self, val: &PlaceTy<'tcx, M::Provenance>) -> bool { + if let Some(mplace) = val.as_mplace_or_local().left() { if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) { - return mutability(self.ecx, alloc_id).is_mut(); + mutability(self.ecx, alloc_id).is_mut() + } else { + // No memory at all. + false + } + } else { + // A local variable -- definitely mutable. + true + } + } + + /// Add the given pointer-length pair to the "data" range of this visit. + fn add_data_range(&mut self, ptr: Pointer>, size: Size) { + if let Some(data_bytes) = self.data_bytes.as_mut() { + // We only have to store the offset, the rest is the same for all pointers here. + let (_prov, offset) = ptr.into_parts(); + // Add this. + data_bytes.add_range(offset, size); + }; + } + + /// Add the entire given place to the "data" range of this visit. + fn add_data_range_place(&mut self, place: &PlaceTy<'tcx, M::Provenance>) { + // Only sized places can be added this way. + debug_assert!(place.layout.abi.is_sized()); + if let Some(data_bytes) = self.data_bytes.as_mut() { + let offset = Self::data_range_offset(self.ecx, place); + data_bytes.add_range(offset, place.layout.size); + } + } + + /// Convert a place into the offset it starts at, for the purpose of data_range tracking. + /// Must only be called if `data_bytes` is `Some(_)`. + fn data_range_offset(ecx: &InterpCx<'tcx, M>, place: &PlaceTy<'tcx, M::Provenance>) -> Size { + // The presence of `data_bytes` implies that our place is in memory. + let ptr = ecx + .place_to_op(place) + .expect("place must be in memory") + .as_mplace_or_imm() + .expect_left("place must be in memory") + .ptr(); + let (_prov, offset) = ptr.into_parts(); + offset + } + + fn reset_padding(&mut self, place: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { + let Some(data_bytes) = self.data_bytes.as_mut() else { return Ok(()) }; + // Our value must be in memory, otherwise we would not have set up `data_bytes`. + let mplace = self.ecx.force_allocation(place)?; + // Determine starting offset and size. + let (_prov, start_offset) = mplace.ptr().into_parts(); + let (size, _align) = self + .ecx + .size_and_align_of_mplace(&mplace)? + .unwrap_or((mplace.layout.size, mplace.layout.align.abi)); + // If there is no padding at all, we can skip the rest: check for + // a single data range covering the entire value. + if data_bytes.0 == &[(start_offset, size)] { + return Ok(()); + } + // Get a handle for the allocation. Do this only once, to avoid looking up the same + // allocation over and over again. (Though to be fair, iterating the value already does + // exactly that.) + let Some(mut alloc) = self.ecx.get_ptr_alloc_mut(mplace.ptr(), size)? else { + // A ZST, no padding to clear. + return Ok(()); + }; + // Add a "finalizer" data range at the end, so that the iteration below finds all gaps + // between ranges. + data_bytes.0.push((start_offset + size, Size::ZERO)); + // Iterate, and reset gaps. + let mut padding_cleared_until = start_offset; + for &(offset, size) in data_bytes.0.iter() { + assert!( + offset >= padding_cleared_until, + "reset_padding on {}: previous field ended at offset {}, next field starts at {} (and has a size of {} bytes)", + mplace.layout.ty, + (padding_cleared_until - start_offset).bytes(), + (offset - start_offset).bytes(), + size.bytes(), + ); + if offset > padding_cleared_until { + // We found padding. Adjust the range to be relative to `alloc`, and make it uninit. + let padding_start = padding_cleared_until - start_offset; + let padding_size = offset - padding_cleared_until; + let range = alloc_range(padding_start, padding_size); + trace!("reset_padding on {}: resetting padding range {range:?}", mplace.layout.ty); + alloc.write_uninit(range)?; + } + padding_cleared_until = offset + size; + } + assert!(padding_cleared_until == start_offset + size); + Ok(()) + } + + /// Computes the data range of this union type: + /// which bytes are inside a field (i.e., not padding.) + fn union_data_range<'e>( + ecx: &'e mut InterpCx<'tcx, M>, + layout: TyAndLayout<'tcx>, + ) -> Cow<'e, RangeSet> { + assert!(layout.ty.is_union()); + assert!(layout.abi.is_sized(), "there are no unsized unions"); + let layout_cx = LayoutCx { tcx: *ecx.tcx, param_env: ecx.param_env }; + return M::cached_union_data_range(ecx, layout.ty, || { + let mut out = RangeSet(Vec::new()); + union_data_range_uncached(&layout_cx, layout, Size::ZERO, &mut out); + out + }); + + /// Helper for recursive traversal: add data ranges of the given type to `out`. + fn union_data_range_uncached<'tcx>( + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, + layout: TyAndLayout<'tcx>, + base_offset: Size, + out: &mut RangeSet, + ) { + // If this is a ZST, we don't contain any data. In particular, this helps us to quickly + // skip over huge arrays of ZST. + if layout.is_zst() { + return; + } + // Just recursively add all the fields of everything to the output. + match &layout.fields { + FieldsShape::Primitive => { + out.add_range(base_offset, layout.size); + } + &FieldsShape::Union(fields) => { + // Currently, all fields start at offset 0 (relative to `base_offset`). + for field in 0..fields.get() { + let field = layout.field(cx, field); + union_data_range_uncached(cx, field, base_offset, out); + } + } + &FieldsShape::Array { stride, count } => { + let elem = layout.field(cx, 0); + + // Fast-path for large arrays of simple types that do not contain any padding. + if elem.abi.is_scalar() { + out.add_range(base_offset, elem.size * count); + } else { + for idx in 0..count { + // This repeats the same computation for every array element... but the alternative + // is to allocate temporary storage for a dedicated `out` set for the array element, + // and replicating that N times. Is that better? + union_data_range_uncached(cx, elem, base_offset + idx * stride, out); + } + } + } + FieldsShape::Arbitrary { offsets, .. } => { + for (field, &offset) in offsets.iter_enumerated() { + let field = layout.field(cx, field.as_usize()); + union_data_range_uncached(cx, field, base_offset + offset, out); + } + } + } + // Don't forget potential other variants. + match &layout.variants { + Variants::Single { .. } => { + // Fully handled above. + } + Variants::Multiple { variants, .. } => { + for variant in variants.indices() { + let variant = layout.for_variant(cx, variant); + union_data_range_uncached(cx, variant, base_offset, out); + } + } } } - false } } @@ -774,7 +1056,7 @@ fn mutability<'tcx>(ecx: &InterpCx<'tcx, impl Machine<'tcx>>, alloc_id: AllocId) } impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, 'tcx, M> { - type V = OpTy<'tcx, M::Provenance>; + type V = PlaceTy<'tcx, M::Provenance>; #[inline(always)] fn ecx(&self) -> &InterpCx<'tcx, M> { @@ -783,11 +1065,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, fn read_discriminant( &mut self, - op: &OpTy<'tcx, M::Provenance>, + val: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, VariantIdx> { self.with_elem(PathElem::EnumTag, move |this| { Ok(try_validation!( - this.ecx.read_discriminant(op), + this.ecx.read_discriminant(val), this.path, Ub(InvalidTag(val)) => InvalidEnumTag { value: format!("{val:x}"), @@ -802,44 +1084,54 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, #[inline] fn visit_field( &mut self, - old_op: &OpTy<'tcx, M::Provenance>, + old_val: &PlaceTy<'tcx, M::Provenance>, field: usize, - new_op: &OpTy<'tcx, M::Provenance>, + new_val: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - let elem = self.aggregate_field_path_elem(old_op.layout, field); - self.with_elem(elem, move |this| this.visit_value(new_op)) + let elem = self.aggregate_field_path_elem(old_val.layout, field); + self.with_elem(elem, move |this| this.visit_value(new_val)) } #[inline] fn visit_variant( &mut self, - old_op: &OpTy<'tcx, M::Provenance>, + old_val: &PlaceTy<'tcx, M::Provenance>, variant_id: VariantIdx, - new_op: &OpTy<'tcx, M::Provenance>, + new_val: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - let name = match old_op.layout.ty.kind() { + let name = match old_val.layout.ty.kind() { ty::Adt(adt, _) => PathElem::Variant(adt.variant(variant_id).name), // Coroutines also have variants ty::Coroutine(..) => PathElem::CoroutineState(variant_id), - _ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty), + _ => bug!("Unexpected type with variant: {:?}", old_val.layout.ty), }; - self.with_elem(name, move |this| this.visit_value(new_op)) + self.with_elem(name, move |this| this.visit_value(new_val)) } #[inline(always)] fn visit_union( &mut self, - op: &OpTy<'tcx, M::Provenance>, + val: &PlaceTy<'tcx, M::Provenance>, _fields: NonZero, ) -> InterpResult<'tcx> { // Special check for CTFE validation, preventing `UnsafeCell` inside unions in immutable memory. if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) { - if !op.layout.is_zst() && !op.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) { - if !self.in_mutable_memory(op) { + if !val.layout.is_zst() && !val.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) { + if !self.in_mutable_memory(val) { throw_validation_failure!(self.path, UnsafeCellInImmutable); } } } + if self.reset_provenance_and_padding + && let Some(data_bytes) = self.data_bytes.as_mut() + { + let base_offset = Self::data_range_offset(self.ecx, val); + // Determine and add data range for this union. + let union_data_range = Self::union_data_range(self.ecx, val.layout); + for &(offset, size) in union_data_range.0.iter() { + data_bytes.add_range(base_offset + offset, size); + } + } Ok(()) } @@ -847,39 +1139,41 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, fn visit_box( &mut self, _box_ty: Ty<'tcx>, - op: &OpTy<'tcx, M::Provenance>, + val: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - self.check_safe_pointer(op, PointerKind::Box)?; + self.check_safe_pointer(val, PointerKind::Box)?; Ok(()) } #[inline] - fn visit_value(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { - trace!("visit_value: {:?}, {:?}", *op, op.layout); + fn visit_value(&mut self, val: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { + trace!("visit_value: {:?}, {:?}", *val, val.layout); // Check primitive types -- the leaves of our recursive descent. + // This is called even for enum discriminants (which are "fields" of their enum), + // so for integer-typed discriminants the provenance reset will happen here. // We assume that the Scalar validity range does not restrict these values // any further than `try_visit_primitive` does! - if self.try_visit_primitive(op)? { + if self.try_visit_primitive(val)? { return Ok(()); } // Special check preventing `UnsafeCell` in the inner part of constants if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) { - if !op.layout.is_zst() - && let Some(def) = op.layout.ty.ty_adt_def() + if !val.layout.is_zst() + && let Some(def) = val.layout.ty.ty_adt_def() && def.is_unsafe_cell() { - if !self.in_mutable_memory(op) { + if !self.in_mutable_memory(val) { throw_validation_failure!(self.path, UnsafeCellInImmutable); } } } // Recursively walk the value at its type. Apply optimizations for some large types. - match op.layout.ty.kind() { + match val.layout.ty.kind() { ty::Str => { - let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate + let mplace = val.assert_mem_place(); // strings are unsized and hence never immediate let len = mplace.len(self.ecx)?; try_validation!( self.ecx.read_bytes_ptr_strip_provenance(mplace.ptr(), Size::from_bytes(len)), @@ -889,11 +1183,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, ); } ty::Array(tys, ..) | ty::Slice(tys) - // This optimization applies for types that can hold arbitrary bytes (such as - // integer and floating point types) or for structs or tuples with no fields. - // FIXME(wesleywiser) This logic could be extended further to arbitrary structs - // or tuples made up of integer/floating point types or inhabited ZSTs with no - // padding. + // This optimization applies for types that can hold arbitrary non-provenance bytes (such as + // integer and floating point types). + // FIXME(wesleywiser) This logic could be extended further to arbitrary structs or + // tuples made up of integer/floating point types or inhabited ZSTs with no padding. if matches!(tys.kind(), ty::Int(..) | ty::Uint(..) | ty::Float(..)) => { @@ -901,18 +1194,19 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // Optimized handling for arrays of integer/float type. // This is the length of the array/slice. - let len = op.len(self.ecx)?; + let len = val.len(self.ecx)?; // This is the element type size. let layout = self.ecx.layout_of(*tys)?; // This is the size in bytes of the whole array. (This checks for overflow.) let size = layout.size * len; // If the size is 0, there is nothing to check. - // (`size` can only be 0 of `len` is 0, and empty arrays are always valid.) + // (`size` can only be 0 if `len` is 0, and empty arrays are always valid.) if size == Size::ZERO { return Ok(()); } - // Now that we definitely have a non-ZST array, we know it lives in memory. - let mplace = match op.as_mplace_or_imm() { + // Now that we definitely have a non-ZST array, we know it lives in memory -- except it may + // be an uninitialized local variable, those are also "immediate". + let mplace = match val.to_op(self.ecx)?.as_mplace_or_imm() { Left(mplace) => mplace, Right(imm) => match *imm { Immediate::Uninit => @@ -958,20 +1252,30 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, } } } + + // Don't forget that these are all non-pointer types, and thus do not preserve + // provenance. + if self.reset_provenance_and_padding { + // We can't share this with above as above, we might be looking at read-only memory. + let mut alloc = self.ecx.get_ptr_alloc_mut(mplace.ptr(), size)?.expect("we already excluded size 0"); + alloc.clear_provenance()?; + // Also, mark this as containing data, not padding. + self.add_data_range(mplace.ptr(), size); + } } // Fast path for arrays and slices of ZSTs. We only need to check a single ZST element // of an array and not all of them, because there's only a single value of a specific // ZST type, so either validation fails for all elements or none. ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(*tys)?.is_zst() => { // Validate just the first element (if any). - if op.len(self.ecx)? > 0 { - self.visit_field(op, 0, &self.ecx.project_index(op, 0)?)?; + if val.len(self.ecx)? > 0 { + self.visit_field(val, 0, &self.ecx.project_index(val, 0)?)?; } } _ => { // default handler try_validation!( - self.walk_value(op), + self.walk_value(val), self.path, // It's not great to catch errors here, since we can't give a very good path, // but it's better than ICEing. @@ -992,15 +1296,15 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // FIXME: We could avoid some redundant checks here. For newtypes wrapping // scalars, we do the same check on every "level" (e.g., first we check // MyNewtype and then the scalar in there). - match op.layout.abi { + match val.layout.abi { Abi::Uninhabited => { - let ty = op.layout.ty; + let ty = val.layout.ty; throw_validation_failure!(self.path, UninhabitedVal { ty }); } Abi::Scalar(scalar_layout) => { if !scalar_layout.is_uninit_valid() { // There is something to check here. - let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?; + let scalar = self.read_scalar(val, ExpectedKind::InitScalar)?; self.visit_scalar(scalar, scalar_layout)?; } } @@ -1010,7 +1314,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // the other must be init. if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() { let (a, b) = - self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair(); + self.read_immediate(val, ExpectedKind::InitScalar)?.to_scalar_pair(); self.visit_scalar(a, a_layout)?; self.visit_scalar(b, b_layout)?; } @@ -1031,19 +1335,34 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn validate_operand_internal( - &self, - op: &OpTy<'tcx, M::Provenance>, + &mut self, + val: &PlaceTy<'tcx, M::Provenance>, path: Vec, ref_tracking: Option<&mut RefTracking, Vec>>, ctfe_mode: Option, + reset_provenance_and_padding: bool, ) -> InterpResult<'tcx> { - trace!("validate_operand_internal: {:?}, {:?}", *op, op.layout.ty); - - // Construct a visitor - let mut visitor = ValidityVisitor { path, ref_tracking, ctfe_mode, ecx: self }; + trace!("validate_operand_internal: {:?}, {:?}", *val, val.layout.ty); - // Run it. - match self.run_for_validation(|| visitor.visit_value(op)) { + // Run the visitor. + match self.run_for_validation(|ecx| { + let reset_padding = reset_provenance_and_padding && { + // Check if `val` is actually stored in memory. If not, padding is not even + // represented and we need not reset it. + ecx.place_to_op(val)?.as_mplace_or_imm().is_left() + }; + let mut v = ValidityVisitor { + path, + ref_tracking, + ctfe_mode, + ecx, + reset_provenance_and_padding, + data_bytes: reset_padding.then_some(RangeSet(Vec::new())), + }; + v.visit_value(val)?; + v.reset_padding(val)?; + InterpResult::Ok(()) + }) { Ok(()) => Ok(()), // Pass through validation failures and "invalid program" issues. Err(err) @@ -1079,13 +1398,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// - no `UnsafeCell` or non-ZST `&mut`. #[inline(always)] pub(crate) fn const_validate_operand( - &self, - op: &OpTy<'tcx, M::Provenance>, + &mut self, + val: &PlaceTy<'tcx, M::Provenance>, path: Vec, ref_tracking: &mut RefTracking, Vec>, ctfe_mode: CtfeValidationMode, ) -> InterpResult<'tcx> { - self.validate_operand_internal(op, path, Some(ref_tracking), Some(ctfe_mode)) + self.validate_operand_internal( + val, + path, + Some(ref_tracking), + Some(ctfe_mode), + /*reset_provenance*/ false, + ) } /// This function checks the data at `op` to be runtime-valid. @@ -1093,21 +1418,41 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// It will error if the bits at the destination do not match the ones described by the layout. #[inline(always)] pub fn validate_operand( - &self, - op: &OpTy<'tcx, M::Provenance>, + &mut self, + val: &PlaceTy<'tcx, M::Provenance>, recursive: bool, + reset_provenance_and_padding: bool, ) -> InterpResult<'tcx> { // Note that we *could* actually be in CTFE here with `-Zextra-const-ub-checks`, but it's // still correct to not use `ctfe_mode`: that mode is for validation of the final constant // value, it rules out things like `UnsafeCell` in awkward places. if !recursive { - return self.validate_operand_internal(op, vec![], None, None); + return self.validate_operand_internal( + val, + vec![], + None, + None, + reset_provenance_and_padding, + ); } // Do a recursive check. let mut ref_tracking = RefTracking::empty(); - self.validate_operand_internal(op, vec![], Some(&mut ref_tracking), None)?; + self.validate_operand_internal( + val, + vec![], + Some(&mut ref_tracking), + None, + reset_provenance_and_padding, + )?; while let Some((mplace, path)) = ref_tracking.todo.pop() { - self.validate_operand_internal(&mplace.into(), path, Some(&mut ref_tracking), None)?; + // Things behind reference do *not* have the provenance reset. + self.validate_operand_internal( + &mplace.into(), + path, + Some(&mut ref_tracking), + None, + /*reset_provenance_and_padding*/ false, + )?; } Ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index b02f12e3c7f0b..d8af67bd0e705 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -5,6 +5,7 @@ use std::num::NonZero; use rustc_index::IndexVec; use rustc_middle::mir::interpret::InterpResult; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; use rustc_target::abi::{FieldIdx, FieldsShape, VariantIdx, Variants}; use tracing::trace; @@ -82,6 +83,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { self.visit_value(new_val) } + /// Traversal logic; should not be overloaded. fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx> { let ty = v.layout().ty; trace!("walk_value: type: {ty}"); @@ -104,6 +106,17 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { // DynStar types. Very different from a dyn type (but strangely part of the // same variant in `TyKind`): These are pairs where the 2nd component is the // vtable, and the first component is the data (which must be ptr-sized). + + // First make sure the vtable can be read at its type. + // The type of this vtable is fake, it claims to be a reference to some actual memory but that isn't true. + // So we transmute it to a raw pointer. + let raw_ptr_ty = Ty::new_mut_ptr(*self.ecx().tcx, self.ecx().tcx.types.unit); + let raw_ptr_ty = self.ecx().layout_of(raw_ptr_ty)?; + let vtable_field = + self.ecx().project_field(v, 1)?.transmute(raw_ptr_ty, self.ecx())?; + self.visit_field(v, 1, &vtable_field)?; + + // Then unpack the first field, and continue. let data = self.ecx().unpack_dyn_star(v, data)?; return self.visit_field(v, 0, &data); } diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index d825a47bfdf5d..73dda81bd298b 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -14,6 +14,7 @@ #![feature(trait_alias)] #![feature(try_blocks)] #![feature(yeet_expr)] +#![warn(unreachable_pub)] // tidy-alphabetical-end pub mod check_consts; diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index cbd1fdeea2aa3..611a8e1a88497 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -4,7 +4,7 @@ use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants}; use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine}; -use crate::interpret::{InterpCx, MemoryKind, OpTy}; +use crate::interpret::{InterpCx, MemoryKind}; /// Determines if this type permits "raw" initialization by just transmuting some memory into an /// instance of `T`. @@ -32,15 +32,15 @@ pub fn check_validity_requirement<'tcx>( let layout_cx = LayoutCx { tcx, param_env: param_env_and_ty.param_env }; if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks { - might_permit_raw_init_strict(layout, &layout_cx, kind) + check_validity_requirement_strict(layout, &layout_cx, kind) } else { - might_permit_raw_init_lax(layout, &layout_cx, kind) + check_validity_requirement_lax(layout, &layout_cx, kind) } } -/// Implements the 'strict' version of the `might_permit_raw_init` checks; see that function for -/// details. -fn might_permit_raw_init_strict<'tcx>( +/// Implements the 'strict' version of the [`check_validity_requirement`] checks; see that function +/// for details. +fn check_validity_requirement_strict<'tcx>( ty: TyAndLayout<'tcx>, cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, kind: ValidityRequirement, @@ -61,18 +61,24 @@ fn might_permit_raw_init_strict<'tcx>( .expect("failed to write bytes for zero valid check"); } - let ot: OpTy<'_, _> = allocated.into(); - // Assume that if it failed, it's a validation failure. // This does *not* actually check that references are dereferenceable, but since all types that // require dereferenceability also require non-null, we don't actually get any false negatives // due to this. - Ok(cx.validate_operand(&ot, /*recursive*/ false).is_ok()) + // The value we are validating is temporary and discarded at the end of this function, so + // there is no point in reseting provenance and padding. + Ok(cx + .validate_operand( + &allocated.into(), + /*recursive*/ false, + /*reset_provenance_and_padding*/ false, + ) + .is_ok()) } -/// Implements the 'lax' (default) version of the `might_permit_raw_init` checks; see that function for -/// details. -fn might_permit_raw_init_lax<'tcx>( +/// Implements the 'lax' (default) version of the [`check_validity_requirement`] checks; see that +/// function for details. +fn check_validity_requirement_lax<'tcx>( this: TyAndLayout<'tcx>, cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, init_kind: ValidityRequirement, @@ -137,7 +143,7 @@ fn might_permit_raw_init_lax<'tcx>( } FieldsShape::Arbitrary { offsets, .. } => { for idx in 0..offsets.len() { - if !might_permit_raw_init_lax(this.field(cx, idx), cx, init_kind)? { + if !check_validity_requirement_lax(this.field(cx, idx), cx, init_kind)? { // We found a field that is unhappy with this kind of initialization. return Ok(false); } diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 69cbf8c416105..38c6c8a8d11c7 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -22,7 +22,7 @@ rustc_index = { path = "../rustc_index", package = "rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] } -stacker = "0.1.15" +stacker = "0.1.17" tempfile = "3.2" thin-vec = "0.2.12" tracing = "0.1" diff --git a/compiler/rustc_data_structures/src/base_n.rs b/compiler/rustc_data_structures/src/base_n.rs index 1c2321623e491..0c3d7613d4f1f 100644 --- a/compiler/rustc_data_structures/src/base_n.rs +++ b/compiler/rustc_data_structures/src/base_n.rs @@ -42,7 +42,7 @@ impl fmt::Display for BaseNString { } // This trait just lets us reserve the exact right amount of space when doing fixed-length -// case-insensitve encoding. Add any impls you need. +// case-insensitive encoding. Add any impls you need. pub trait ToBaseN: Into { fn encoded_len(base: usize) -> usize; diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 2a457ffb70b08..06fedef00fc3f 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -477,7 +477,7 @@ where // will know when we hit the state where previous_node == node. loop { // Back at the beginning, we can return. Note that we return the root state. - // This is becuse for components being explored, we would otherwise get a + // This is because for components being explored, we would otherwise get a // `node_state[n] = InCycleWith{ parent: n }` and that's wrong. if previous_node == node { return root_state; diff --git a/compiler/rustc_data_structures/src/hashes.rs b/compiler/rustc_data_structures/src/hashes.rs index f98c8de1eb097..8f4639fc2e666 100644 --- a/compiler/rustc_data_structures/src/hashes.rs +++ b/compiler/rustc_data_structures/src/hashes.rs @@ -3,11 +3,11 @@ //! or 16 bytes of the hash. //! //! The types in this module represent 64-bit or 128-bit hashes produced by a `StableHasher`. -//! `Hash64` and `Hash128` expose some utilty functions to encourage users to not extract the inner +//! `Hash64` and `Hash128` expose some utility functions to encourage users to not extract the inner //! hash value as an integer type and accidentally apply varint encoding to it. //! //! In contrast with `Fingerprint`, users of these types cannot and should not attempt to construct -//! and decompose these types into constitutent pieces. The point of these types is only to +//! and decompose these types into constituent pieces. The point of these types is only to //! connect the fact that they can only be produced by a `StableHasher` to their //! `Encode`/`Decode` impls. diff --git a/compiler/rustc_data_structures/src/steal.rs b/compiler/rustc_data_structures/src/steal.rs index 0f2c0eee27d2f..aaa95f6b7f19e 100644 --- a/compiler/rustc_data_structures/src/steal.rs +++ b/compiler/rustc_data_structures/src/steal.rs @@ -57,6 +57,7 @@ impl Steal { /// /// This should not be used within rustc as it leaks information not tracked /// by the query system, breaking incremental compilation. + #[cfg_attr(not(bootstrap), rustc_lint_untracked_query_information)] pub fn is_stolen(&self) -> bool { self.value.borrow().is_none() } diff --git a/compiler/rustc_data_structures/src/sync/worker_local.rs b/compiler/rustc_data_structures/src/sync/worker_local.rs index 4950481d311f0..b6efcada10b77 100644 --- a/compiler/rustc_data_structures/src/sync/worker_local.rs +++ b/compiler/rustc_data_structures/src/sync/worker_local.rs @@ -19,7 +19,7 @@ impl RegistryId { /// index within the registry. This panics if the current thread is not associated with this /// registry. /// - /// Note that there's a race possible where the identifer in `THREAD_DATA` could be reused + /// Note that there's a race possible where the identifier in `THREAD_DATA` could be reused /// so this can succeed from a different registry. #[cfg(parallel_compiler)] fn verify(self) -> usize { @@ -50,7 +50,7 @@ struct ThreadData { } thread_local! { - /// A thread local which contains the identifer of `REGISTRY` but allows for faster access. + /// A thread local which contains the identifier of `REGISTRY` but allows for faster access. /// It also holds the index of the current thread. static THREAD_DATA: ThreadData = const { ThreadData { registry_id: Cell::new(RegistryId(ptr::null())), @@ -66,7 +66,7 @@ impl Registry { /// Gets the registry associated with the current thread. Panics if there's no such registry. pub fn current() -> Self { - REGISTRY.with(|registry| registry.get().cloned().expect("No assocated registry")) + REGISTRY.with(|registry| registry.get().cloned().expect("No associated registry")) } /// Registers the current thread with the registry so worker locals can be used on it. @@ -92,7 +92,7 @@ impl Registry { } } - /// Gets the identifer of this registry. + /// Gets the identifier of this registry. fn id(&self) -> RegistryId { RegistryId(&*self.0) } diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 91cbffcd7072b..6d6d3f35a4b1b 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -23,6 +23,7 @@ rustc_hir_analysis = { path = "../rustc_hir_analysis" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_hir_typeck = { path = "../rustc_hir_typeck" } rustc_incremental = { path = "../rustc_incremental" } +rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } rustc_interface = { path = "../rustc_interface" } rustc_lint = { path = "../rustc_lint" } @@ -72,6 +73,10 @@ ctrlc = "3.4.4" # tidy-alphabetical-start llvm = ['rustc_interface/llvm'] max_level_info = ['rustc_log/max_level_info'] +rustc_randomized_layouts = [ + 'rustc_index/rustc_randomized_layouts', + 'rustc_middle/rustc_randomized_layouts' +] rustc_use_parallel_compiler = [ 'rustc_data_structures/rustc_use_parallel_compiler', 'rustc_interface/rustc_use_parallel_compiler', diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index e49ae60e890ba..1b7ca61cee849 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -7,7 +7,6 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable -#![cfg_attr(bootstrap, feature(unsafe_extern_blocks))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(decl_macro)] @@ -61,7 +60,6 @@ use rustc_session::lint::{Lint, LintId}; use rustc_session::output::collect_crate_types; use rustc_session::{config, filesearch, EarlyDiagCtxt, Session}; use rustc_span::source_map::FileLoader; -use rustc_span::symbol::sym; use rustc_span::FileName; use rustc_target::json::ToJson; use rustc_target::spec::{Target, TargetTriple}; @@ -777,16 +775,8 @@ fn print_crate_info( .config .iter() .filter_map(|&(name, value)| { - // Note that crt-static is a specially recognized cfg - // directive that's printed out here as part of - // rust-lang/rust#37406, but in general the - // `target_feature` cfg is gated under - // rust-lang/rust#29717. For now this is just - // specifically allowing the crt-static cfg and that's - // it, this is intended to get into Cargo and then go - // through to build scripts. - if (name != sym::target_feature || value != Some(sym::crt_dash_static)) - && !sess.is_nightly_build() + // On stable, exclude unstable flags. + if !sess.is_nightly_build() && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some() { return None; @@ -871,9 +861,9 @@ fn print_crate_info( use rustc_target::spec::current_apple_deployment_target; if sess.target.is_like_osx { - let (major, minor) = current_apple_deployment_target(&sess.target) - .expect("unknown Apple target OS"); - println_info!("deployment_target={}", format!("{major}.{minor}")) + let (major, minor, patch) = current_apple_deployment_target(&sess.target); + let patch = if patch != 0 { format!(".{patch}") } else { String::new() }; + println_info!("deployment_target={major}.{minor}{patch}") } else { #[allow(rustc::diagnostic_outside_of_impl)] sess.dcx().fatal("only Apple targets currently support deployment version info") diff --git a/compiler/rustc_error_codes/src/error_codes/E0582.md b/compiler/rustc_error_codes/src/error_codes/E0582.md index b2cdb509c95c0..c4aaa17706add 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0582.md +++ b/compiler/rustc_error_codes/src/error_codes/E0582.md @@ -44,7 +44,7 @@ where ``` The latter scenario encounters this error because `Foo::Assoc<'a>` could be implemented by a type that does not use the `'a` parameter, so there is no -guarentee that `X::Assoc<'a>` actually uses `'a`. +guarantee that `X::Assoc<'a>` actually uses `'a`. To fix this we can pass a dummy parameter: ``` diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 3303e4ee752c2..1c39840207ca6 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -12,7 +12,7 @@ use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{AttrId, Span, DUMMY_SP}; use tracing::debug; use crate::snippet::Style; @@ -356,24 +356,19 @@ impl DiagInner { pub(crate) fn update_unstable_expectation_id( &mut self, - unstable_to_stable: &FxIndexMap, + unstable_to_stable: &FxIndexMap, ) { if let Level::Expect(expectation_id) | Level::ForceWarning(Some(expectation_id)) = &mut self.level + && let LintExpectationId::Unstable { attr_id, lint_index } = *expectation_id { - if expectation_id.is_stable() { - return; - } - // The unstable to stable map only maps the unstable `AttrId` to a stable `HirId` with an attribute index. // The lint index inside the attribute is manually transferred here. - let lint_index = expectation_id.get_lint_index(); - expectation_id.set_lint_index(None); - let mut stable_id = unstable_to_stable - .get(expectation_id) - .expect("each unstable `LintExpectationId` must have a matching stable id") - .normalize(); + let Some(stable_id) = unstable_to_stable.get(&attr_id) else { + panic!("{expectation_id:?} must have a matching stable id") + }; + let mut stable_id = *stable_id; stable_id.set_lint_index(lint_index); *expectation_id = stable_id; } @@ -484,7 +479,7 @@ pub struct Subdiag { /// - The `EmissionGuarantee`, which determines the type returned from `emit`. /// /// Each constructed `Diag` must be consumed by a function such as `emit`, -/// `cancel`, `delay_as_bug`, or `into_diag`. A panic occurrs if a `Diag` +/// `cancel`, `delay_as_bug`, or `into_diag`. A panic occurs if a `Diag` /// is dropped without being consumed by one of these functions. /// /// If there is some state in a downstream crate you would like to access in diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 2bc29dabd18a1..2b135df91a4bb 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2300,7 +2300,7 @@ impl HumanEmitter { // For example, for the following: // | // 2 - .await - // 2 + (note the left over whitepsace) + // 2 + (note the left over whitespace) // | // We really want // | diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 99ee8fb17d7dc..02ead41fc68f1 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -69,7 +69,7 @@ use rustc_macros::{Decodable, Encodable}; pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; use rustc_span::source_map::SourceMap; pub use rustc_span::ErrorGuaranteed; -use rustc_span::{Loc, Span, DUMMY_SP}; +use rustc_span::{AttrId, Loc, Span, DUMMY_SP}; pub use snippet::Style; // Used by external projects such as `rust-gpu`. // See https://github.com/rust-lang/rust/pull/115393. @@ -817,7 +817,7 @@ impl<'a> DiagCtxtHandle<'a> { ); } // We delay a bug here so that `-Ztreat-err-as-bug -Zeagerly-emit-delayed-bugs` - // can be used to create a backtrace at the stashing site insted of whenever the + // can be used to create a backtrace at the stashing site instead of whenever the // diagnostic context is dropped and thus delayed bugs are emitted. Error => Some(self.span_delayed_bug(span, format!("stashing {key:?}"))), DelayedBug => { @@ -1096,7 +1096,7 @@ impl<'a> DiagCtxtHandle<'a> { pub fn update_unstable_expectation_id( &self, - unstable_to_stable: &FxIndexMap, + unstable_to_stable: FxIndexMap, ) { let mut inner = self.inner.borrow_mut(); let diags = std::mem::take(&mut inner.unstable_expect_diagnostics); @@ -1105,7 +1105,7 @@ impl<'a> DiagCtxtHandle<'a> { if !diags.is_empty() { inner.suppressed_expected_diag = true; for mut diag in diags.into_iter() { - diag.update_unstable_expectation_id(unstable_to_stable); + diag.update_unstable_expectation_id(&unstable_to_stable); // Here the diagnostic is given back to `emit_diagnostic` where it was first // intercepted. Now it should be processed as usual, since the unstable expectation @@ -1117,11 +1117,11 @@ impl<'a> DiagCtxtHandle<'a> { inner .stashed_diagnostics .values_mut() - .for_each(|(diag, _guar)| diag.update_unstable_expectation_id(unstable_to_stable)); + .for_each(|(diag, _guar)| diag.update_unstable_expectation_id(&unstable_to_stable)); inner .future_breakage_diagnostics .iter_mut() - .for_each(|diag| diag.update_unstable_expectation_id(unstable_to_stable)); + .for_each(|diag| diag.update_unstable_expectation_id(&unstable_to_stable)); } /// This methods steals all [`LintExpectationId`]s that are stored inside @@ -1567,7 +1567,7 @@ impl DiagCtxtInner { if let LintExpectationId::Unstable { .. } = expect_id { unreachable!(); // this case was handled at the top of this function } - self.fulfilled_expectations.insert(expect_id.normalize()); + self.fulfilled_expectations.insert(expect_id); if let Expect(_) = diagnostic.level { // Nothing emitted here for expected lints. TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs index b1db44dd215b8..8dd146c1c337f 100644 --- a/compiler/rustc_errors/src/markdown/parse.rs +++ b/compiler/rustc_errors/src/markdown/parse.rs @@ -487,7 +487,7 @@ fn is_break_ty(val: &MdTree<'_>) -> bool { || matches!(val, MdTree::PlainText(txt) if txt.trim().is_empty()) } -/// Perform tranformations to text. This splits paragraphs, replaces patterns, +/// Perform transformations to text. This splits paragraphs, replaces patterns, /// and corrects newlines. /// /// To avoid allocating strings (and using a different heavier tt type), our diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 9011d02da3358..501a2417fcf31 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -398,8 +398,10 @@ pub(crate) enum NamedMatch { fn token_name_eq(t1: &Token, t2: &Token) -> bool { if let (Some((ident1, is_raw1)), Some((ident2, is_raw2))) = (t1.ident(), t2.ident()) { ident1.name == ident2.name && is_raw1 == is_raw2 - } else if let (Some(ident1), Some(ident2)) = (t1.lifetime(), t2.lifetime()) { - ident1.name == ident2.name + } else if let (Some((ident1, is_raw1)), Some((ident2, is_raw2))) = + (t1.lifetime(), t2.lifetime()) + { + ident1.name == ident2.name && is_raw1 == is_raw2 } else { t1.kind == t2.kind } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index b06910595bb25..39489a8df1bed 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -283,9 +283,9 @@ pub(super) fn transcribe<'a>( let kind = token::NtIdent(*ident, *is_raw); TokenTree::token_alone(kind, sp) } - MatchedSingle(ParseNtResult::Lifetime(ident)) => { + MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => { marker.visit_span(&mut sp); - let kind = token::NtLifetime(*ident); + let kind = token::NtLifetime(*ident, *is_raw); TokenTree::token_alone(kind, sp) } MatchedSingle(ParseNtResult::Nt(nt)) => { diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 4ff5da1a4bdfa..5798bcedc2263 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -229,15 +229,16 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec { + Lifetime(name, is_raw) => { let ident = symbol::Ident::new(name, span).without_first_quote(); trees.extend([ TokenTree::Punct(Punct { ch: b'\'', joint: true, span }), - TokenTree::Ident(Ident { sym: ident.name, is_raw: false, span }), + TokenTree::Ident(Ident { sym: ident.name, is_raw: is_raw.into(), span }), ]); } - NtLifetime(ident) => { - let stream = TokenStream::token_alone(token::Lifetime(ident.name), ident.span); + NtLifetime(ident, is_raw) => { + let stream = + TokenStream::token_alone(token::Lifetime(ident.name, is_raw), ident.span); trees.push(TokenTree::Group(Group { delimiter: pm::Delimiter::None, stream: Some(stream), diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 9223c3c322a56..8949fdffdaec1 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -61,7 +61,7 @@ declare_features! ( /// Allows explicit discriminants on non-unit enum variants. (accepted, arbitrary_enum_discriminant, "1.66.0", Some(60553)), /// Allows using `const` operands in inline assembly. - (accepted, asm_const, "CURRENT_RUSTC_VERSION", Some(93332)), + (accepted, asm_const, "1.82.0", Some(93332)), /// Allows using `sym` operands in inline assembly. (accepted, asm_sym, "1.66.0", Some(93333)), /// Allows the definition of associated constants in `trait` or `impl` blocks. @@ -116,7 +116,7 @@ declare_features! ( /// Allows calling constructor functions in `const fn`. (accepted, const_constructor, "1.40.0", Some(61456)), /// Allows basic arithmetic on floating point types in a `const fn`. - (accepted, const_fn_floating_point_arithmetic, "CURRENT_RUSTC_VERSION", Some(57241)), + (accepted, const_fn_floating_point_arithmetic, "1.82.0", Some(57241)), /// Allows using and casting function pointers in a `const fn`. (accepted, const_fn_fn_ptr_basics, "1.61.0", Some(57563)), /// Allows trait bounds in `const fn`. @@ -272,7 +272,7 @@ declare_features! ( /// Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions. (accepted, min_const_unsafe_fn, "1.33.0", Some(55607)), /// Allows exhaustive pattern matching on uninhabited types when matched by value. - (accepted, min_exhaustive_patterns, "CURRENT_RUSTC_VERSION", Some(119612)), + (accepted, min_exhaustive_patterns, "1.82.0", Some(119612)), /// Allows using `Self` and associated types in struct expressions and patterns. (accepted, more_struct_aliases, "1.16.0", Some(37544)), /// Allows using the MOVBE target feature. @@ -299,7 +299,7 @@ declare_features! ( /// Allows `foo.rs` as an alternative to `foo/mod.rs`. (accepted, non_modrs_mods, "1.30.0", Some(44660)), /// Allows using multiple nested field accesses in offset_of! - (accepted, offset_of_nested, "CURRENT_RUSTC_VERSION", Some(120140)), + (accepted, offset_of_nested, "1.82.0", Some(120140)), /// Allows the use of or-patterns (e.g., `0 | 1`). (accepted, or_patterns, "1.53.0", Some(54883)), /// Allows using `+bundle,+whole-archive` link modifiers with native libs. @@ -312,7 +312,7 @@ declare_features! ( /// Allows parentheses in patterns. (accepted, pattern_parentheses, "1.31.0", Some(51087)), /// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args. - (accepted, precise_capturing, "CURRENT_RUSTC_VERSION", Some(123432)), + (accepted, precise_capturing, "1.82.0", Some(123432)), /// Allows procedural macros in `proc-macro` crates. (accepted, proc_macro, "1.29.0", Some(38356)), /// Allows multi-segment paths in attributes and derives. @@ -326,7 +326,7 @@ declare_features! ( /// Allows keywords to be escaped for use as identifiers. (accepted, raw_identifiers, "1.30.0", Some(48589)), /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. - (accepted, raw_ref_op, "CURRENT_RUSTC_VERSION", Some(64490)), + (accepted, raw_ref_op, "1.82.0", Some(64490)), /// Allows relaxing the coherence rules such that /// `impl ForeignTrait for ForeignType` is permitted. (accepted, re_rebalance_coherence, "1.41.0", Some(55437)), @@ -399,11 +399,11 @@ declare_features! ( /// Allows arbitrary delimited token streams in non-macro attributes. (accepted, unrestricted_attribute_tokens, "1.34.0", Some(55208)), /// Allows unsafe attributes. - (accepted, unsafe_attributes, "CURRENT_RUSTC_VERSION", Some(123757)), + (accepted, unsafe_attributes, "1.82.0", Some(123757)), /// The `unsafe_op_in_unsafe_fn` lint (allowed by default): no longer treat an unsafe function as an unsafe block. (accepted, unsafe_block_in_unsafe_fn, "1.52.0", Some(71668)), /// Allows unsafe on extern declarations and safety qualifiers over internal items. - (accepted, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)), + (accepted, unsafe_extern_blocks, "1.82.0", Some(123743)), /// Allows importing and reexporting macros with `use`, /// enables macro modularization in general. (accepted, use_extern_macros, "1.30.0", Some(35896)), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index e2491922b8df6..3b7e0d82d0f62 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -793,6 +793,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_lint_query_instability, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE ), + // Used by the `rustc::untracked_query_information` lint to warn methods which + // might not be stable during incremental compilation. + rustc_attr!( + rustc_lint_untracked_query_information, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + ), // Used by the `rustc::diagnostic_outside_of_impl` lints to assist in changes to diagnostic // APIs. Any function with this attribute will be checked by that lint. rustc_attr!( @@ -1180,3 +1186,11 @@ pub static BUILTIN_ATTRIBUTE_MAP: LazyLock> } map }); + +pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool { + match sym { + sym::on_unimplemented => true, + sym::do_not_recommend => features.do_not_recommend, + _ => false, + } +} diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index adaaba3cd235e..fe12930e6b9d6 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -130,8 +130,9 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option() {`. - (incomplete, unsized_const_params, "CURRENT_RUSTC_VERSION", Some(95174)), + (incomplete, unsized_const_params, "1.82.0", Some(95174)), /// Allows unsized fn parameters. (internal, unsized_fn_params, "1.49.0", Some(48055)), /// Allows unsized rvalues at arguments and parameters. diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 36e29d2dcb2cc..bd55617d84ea2 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -326,41 +326,6 @@ impl DefKind { | DefKind::ExternCrate => false, } } - - /// Whether `query struct_target_features` should be used with this definition. - pub fn has_struct_target_features(self) -> bool { - match self { - DefKind::Struct | DefKind::Union | DefKind::Enum => true, - DefKind::Fn - | DefKind::AssocFn - | DefKind::Ctor(..) - | DefKind::Closure - | DefKind::Static { .. } - | DefKind::Mod - | DefKind::Variant - | DefKind::Trait - | DefKind::TyAlias - | DefKind::ForeignTy - | DefKind::TraitAlias - | DefKind::AssocTy - | DefKind::Const - | DefKind::AssocConst - | DefKind::Macro(..) - | DefKind::Use - | DefKind::ForeignMod - | DefKind::OpaqueTy - | DefKind::Impl { .. } - | DefKind::Field - | DefKind::TyParam - | DefKind::ConstParam - | DefKind::LifetimeParam - | DefKind::AnonConst - | DefKind::InlineConst - | DefKind::SyntheticCoroutineBody - | DefKind::GlobalAsm - | DefKind::ExternCrate => false, - } - } } /// The resolution of a path or export. @@ -863,8 +828,13 @@ pub enum LifetimeRes { /// This variant is used for anonymous lifetimes that we did not resolve during /// late resolution. Those lifetimes will be inferred by typechecking. Infer, - /// Explicit `'static` lifetime. - Static, + /// `'static` lifetime. + Static { + /// We do not want to emit `elided_named_lifetimes` + /// when we are inside of a const item or a static, + /// because it would get too annoying. + suppress_elision_warning: bool, + }, /// Resolution failure. Error, /// HACK: This is used to recover the NodeId of an elided lifetime. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 57c47d29857c3..b01c24cf3053e 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -168,6 +168,19 @@ impl Lifetime { (LifetimeSuggestionPosition::Normal, self.ident.span) } } + + pub fn suggestion(&self, new_lifetime: &str) -> (Span, String) { + debug_assert!(new_lifetime.starts_with('\'')); + let (pos, span) = self.suggestion_position(); + let code = match pos { + LifetimeSuggestionPosition::Normal => format!("{new_lifetime}"), + LifetimeSuggestionPosition::Ampersand => format!("{new_lifetime} "), + LifetimeSuggestionPosition::ElidedPath => format!("<{new_lifetime}>"), + LifetimeSuggestionPosition::ElidedPathArgument => format!("{new_lifetime}, "), + LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lifetime}"), + }; + (span, code) + } } /// A `Path` is essentially Rust's notion of a name; for instance, diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index 53c8586b52a79..46a0d4f498aa9 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -4,6 +4,7 @@ use rustc_session::Limit; use rustc_span::def_id::{LocalDefId, LOCAL_CRATE}; use rustc_span::Span; use rustc_trait_selection::traits::ObligationCtxt; +use tracing::{debug, instrument}; use crate::errors::AutoDerefReachedRecursionLimit; use crate::traits; diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d414bcdb95b34..e47c707ee18d9 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -27,6 +27,7 @@ use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_type_ir::fold::TypeFoldable; +use tracing::{debug, instrument}; use {rustc_attr as attr, rustc_hir as hir}; use super::compare_impl_item::{check_type_bounds, compare_impl_method, compare_impl_ty}; 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 35577613800b2..2afed04c5bcd5 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -28,6 +28,7 @@ use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{ self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal, }; +use tracing::{debug, instrument}; use super::potentially_plural_count; use crate::errors::{LifetimesOrBoundsMismatchOnTrait, MethodShouldReturnFuture}; diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 678b8c89a5054..6820a44f141bd 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -97,6 +97,7 @@ use rustc_trait_selection::error_reporting::infer::ObligationCauseExt as _; use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::ObligationCtxt; +use tracing::debug; use self::compare_impl_item::collect_return_position_impl_trait_in_trait_tys; use self::region::region_scope_tree; diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 483351739792b..2d6147cff2a5a 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -18,6 +18,7 @@ use rustc_middle::bug; use rustc_middle::middle::region::*; use rustc_middle::ty::TyCtxt; use rustc_span::source_map; +use tracing::debug; use super::errs::{maybe_expr_static_mut, maybe_stmt_static_mut}; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index cb66179ec8047..3627faf8dfc57 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -36,6 +36,7 @@ use rustc_trait_selection::traits::{ }; use rustc_type_ir::solve::NoSolution; use rustc_type_ir::TypeFlags; +use tracing::{debug, instrument}; use {rustc_ast as ast, rustc_hir as hir}; use crate::autoderef::Autoderef; @@ -1015,7 +1016,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), sym::adt_const_params, )]) } - // Implments `ConstParamTy`, suggest adding the feature to enable. + // Implements `ConstParamTy`, suggest adding the feature to enable. Ok(..) => Some(vec![(adt_const_params_feature_string, sym::adt_const_params)]), }; if let Some(features) = may_suggest_feature { @@ -1651,6 +1652,13 @@ fn check_fn_or_method<'tcx>( } } +/// The `arbitrary_self_types_pointers` feature implies `arbitrary_self_types`. +#[derive(Clone, Copy, PartialEq)] +enum ArbitrarySelfTypesLevel { + Basic, // just arbitrary_self_types + WithPointers, // both arbitrary_self_types and arbitrary_self_types_pointers +} + #[instrument(level = "debug", skip(wfcx))] fn check_method_receiver<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, @@ -1683,14 +1691,27 @@ fn check_method_receiver<'tcx>( return Ok(()); } - if tcx.features().arbitrary_self_types { - if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, true) { - // Report error; `arbitrary_self_types` was enabled. - return Err(tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty })); - } + let arbitrary_self_types_level = if tcx.features().arbitrary_self_types_pointers { + Some(ArbitrarySelfTypesLevel::WithPointers) + } else if tcx.features().arbitrary_self_types { + Some(ArbitrarySelfTypesLevel::Basic) } else { - if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, false) { - return Err(if receiver_is_valid(wfcx, span, receiver_ty, self_ty, true) { + None + }; + + if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, arbitrary_self_types_level) { + return Err(match arbitrary_self_types_level { + // Wherever possible, emit a message advising folks that the features + // `arbitrary_self_types` or `arbitrary_self_types_pointers` might + // have helped. + None if receiver_is_valid( + wfcx, + span, + receiver_ty, + self_ty, + Some(ArbitrarySelfTypesLevel::Basic), + ) => + { // Report error; would have worked with `arbitrary_self_types`. feature_err( &tcx.sess, @@ -1698,25 +1719,49 @@ fn check_method_receiver<'tcx>( span, format!( "`{receiver_ty}` cannot be used as the type of `self` without \ - the `arbitrary_self_types` feature", + the `arbitrary_self_types` feature", ), ) .with_help(fluent::hir_analysis_invalid_receiver_ty_help) .emit() - } else { - // Report error; would not have worked with `arbitrary_self_types`. + } + None | Some(ArbitrarySelfTypesLevel::Basic) + if receiver_is_valid( + wfcx, + span, + receiver_ty, + self_ty, + Some(ArbitrarySelfTypesLevel::WithPointers), + ) => + { + // Report error; would have worked with `arbitrary_self_types_pointers`. + feature_err( + &tcx.sess, + sym::arbitrary_self_types_pointers, + span, + format!( + "`{receiver_ty}` cannot be used as the type of `self` without \ + the `arbitrary_self_types_pointers` feature", + ), + ) + .with_help(fluent::hir_analysis_invalid_receiver_ty_help) + .emit() + } + _ => + // Report error; would not have worked with `arbitrary_self_types[_pointers]`. + { tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty }) - }); - } + } + }); } Ok(()) } /// Returns whether `receiver_ty` would be considered a valid receiver type for `self_ty`. If /// `arbitrary_self_types` is enabled, `receiver_ty` must transitively deref to `self_ty`, possibly -/// through a `*const/mut T` raw pointer. If the feature is not enabled, the requirements are more -/// strict: `receiver_ty` must implement `Receiver` and directly implement -/// `Deref`. +/// through a `*const/mut T` raw pointer if `arbitrary_self_types_pointers` is also enabled. +/// If neither feature is enabled, the requirements are more strict: `receiver_ty` must implement +/// `Receiver` and directly implement `Deref`. /// /// N.B., there are cases this function returns `true` but causes an error to be emitted, /// particularly when `receiver_ty` derefs to a type that is the same as `self_ty` but has the @@ -1726,7 +1771,7 @@ fn receiver_is_valid<'tcx>( span: Span, receiver_ty: Ty<'tcx>, self_ty: Ty<'tcx>, - arbitrary_self_types_enabled: bool, + arbitrary_self_types_enabled: Option, ) -> bool { let infcx = wfcx.infcx; let tcx = wfcx.tcx(); @@ -1744,8 +1789,8 @@ fn receiver_is_valid<'tcx>( let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty); - // The `arbitrary_self_types` feature allows raw pointer receivers like `self: *const Self`. - if arbitrary_self_types_enabled { + // The `arbitrary_self_types_pointers` feature allows raw pointer receivers like `self: *const Self`. + if arbitrary_self_types_enabled == Some(ArbitrarySelfTypesLevel::WithPointers) { autoderef = autoderef.include_raw_pointers(); } @@ -1771,7 +1816,7 @@ fn receiver_is_valid<'tcx>( // Without `feature(arbitrary_self_types)`, we require that each step in the // deref chain implement `receiver`. - if !arbitrary_self_types_enabled { + if arbitrary_self_types_enabled.is_none() { if !receiver_is_implemented( wfcx, receiver_trait_def_id, diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs index ca9e2e8a3cc6a..68b7b44c36d67 100644 --- a/compiler/rustc_hir_analysis/src/check_unused.rs +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -4,6 +4,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint; +use tracing::debug; pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_unused_traits, ..*providers }; diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 23f1adfe302e3..30fc06829ed8e 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -23,6 +23,7 @@ use rustc_trait_selection::traits::misc::{ ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason, }; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt}; +use tracing::debug; use crate::errors; @@ -218,7 +219,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() // Later parts of the compiler rely on all DispatchFromDyn types to be ABI-compatible with raw // pointers. This is enforced here: we only allow impls for references, raw pointers, and things // that are effectively repr(transparent) newtypes around types that already hav a - // DispatchedFromDyn impl. We cannot literally use repr(transparent) on those tpyes since some + // DispatchedFromDyn impl. We cannot literally use repr(transparent) on those types since some // of them support an allocator, but we ensure that for the cases where the type implements this // trait, they *do* satisfy the repr(transparent) rules, and then we assume that everything else // in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent) diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index bd8b43e28e540..9fce927e2c7f2 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -93,7 +93,8 @@ impl<'tcx> InherentCollect<'tcx> { } } - if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsCandidateKey) { + if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::InstantiateWithInfer) + { self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id); } else { bug!("unexpected self type: {:?}", self_ty); @@ -132,7 +133,7 @@ impl<'tcx> InherentCollect<'tcx> { } } - if let Some(simp) = simplify_type(self.tcx, ty, TreatParams::AsCandidateKey) { + if let Some(simp) = simplify_type(self.tcx, ty, TreatParams::InstantiateWithInfer) { self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id); } else { bug!("unexpected primitive type: {:?}", ty); diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index 00bbbf7130f83..6825c2e33fe71 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{ErrorGuaranteed, Symbol}; use rustc_trait_selection::traits::{self, SkipLeakCheck}; use smallvec::SmallVec; +use tracing::debug; pub(crate) fn crate_inherent_impls_overlap_check( tcx: TyCtxt<'_>, diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 3d800bb165cb4..db809e4837d66 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -13,6 +13,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_session::parse::feature_err; use rustc_span::{sym, ErrorGuaranteed}; +use tracing::debug; use crate::errors; diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index dcd0e3111a489..5fdaba41fb262 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -14,6 +14,7 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_trait_selection::traits::{ self, IsFirstInputType, OrphanCheckErr, OrphanCheckMode, UncoveredTyParams, }; +use tracing::{debug, instrument}; use crate::errors; diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index b8fbe0e99ef79..ac9976148e25c 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -42,6 +42,7 @@ use rustc_target::spec::abi; use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCtxt; +use tracing::{debug, instrument}; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::errors; @@ -1036,7 +1037,7 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { /// Check the uniqueness of fields in a struct variant, and recursively /// check the nested fields if it is an unnamed field with type of an - /// annoymous adt. + /// anonymous adt. fn check_field(&mut self, field: &hir::FieldDef<'_>) { if field.ident.name != kw::Underscore { self.check_field_decl(field.ident, field.span.into()); @@ -1490,7 +1491,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( Some(ty) => { let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; // Typeck doesn't expect erased regions to be returned from `type_of`. - // This is a heuristic approach. If the scope has region paramters, + // This is a heuristic approach. If the scope has region parameters, // we should change fn_sig's lifetime from `ReErased` to `ReError`, // otherwise to `ReStatic`. let has_region_params = generics.params.iter().any(|param| match param.kind { @@ -1697,8 +1698,6 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option, 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(_) | Node::TraitItem(_) @@ -228,16 +235,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // inherit the generics of the item. Some(parent.to_def_id()) } - ItemKind::Fn(sig, _, _) => { - // For a delegation item inherit generics from callee. - if let Some(sig_id) = sig.decl.opt_delegation_sig_id() - && let Some(generics) = - inherit_generics_for_delegation_item(tcx, def_id, sig_id) - { - return generics; - } - None - } _ => None, }, _ => None, diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index ec48c781c0e43..d62727e76b586 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -8,6 +8,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; use rustc_type_ir::Upcast; +use tracing::{debug, instrument}; use super::ItemCtxt; use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter}; diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 1bff91b1fac87..7243e85ce98d2 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::{self, GenericPredicates, ImplTraitInTraitData, Ty, TyCtxt use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; +use tracing::{debug, instrument, trace}; use crate::bounds::Bounds; use crate::collect::ItemCtxt; @@ -137,6 +138,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen 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_predicates_for_delegation_item(tcx, def_id, sig_id); + } + let mut is_trait = None; let mut is_default_impl_trait = None; @@ -163,16 +170,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen ItemKind::Trait(_, _, _, self_bounds, ..) | ItemKind::TraitAlias(_, self_bounds) => { is_trait = Some(self_bounds); } - - ItemKind::Fn(sig, _, _) => { - // For a delegation item inherit predicates from callee. - if let Some(sig_id) = sig.decl.opt_delegation_sig_id() - && let Some(predicates) = - inherit_predicates_for_delegation_item(tcx, def_id, sig_id) - { - return predicates; - } - } _ => {} } }; 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 cb203e04f0c65..b4cbd1f309c97 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -24,6 +24,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; +use tracing::{debug, debug_span, instrument}; use crate::errors; @@ -1190,23 +1191,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { (generics.span, "<'a>".to_owned()) }; - let lifetime_sugg = match lifetime_ref.suggestion_position() { - (hir::LifetimeSuggestionPosition::Normal, span) => { - (span, "'a".to_owned()) - } - (hir::LifetimeSuggestionPosition::Ampersand, span) => { - (span, "'a ".to_owned()) - } - (hir::LifetimeSuggestionPosition::ElidedPath, span) => { - (span, "<'a>".to_owned()) - } - (hir::LifetimeSuggestionPosition::ElidedPathArgument, span) => { - (span, "'a, ".to_owned()) - } - (hir::LifetimeSuggestionPosition::ObjectDefault, span) => { - (span, "+ 'a".to_owned()) - } - }; + let lifetime_sugg = lifetime_ref.suggestion("'a"); let suggestions = vec![lifetime_sugg, new_param_sugg]; diag.span_label( diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 96256b91b9f9d..5cb90e97eefe0 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -11,6 +11,7 @@ use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableEx use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; +use tracing::debug; use super::{bad_placeholder, ItemCtxt}; use crate::errors::TypeofReservedKeywordUsed; diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index c8b0f03a9290c..7f4a8208faaed 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -7,6 +7,7 @@ use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::DUMMY_SP; +use tracing::{debug, instrument, trace}; use crate::errors::{TaitForwardCompat, TaitForwardCompat2, UnconstrainedOpaqueType}; diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index edf6524566418..6a9ae0de1c1ab 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -4,6 +4,7 @@ use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitor}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; use rustc_type_ir::fold::TypeFoldable; +use tracing::debug; #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct Parameter(pub u32); diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index 20aaa43219f34..2c9f20b7840f3 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -1,3 +1,7 @@ +//! Support inheriting generic parameters and predicates for function delegation. +//! +//! For more information about delegation design, see the tracking issue #118212. + use std::assert_matches::debug_assert_matches; use rustc_data_structures::fx::FxHashMap; @@ -5,7 +9,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::ErrorGuaranteed; +use rustc_span::{ErrorGuaranteed, Span}; use rustc_type_ir::visit::TypeVisitableExt; type RemapTable = FxHashMap; @@ -76,127 +80,381 @@ fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> FnKind { } } +/// Given the current context(caller and callee `FnKind`), it specifies +/// the policy of predicates and generic parameters inheritance. +#[derive(Clone, Copy, Debug, PartialEq)] +enum InheritanceKind { + /// Copying all predicates and parameters, including those of the parent + /// container. + /// + /// Boolean value defines whether the `Self` parameter or `Self: Trait` + /// predicate are copied. It's always equal to `false` except when + /// delegating from a free function to a trait method. + /// + /// FIXME(fn_delegation): This often leads to type inference + /// errors. Support providing generic arguments or restrict use sites. + WithParent(bool), + /// The trait implementation should be compatible with the original trait. + /// Therefore, for trait implementations only the method's own parameters + /// and predicates are copied. + Own, +} + +struct GenericsBuilder<'tcx> { + tcx: TyCtxt<'tcx>, + sig_id: DefId, + parent: Option, + inh_kind: InheritanceKind, +} + +impl<'tcx> GenericsBuilder<'tcx> { + fn new(tcx: TyCtxt<'tcx>, sig_id: DefId) -> GenericsBuilder<'tcx> { + GenericsBuilder { tcx, sig_id, parent: None, inh_kind: InheritanceKind::WithParent(false) } + } + + fn with_parent(mut self, parent: DefId) -> Self { + self.parent = Some(parent); + self + } + + fn with_inheritance_kind(mut self, inh_kind: InheritanceKind) -> Self { + self.inh_kind = inh_kind; + self + } + + fn build(self) -> ty::Generics { + let mut own_params = vec![]; + + let sig_generics = self.tcx.generics_of(self.sig_id); + if let InheritanceKind::WithParent(has_self) = self.inh_kind + && let Some(parent_def_id) = sig_generics.parent + { + let sig_parent_generics = self.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()); + + // 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 param_def_id_to_index = + own_params.iter().map(|param| (param.def_id, param.index)).collect(); + + let (parent_count, has_self) = if let Some(def_id) = self.parent { + let parent_generics = self.tcx.generics_of(def_id); + let parent_kind = self.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; + } + } + + ty::Generics { + parent: self.parent, + parent_count, + own_params, + param_def_id_to_index, + has_self, + has_late_bound_regions: sig_generics.has_late_bound_regions, + host_effect_index: sig_generics.host_effect_index, + } + } +} + +struct PredicatesBuilder<'tcx> { + tcx: TyCtxt<'tcx>, + sig_id: DefId, + parent: Option, + inh_kind: InheritanceKind, + args: ty::GenericArgsRef<'tcx>, +} + +impl<'tcx> PredicatesBuilder<'tcx> { + fn new( + tcx: TyCtxt<'tcx>, + args: ty::GenericArgsRef<'tcx>, + sig_id: DefId, + ) -> PredicatesBuilder<'tcx> { + PredicatesBuilder { + tcx, + sig_id, + parent: None, + inh_kind: InheritanceKind::WithParent(false), + args, + } + } + + fn with_parent(mut self, parent: DefId) -> Self { + self.parent = Some(parent); + self + } + + fn with_inheritance_kind(mut self, inh_kind: InheritanceKind) -> Self { + self.inh_kind = inh_kind; + self + } + + fn build(self) -> ty::GenericPredicates<'tcx> { + struct PredicatesCollector<'tcx> { + tcx: TyCtxt<'tcx>, + preds: Vec<(ty::Clause<'tcx>, Span)>, + args: ty::GenericArgsRef<'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); + self + } + + fn with_preds( + mut self, + f: impl Fn(DefId) -> ty::GenericPredicates<'tcx> + Copy, + def_id: DefId, + ) -> Self { + let preds = f(def_id); + 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(self.tcx, self.args); + + // `explicit_predicates_of` is used here to avoid copying `Self: Trait` predicate. + // Note: `predicates_of` query can also add inferred outlives predicates, but that + // is not the case here as `sig_id` is either a trait or a function. + let preds = match self.inh_kind { + InheritanceKind::WithParent(false) => { + collector.with_preds(|def_id| self.tcx.explicit_predicates_of(def_id), self.sig_id) + } + InheritanceKind::WithParent(true) => { + collector.with_preds(|def_id| self.tcx.predicates_of(def_id), self.sig_id) + } + InheritanceKind::Own => { + collector.with_own_preds(|def_id| self.tcx.predicates_of(def_id), self.sig_id) + } + } + .preds; + + ty::GenericPredicates { + parent: self.parent, + predicates: self.tcx.arena.alloc_from_iter(preds), + // FIXME(fn_delegation): Support effects. + effects_min_tys: ty::List::empty(), + } + } +} + +struct GenericArgsBuilder<'tcx> { + tcx: TyCtxt<'tcx>, + remap_table: RemapTable, + sig_id: DefId, + def_id: LocalDefId, +} + +impl<'tcx> GenericArgsBuilder<'tcx> { + fn new(tcx: TyCtxt<'tcx>, sig_id: DefId, def_id: LocalDefId) -> GenericArgsBuilder<'tcx> { + GenericArgsBuilder { tcx, remap_table: FxHashMap::default(), sig_id, def_id } + } + + fn build_from_args(mut self, args: ty::GenericArgsRef<'tcx>) -> ty::GenericArgsRef<'tcx> { + let caller_generics = self.tcx.generics_of(self.def_id); + let callee_generics = self.tcx.generics_of(self.sig_id); + + for caller_param in &caller_generics.own_params { + let callee_index = + callee_generics.param_def_id_to_index(self.tcx, caller_param.def_id).unwrap(); + self.remap_table.insert(callee_index, caller_param.index); + } + + let mut folder = ParamIndexRemapper { tcx: self.tcx, remap_table: self.remap_table }; + args.fold_with(&mut folder) + } +} + fn create_generic_args<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, sig_id: DefId, ) -> ty::GenericArgsRef<'tcx> { - let caller_generics = tcx.generics_of(def_id); - let callee_generics = tcx.generics_of(sig_id); + let builder = GenericArgsBuilder::new(tcx, sig_id, def_id); let caller_kind = fn_kind(tcx, def_id.into()); let callee_kind = fn_kind(tcx, sig_id); - // FIXME(fn_delegation): Support generics on associated delegation items. - // Error will be reported in `check_constraints`. match (caller_kind, callee_kind) { - (FnKind::Free, _) => { - // 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(...) - // } - let mut remap_table = RemapTable::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 }; - ty::GenericArgs::identity_for_item(tcx, sig_id).fold_with(&mut folder) + (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); + builder.build_from_args(args) } - // FIXME(fn_delegation): Only `Self` param supported here. - (FnKind::AssocTraitImpl, FnKind::AssocTrait) - | (FnKind::AssocInherentImpl, FnKind::AssocTrait) => { + + (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).unwrap().trait_ref.instantiate_identity().args; + + let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id); + let method_args = tcx.mk_args_from_iter(trait_args.iter().skip(callee_generics.parent_count)); + let method_args = builder.build_from_args(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); - tcx.mk_args_from_iter(std::iter::once(generic_self_ty)) + + let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id); + let trait_args = builder.build_from_args(trait_args); + + let args = std::iter::once(generic_self_ty).chain(trait_args.iter().skip(1)); + tcx.mk_args_from_iter(args) } - _ => ty::GenericArgs::identity_for_item(tcx, sig_id), + + // For trait impl's `sig_id` is always equal to the corresponding trait method. + (FnKind::AssocTraitImpl, _) + | (_, FnKind::AssocTraitImpl) + // Delegation to inherent methods is not yet supported. + | (_, 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, -) -> Option { - // FIXME(fn_delegation): Support generics on associated delegation items. - // Error will be reported in `check_constraints`. - if fn_kind(tcx, def_id.into()) != FnKind::Free { - return None; - } +) -> ty::Generics { + let builder = GenericsBuilder::new(tcx, sig_id); - let mut own_params = vec![]; - - let callee_generics = tcx.generics_of(sig_id); - if let Some(parent_sig_id) = callee_generics.parent { - let parent_sig_generics = tcx.generics_of(parent_sig_id); - own_params.append(&mut parent_sig_generics.own_params.clone()); - } - own_params.append(&mut callee_generics.own_params.clone()); + 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) => builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build(), + + (FnKind::AssocTraitImpl, FnKind::AssocTrait) => { + builder + .with_parent(tcx.parent(def_id.into())) + .with_inheritance_kind(InheritanceKind::Own) + .build() + } - // Lifetimes go first. - own_params.sort_by_key(|key| key.kind.is_ty_or_const()); + (FnKind::AssocInherentImpl, FnKind::AssocTrait) + | (FnKind::AssocTrait, FnKind::AssocTrait) => { + builder + .with_parent(tcx.parent(def_id.into())) + .build() + } - for (idx, param) in own_params.iter_mut().enumerate() { - param.index = idx as u32; - // Default parameters are not inherited: they are not allowed - // in fn's. - if let ty::GenericParamDefKind::Type { has_default, .. } - | ty::GenericParamDefKind::Const { has_default, .. } = &mut param.kind - { - *has_default = false; + (FnKind::AssocInherentImpl, FnKind::Free) + | (FnKind::AssocTrait, FnKind::Free) => { + builder + .with_parent(tcx.parent(def_id.into())) + .build() } - } - let param_def_id_to_index = - own_params.iter().map(|param| (param.def_id, param.index)).collect(); - - Some(ty::Generics { - parent: None, - parent_count: 0, - own_params, - param_def_id_to_index, - has_self: false, - has_late_bound_regions: callee_generics.has_late_bound_regions, - host_effect_index: callee_generics.host_effect_index, - }) + // For trait impl's `sig_id` is always equal to the corresponding trait method. + (FnKind::AssocTraitImpl, _) + | (_, FnKind::AssocTraitImpl) + // Delegation to inherent methods is not yet supported. + | (_, FnKind::AssocInherentImpl) => unreachable!(), + } } pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, sig_id: DefId, -) -> Option> { - // FIXME(fn_delegation): Support generics on associated delegation items. - // Error will be reported in `check_constraints`. - if fn_kind(tcx, def_id.into()) != FnKind::Free { - return None; - } - - let callee_predicates = tcx.predicates_of(sig_id); +) -> ty::GenericPredicates<'tcx> { let args = create_generic_args(tcx, def_id, sig_id); + let builder = PredicatesBuilder::new(tcx, args, sig_id); - let mut preds = vec![]; - if let Some(parent_id) = callee_predicates.parent { - preds.extend(tcx.predicates_of(parent_id).instantiate_own(tcx, args)); - } - preds.extend(callee_predicates.instantiate_own(tcx, args)); + 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) => { + builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build() + } + + (FnKind::AssocTraitImpl, FnKind::AssocTrait) => { + builder + .with_parent(tcx.parent(def_id.into())) + .with_inheritance_kind(InheritanceKind::Own) + .build() + } + + (FnKind::AssocInherentImpl, FnKind::AssocTrait) + | (FnKind::AssocTrait, FnKind::AssocTrait) + | (FnKind::AssocInherentImpl, FnKind::Free) + | (FnKind::AssocTrait, FnKind::Free) => { + builder + .with_parent(tcx.parent(def_id.into())) + .build() + } - Some(ty::GenericPredicates { - parent: None, - predicates: tcx.arena.alloc_from_iter(preds), - effects_min_tys: ty::List::empty(), - }) + // For trait impl's `sig_id` is always equal to the corresponding trait method. + (FnKind::AssocTraitImpl, _) + | (_, FnKind::AssocTraitImpl) + // Delegation to inherent methods is not yet supported. + | (_, FnKind::AssocInherentImpl) => unreachable!(), + } } fn check_constraints<'tcx>( @@ -224,19 +482,6 @@ fn check_constraints<'tcx>( emit("recursive delegation is not supported yet"); } - if fn_kind(tcx, def_id.into()) != FnKind::Free { - let sig_generics = tcx.generics_of(sig_id); - let parent = tcx.parent(def_id.into()); - let parent_generics = tcx.generics_of(parent); - - let parent_has_self = parent_generics.has_self as usize; - let sig_has_self = sig_generics.has_self as usize; - - if sig_generics.count() > sig_has_self || parent_generics.count() > parent_has_self { - emit("early bound generics are not supported for associated delegation items"); - } - } - ret } 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 f8b2469dfea45..da89f5769d1fc 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 @@ -5,6 +5,7 @@ use rustc_errors::{pluralize, Applicability, Diag, Diagnostic, EmissionGuarantee use rustc_hir as hir; use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt}; use rustc_span::def_id::DefId; +use tracing::debug; use GenericArgsInfo::*; /// Handles the `wrong number of type / lifetime / ... arguments` family of error messages. 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 7f4c75d3a6a21..bffe68f9b745f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -13,6 +13,7 @@ use rustc_span::{sym, ErrorGuaranteed, Span, Symbol}; use rustc_trait_selection::traits; use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use smallvec::SmallVec; +use tracing::{debug, instrument}; use crate::bounds::Bounds; use crate::errors; @@ -528,7 +529,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Detect and reject early-bound & escaping late-bound generic params in the type of assoc const bindings. /// -/// FIXME(const_generics): This is a temporary and semi-artifical restriction until the +/// FIXME(const_generics): This is a temporary and semi-artificial restriction until the /// arrival of *generic const generics*[^1]. /// /// It might actually be possible that we can already support early-bound generic params diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 1b73cecd6664b..5150db7f51b19 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -72,8 +72,11 @@ fn is_valid_cmse_inputs<'tcx>( let mut span = None; let mut accum = 0u64; - for (index, arg_def) in fn_sig.inputs().iter().enumerate() { - let layout = tcx.layout_of(ParamEnv::reveal_all().and(*arg_def.skip_binder()))?; + // this type is only used for layout computation, which does not rely on regions + let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig); + + for (index, ty) in fn_sig.inputs().iter().enumerate() { + let layout = tcx.layout_of(ParamEnv::reveal_all().and(*ty))?; let align = layout.layout.align().abi.bytes(); let size = layout.layout.size().bytes(); @@ -98,7 +101,10 @@ fn is_valid_cmse_output<'tcx>( tcx: TyCtxt<'tcx>, fn_sig: ty::PolyFnSig<'tcx>, ) -> Result> { - let mut ret_ty = fn_sig.output().skip_binder(); + // this type is only used for layout computation, which does not rely on regions + let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig); + + let mut ret_ty = fn_sig.output(); let layout = tcx.layout_of(ParamEnv::reveal_all().and(ret_ty))?; let size = layout.layout.size().bytes(); 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 a59e9aa85fd7b..8d5f98c7372e1 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -11,6 +11,7 @@ use rustc_middle::ty::{ use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; use rustc_span::symbol::{kw, sym}; use smallvec::SmallVec; +use tracing::{debug, instrument}; use super::{HirTyLowerer, IsMethodCall}; use crate::errors::wrong_number_of_generic_args::{GenericArgsInfo, WrongNumberOfGenericArgs}; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index 7be45463f1512..149bc6d26984d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -133,9 +133,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return; }; let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &impl_trait_name); - if sugg.is_empty() { - return; - }; diag.multipart_suggestion( format!( "alternatively use a blanket implementation to implement `{of_trait_name}` for \ @@ -170,6 +167,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; // FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0` // and suggest `Trait0`. + // Functions are found in three different contexts. + // 1. Independent functions + // 2. Functions inside trait blocks + // 3. Functions inside impl blocks let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => { (sig, generics, None) @@ -180,6 +181,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { owner_id, .. }) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))), + hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(sig, _), + generics, + owner_id, + .. + }) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))), _ => return false, }; let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { @@ -187,6 +194,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())]; let mut is_downgradable = true; + + // Check if trait object is safe for suggesting dynamic dispatch. let is_object_safe = match self_ty.kind { hir::TyKind::TraitObject(objects, ..) => { objects.iter().all(|(o, _)| match o.trait_ref.path.res { @@ -202,8 +211,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } _ => false, }; + + let borrowed = matches!( + tcx.parent_hir_node(self_ty.hir_id), + hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. }) + ); + + // Suggestions for function return type. if let hir::FnRetTy::Return(ty) = sig.decl.output - && ty.hir_id == self_ty.hir_id + && ty.peel_refs().hir_id == self_ty.hir_id { let pre = if !is_object_safe { format!("`{trait_name}` is not object safe, ") @@ -214,14 +230,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \ single underlying type", ); + diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); + + // Suggest `Box` for return type if is_object_safe { - diag.multipart_suggestion_verbose( - "alternatively, you can return an owned trait object", + // If the return type is `&Trait`, we don't want + // the ampersand to be displayed in the `Box` + // suggestion. + let suggestion = if borrowed { + vec![(ty.span, format!("Box"))] + } else { vec![ (ty.span.shrink_to_lo(), "Box".to_string()), - ], + ] + }; + + diag.multipart_suggestion_verbose( + "alternatively, you can return an owned trait object", + suggestion, Applicability::MachineApplicable, ); } else if is_downgradable { @@ -230,24 +258,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } return true; } + + // Suggestions for function parameters. for ty in sig.decl.inputs { - if ty.hir_id != self_ty.hir_id { + if ty.peel_refs().hir_id != self_ty.hir_id { continue; } let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name); - if !sugg.is_empty() { - diag.multipart_suggestion_verbose( - format!("use a new generic type parameter, constrained by `{trait_name}`"), - sugg, - Applicability::MachineApplicable, - ); - diag.multipart_suggestion_verbose( - "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, - Applicability::MachineApplicable, - ); - } + diag.multipart_suggestion_verbose( + format!("use a new generic type parameter, constrained by `{trait_name}`"), + sugg, + Applicability::MachineApplicable, + ); + diag.multipart_suggestion_verbose( + "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, + Applicability::MachineApplicable, + ); if !is_object_safe { diag.note(format!("`{trait_name}` it is not object safe, so it can't be `dyn`")); if is_downgradable { @@ -255,14 +283,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { diag.downgrade_to_delayed_bug(); } } else { + // No ampersand in suggestion if it's borrowed already + let (dyn_str, paren_dyn_str) = + if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; + let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind { // There are more than one trait bound, we need surrounding parentheses. vec![ - (self_ty.span.shrink_to_lo(), "&(dyn ".to_string()), + (self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()), (self_ty.span.shrink_to_hi(), ")".to_string()), ] } else { - vec![(self_ty.span.shrink_to_lo(), "&dyn ".to_string())] + vec![(self_ty.span.shrink_to_lo(), dyn_str.to_string())] }; diag.multipart_suggestion_verbose( format!( 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 98e1297ed0694..ac5bd825b18dd 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -50,6 +50,7 @@ use rustc_target::spec::abi; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; +use tracing::{debug, debug_span, instrument}; use crate::bounds::Bounds; use crate::errors::{AmbiguousLifetimeBound, WildPatTy}; @@ -985,7 +986,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// We **don't** support paths whose self type is an arbitrary type like `Struct::Ty` where /// struct `Struct` impls an in-scope trait that defines an associated type called `Ty`. /// For the latter case, we report ambiguity. - /// While desirable to support, the implemention would be non-trivial. Tracked in [#22519]. + /// While desirable to support, the implementation would be non-trivial. Tracked in [#22519]. /// /// At the time of writing, *inherent associated types* are also resolved here. This however /// is [problematic][iat]. A proper implementation would be as non-trivial as the one diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs index 52e167379b5dd..8853886371033 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs @@ -14,6 +14,7 @@ use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::traits::report_object_safety_error; use rustc_trait_selection::traits::{self, hir_ty_lowering_object_safety_violations}; use smallvec::{smallvec, SmallVec}; +use tracing::{debug, instrument}; use super::HirTyLowerer; use crate::bounds::Bounds; diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 3ecf61501f6d3..2fa4ca680737d 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -8,6 +8,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::LocalDefId; use rustc_trait_selection::traits::{self, ObligationCtxt}; +use tracing::debug; use crate::collect::ItemCtxt; diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index f44a78bac4de2..147646930dde3 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -77,6 +77,7 @@ use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, translate_args_with_cause, wf, ObligationCtxt}; +use tracing::{debug, instrument}; use crate::errors::GenericArgsOnOverriddenImpl; use crate::{constrained_generic_params as cgp, errors}; diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 891e4fcd019c0..1481a4dd141b2 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -75,9 +75,6 @@ This API is completely unstable and subject to change. #![warn(unreachable_pub)] // tidy-alphabetical-end -#[macro_use] -extern crate tracing; - // These are used by Clippy. pub mod check; diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index 454c20d3e6485..c2377b4781c28 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -3,6 +3,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_middle::ty::{self, GenericArg, GenericArgKind, Ty, TyCtxt}; use rustc_span::Span; +use tracing::debug; use super::explicit::ExplicitPredicatesMap; use super::utils::*; diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 4fb7a02f8c937..415b23d812b5f 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -8,6 +8,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; +use tracing::{debug, instrument}; use super::terms::VarianceTerm::*; use super::terms::*; diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index e8e2caf7e62a5..12bb9a3f9e0f3 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -12,6 +12,7 @@ use rustc_middle::span_bug; use rustc_middle::ty::{ self, CrateVariancesMap, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, }; +use tracing::{debug, instrument}; /// Defines the `TermsContext` basically houses an arena where we can /// allocate terms. diff --git a/compiler/rustc_hir_analysis/src/variance/solve.rs b/compiler/rustc_hir_analysis/src/variance/solve.rs index 4f1bac17e71be..d0bdca8677922 100644 --- a/compiler/rustc_hir_analysis/src/variance/solve.rs +++ b/compiler/rustc_hir_analysis/src/variance/solve.rs @@ -7,6 +7,7 @@ use rustc_hir::def_id::DefIdMap; use rustc_middle::ty; +use tracing::debug; use super::constraints::*; use super::terms::VarianceTerm::*; diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index 597699b37b1c5..cf38957bf24aa 100644 --- a/compiler/rustc_hir_analysis/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs @@ -15,6 +15,7 @@ use rustc_arena::DroplessArena; use rustc_hir::def::DefKind; use rustc_hir::def_id::{LocalDefId, LocalDefIdMap}; use rustc_middle::ty::{self, TyCtxt}; +use tracing::debug; use self::VarianceTerm::*; diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 7427fb147166f..afaa4a1ac6d8b 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -8,6 +8,7 @@ use rustc_span::Span; use rustc_trait_selection::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, }; +use tracing::{debug, instrument}; use crate::coercion::{AsCoercionSite, CoerceMany}; use crate::{Diverges, Expectation, FnCtxt, Needs}; diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index fc08b872efc2e..9863d0364498e 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -20,6 +20,7 @@ use rustc_target::spec::abi; use rustc_trait_selection::error_reporting::traits::DefIdOrName; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use tracing::{debug, instrument, trace}; use super::method::probe::ProbeScope; use super::method::MethodCallee; @@ -502,18 +503,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig = self.instantiate_binder_with_fresh_vars(call_expr.span, infer::FnCall, fn_sig); let fn_sig = self.normalize(call_expr.span, fn_sig); - // Call the generic checker. - let expected_arg_tys = self.expected_inputs_for_expected_output( - call_expr.span, - expected, - fn_sig.output(), - fn_sig.inputs(), - ); self.check_argument_types( call_expr.span, call_expr, fn_sig.inputs(), - expected_arg_tys, + fn_sig.output(), + expected, arg_exprs, fn_sig.c_variadic, TupleArgumentsFlag::DontTupleArguments, @@ -865,19 +860,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // don't know the full details yet (`Fn` vs `FnMut` etc), but we // do know the types expected for each argument and the return // type. - - let expected_arg_tys = self.expected_inputs_for_expected_output( - call_expr.span, - expected, - fn_sig.output(), - fn_sig.inputs(), - ); - self.check_argument_types( call_expr.span, call_expr, fn_sig.inputs(), - expected_arg_tys, + fn_sig.output(), + expected, arg_exprs, fn_sig.c_variadic, TupleArgumentsFlag::TupleArguments, diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 3a309d2ec0b4a..1e1e007862e1c 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -44,6 +44,7 @@ use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; +use tracing::{debug, instrument}; use super::FnCtxt; use crate::{errors, type_error_struct}; @@ -649,7 +650,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // cannot distinguish. This would cause us to erroneously discard a cast which will // lead to a borrowck error like #113257. // We still did a coercion above to unify inference variables for `ptr as _` casts. - // This does cause us to miss some trivial casts in the trival cast lint. + // This does cause us to miss some trivial casts in the trivial cast lint. debug!(" -> PointerCast"); } else { self.trivial_cast_lint(fcx); diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index cd357e4a7adb5..9346ff2d6ef6f 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -12,6 +12,7 @@ use rustc_span::def_id::LocalDefId; use rustc_span::symbol::sym; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; +use tracing::{debug, instrument}; use crate::coercion::CoerceMany; use crate::gather_locals::GatherLocalsVisitor; diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 6b813dc64cec2..5117eef9ed8f5 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -19,6 +19,7 @@ use rustc_target::spec::abi::Abi; use rustc_trait_selection::error_reporting::traits::ArgKind; use rustc_trait_selection::traits; use rustc_type_ir::ClosureKind; +use tracing::{debug, instrument, trace}; use super::{check_fn, CoroutineTypes, Expectation, FnCtxt}; diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 54ddff9886922..d97c590bd4109 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -66,6 +66,7 @@ use rustc_trait_selection::traits::{ self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt, }; use smallvec::{smallvec, SmallVec}; +use tracing::{debug, instrument}; use crate::errors::SuggestBoxingForReturnImplTrait; use crate::FnCtxt; @@ -850,8 +851,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). - // FIXME(struct_target_features): should this be true also for functions that inherit - // target features from structs? if b_hdr.safety == hir::Safety::Safe && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 89f27e807749e..0da299f017942 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -13,6 +13,7 @@ use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCause; +use tracing::instrument; use super::method::probe; use crate::FnCtxt; diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index dd33b947b0d06..9bad5633b69dc 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -34,6 +34,7 @@ use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt}; use smallvec::SmallVec; +use tracing::{debug, instrument, trace}; use {rustc_ast as ast, rustc_hir as hir}; use crate::coercion::{CoerceMany, DynamicCoerceMany}; @@ -446,7 +447,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // this time with enough precision to check that the value // whose address was taken can actually be made to live as long // as it needs to live. - let region = self.next_region_var(infer::AddrOfRegion(expr.span)); + let region = self.next_region_var(infer::BorrowRegion(expr.span)); Ty::new_ref(self.tcx, region, ty, mutbl) } } @@ -1672,15 +1673,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let tcx = self.tcx; - let expected_inputs = - self.expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty]); - let adt_ty_hint = if let Some(expected_inputs) = expected_inputs { - expected_inputs.get(0).cloned().unwrap_or(adt_ty) - } else { - adt_ty - }; - // re-link the regions that EIfEO can erase. - self.demand_eqtype(span, adt_ty_hint, adt_ty); + let adt_ty = self.resolve_vars_with_obligations(adt_ty); + let adt_ty_hint = expected.only_has_type(self).and_then(|expected| { + self.fudge_inference_if_ok(|| { + let ocx = ObligationCtxt::new(self); + ocx.sup(&self.misc(span), self.param_env, expected, adt_ty)?; + if !ocx.select_where_possible().is_empty() { + return Err(TypeError::Mismatch); + } + Ok(self.resolve_vars_if_possible(adt_ty)) + }) + .ok() + }); + if let Some(adt_ty_hint) = adt_ty_hint { + // re-link the variables that the fudging above can create. + self.demand_eqtype(span, adt_ty_hint, adt_ty); + } let ty::Adt(adt, args) = adt_ty.kind() else { span_bug!(span, "non-ADT passed to check_expr_struct_fields"); diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 548d5a7cc4ccd..da8c0ad3a30e2 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -26,6 +26,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::{ErrorGuaranteed, Span}; use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; use rustc_trait_selection::infer::InferCtxtExt; +use tracing::{debug, trace}; use ty::BorrowKind::ImmBorrow; use crate::fn_ctxt::FnCtxt; diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index b1dc19b377746..79b02a7f04593 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -14,6 +14,7 @@ use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; +use tracing::debug; use crate::{errors, FnCtxt, TypeckRootCtxt}; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 2d205d1ede9cd..19f7950287f93 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -20,7 +20,6 @@ use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryRespons use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{ @@ -36,8 +35,9 @@ use rustc_span::Span; use rustc_target::abi::FieldIdx; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::traits::{ - self, NormalizeExt, ObligationCauseCode, ObligationCtxt, StructurallyNormalizeExt, + self, NormalizeExt, ObligationCauseCode, StructurallyNormalizeExt, }; +use tracing::{debug, instrument}; use crate::callee::{self, DeferredCallResolution}; use crate::errors::{self, CtorIsPrivate}; @@ -688,42 +688,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { vec![ty_error; len] } - /// Unifies the output type with the expected type early, for more coercions - /// and forward type information on the input expressions. - #[instrument(skip(self, call_span), level = "debug")] - pub(crate) fn expected_inputs_for_expected_output( - &self, - call_span: Span, - expected_ret: Expectation<'tcx>, - formal_ret: Ty<'tcx>, - formal_args: &[Ty<'tcx>], - ) -> Option>> { - let formal_ret = self.resolve_vars_with_obligations(formal_ret); - let ret_ty = expected_ret.only_has_type(self)?; - - let expect_args = self - .fudge_inference_if_ok(|| { - let ocx = ObligationCtxt::new(self); - - // Attempt to apply a subtyping relationship between the formal - // return type (likely containing type variables if the function - // is polymorphic) and the expected return type. - // No argument expectations are produced if unification fails. - let origin = self.misc(call_span); - ocx.sup(&origin, self.param_env, ret_ty, formal_ret)?; - if !ocx.select_where_possible().is_empty() { - return Err(TypeError::Mismatch); - } - - // Record all the argument types, with the args - // produced from the above subtyping unification. - Ok(Some(formal_args.iter().map(|&ty| self.resolve_vars_if_possible(ty)).collect())) - }) - .unwrap_or_default(); - debug!(?formal_args, ?formal_ret, ?expect_args, ?expected_ret); - expect_args - } - pub(crate) fn resolve_lang_item_path( &self, lang_item: hir::LangItem, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 5333982c42029..bdf84f332166d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -17,6 +17,7 @@ use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_index::IndexVec; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TypeTrace}; use rustc_middle::ty::adjustment::AllowTwoPhase; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -25,7 +26,8 @@ use rustc_span::symbol::{kw, Ident}; use rustc_span::{sym, Span, DUMMY_SP}; use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt}; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; +use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext}; +use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; use crate::coercion::CoerceMany; @@ -123,6 +125,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Err(guar) = has_error { let err_inputs = self.err_args(args_no_rcvr.len(), guar); + let err_output = Ty::new_error(self.tcx, guar); let err_inputs = match tuple_arguments { DontTupleArguments => err_inputs, @@ -133,28 +136,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sp, expr, &err_inputs, - None, + err_output, + NoExpectation, args_no_rcvr, false, tuple_arguments, method.ok().map(|method| method.def_id), ); - return Ty::new_error(self.tcx, guar); + return err_output; } let method = method.unwrap(); - // HACK(eddyb) ignore self in the definition (see above). - let expected_input_tys = self.expected_inputs_for_expected_output( - sp, - expected, - method.sig.output(), - &method.sig.inputs()[1..], - ); self.check_argument_types( sp, expr, &method.sig.inputs()[1..], - expected_input_tys, + method.sig.output(), + expected, args_no_rcvr, method.sig.c_variadic, tuple_arguments, @@ -174,8 +172,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, // Types (as defined in the *signature* of the target function) formal_input_tys: &[Ty<'tcx>], - // More specific expected types, after unifying with caller output types - expected_input_tys: Option>>, + formal_output: Ty<'tcx>, + // Expected output from the parent expression or statement + expectation: Expectation<'tcx>, // The expressions for each provided argument provided_args: &'tcx [hir::Expr<'tcx>], // Whether the function is variadic, for example when imported from C @@ -209,6 +208,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } + // First, let's unify the formal method signature with the expectation eagerly. + // We use this to guide coercion inference; it's output is "fudged" which means + // any remaining type variables are assigned to new, unrelated variables. This + // is because the inference guidance here is only speculative. + let formal_output = self.resolve_vars_with_obligations(formal_output); + let expected_input_tys: Option> = expectation + .only_has_type(self) + .and_then(|expected_output| { + self.fudge_inference_if_ok(|| { + let ocx = ObligationCtxt::new(self); + + // Attempt to apply a subtyping relationship between the formal + // return type (likely containing type variables if the function + // is polymorphic) and the expected return type. + // No argument expectations are produced if unification fails. + let origin = self.misc(call_span); + ocx.sup(&origin, self.param_env, expected_output, formal_output)?; + if !ocx.select_where_possible().is_empty() { + return Err(TypeError::Mismatch); + } + + // Record all the argument types, with the args + // produced from the above subtyping unification. + Ok(Some( + formal_input_tys + .iter() + .map(|&ty| self.resolve_vars_if_possible(ty)) + .collect(), + )) + }) + .ok() + }) + .unwrap_or_default(); + let mut err_code = E0061; // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here @@ -291,21 +324,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let coerce_error = self.coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None).err(); - if coerce_error.is_some() { return Compatibility::Incompatible(coerce_error); } - // 3. Check if the formal type is a supertype of the checked one - // and register any such obligations for future type checks - let supertype_error = self.at(&self.misc(provided_arg.span), self.param_env).sup( + // 3. Check if the formal type is actually equal to the checked one + // and register any such obligations for future type checks. + let formal_ty_error = self.at(&self.misc(provided_arg.span), self.param_env).eq( DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty, ); // If neither check failed, the types are compatible - match supertype_error { + match formal_ty_error { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); Compatibility::Compatible diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index be4db2934b7b1..2dcab9ed0044e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -7,6 +7,7 @@ use rustc_span::Span; use rustc_trait_selection::solve::inspect::{ InspectConfig, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor, }; +use tracing::{debug, instrument, trace}; use crate::FnCtxt; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 031aa6159d2bc..4454703645e79 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -31,6 +31,7 @@ use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use tracing::{debug, instrument}; use super::FnCtxt; use crate::fn_ctxt::rustc_span::BytePos; diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 0fd450e869aa3..4ea22884cf376 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -5,6 +5,7 @@ use rustc_infer::traits::ObligationCauseCode; use rustc_middle::ty::{Ty, UserType}; use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use tracing::debug; use crate::FnCtxt; diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 62aa29e673def..f39d83a2a6f79 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -7,6 +7,7 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_target::abi::{Pointer, VariantIdx}; +use tracing::trace; use super::FnCtxt; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 8e6484f1e2934..caf09c1177fd3 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -11,9 +11,6 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end -#[macro_use] -extern crate tracing; - mod _match; mod autoderef; mod callee; @@ -60,6 +57,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config; use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use tracing::{debug, instrument}; use typeck_root_ctxt::TypeckRootCtxt; use crate::check::check_fn; diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 2c3cfcf3cbd63..10a22eee7b74a 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -22,6 +22,7 @@ use rustc_middle::ty::{ use rustc_middle::{bug, span_bug}; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::traits; +use tracing::debug; use super::{probe, MethodCallee}; use crate::{callee, FnCtxt}; @@ -248,7 +249,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } /// Returns a set of generic parameters for the method *receiver* where all type and region - /// parameters are instantiated with fresh variables. This generic paramters does not include any + /// parameters are instantiated with fresh variables. This generic parameters does not include any /// parameters declared on the method itself. /// /// Note that this generic parameters may include late-bound regions from the impl level. If so, @@ -374,7 +375,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { IsMethodCall::Yes, ); - // Create generic paramters for early-bound lifetime parameters, + // Create generic parameters for early-bound lifetime parameters, // combining parameters from the type and those from the method. assert_eq!(generics.parent_count, parent_args.len()); @@ -545,7 +546,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { debug!("instantiate_method_sig(pick={:?}, all_args={:?})", pick, all_args); // Instantiate the bounds on the method with the - // type/early-bound-regions instatiations performed. There can + // type/early-bound-regions instantiations performed. There can // be no late-bound regions appearing here. let def_id = pick.item.def_id; let method_predicates = self.tcx.predicates_of(def_id).instantiate(self.tcx, all_args); diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 39307a29dad1f..7019b718b16e8 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -22,6 +22,7 @@ use rustc_span::symbol::Ident; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{self, NormalizeExt}; +use tracing::{debug, instrument}; use self::probe::{IsSuggestion, ProbeScope}; pub(crate) use self::MethodError::*; 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 0a4c3dc8af96f..ac5e1040803e0 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -12,6 +12,7 @@ use rustc_span::symbol::kw::{Empty, Underscore}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; +use tracing::debug; use crate::method::probe::{self, Pick}; use crate::FnCtxt; @@ -62,8 +63,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Instead, the problem is that the array-into_iter hack will no longer // apply in Rust 2021. (ARRAY_INTO_ITER, "2021") - } else if self_ty.is_box() - && self_ty.boxed_ty().is_slice() + } else if self_ty.boxed_ty().is_some_and(Ty::is_slice) && !span.at_least_rust_2024() { // In this case, it wasn't really a prelude addition that was the problem. diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 48e33c81b852b..3ba3429cbb331 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -36,6 +36,7 @@ use rustc_trait_selection::traits::query::method_autoderef::{ use rustc_trait_selection::traits::query::CanonicalTyGoal; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt}; use smallvec::{smallvec, SmallVec}; +use tracing::{debug, instrument}; use self::CandidateKind::*; pub(crate) use self::PickKind::*; @@ -402,7 +403,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mode, })); } else if bad_ty.reached_raw_pointer - && !self.tcx.features().arbitrary_self_types + && !self.tcx.features().arbitrary_self_types_pointers && !self.tcx.sess.at_least_rust_2018() { // this case used to be allowed by the compiler, @@ -714,7 +715,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>) { - let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsCandidateKey) else { + let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::InstantiateWithInfer) else { bug!("unexpected incoherent type: {:?}", self_ty) }; for &impl_def_id in self.tcx.incoherent_impls(simp).into_iter().flatten() { @@ -1484,8 +1485,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // Some trait methods are excluded for boxed slices before 2024. // (`boxed_slice.into_iter()` wants a slice iterator for compatibility.) - if self_ty.is_box() - && self_ty.boxed_ty().is_slice() + if self_ty.boxed_ty().is_some_and(Ty::is_slice) && !method_name.span.at_least_rust_2024() { let trait_def = self.tcx.trait_def(poly_trait_ref.def_id()); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index b78bb8cb98db7..14ad5830111b4 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -37,6 +37,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _ use rustc_trait_selection::traits::{ supertraits, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, }; +use tracing::{debug, info, instrument}; use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; @@ -2234,8 +2235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let target_ty = self .autoderef(sugg_span, rcvr_ty) .find(|(rcvr_ty, _)| { - DeepRejectCtxt::new(self.tcx, TreatParams::ForLookup) - .types_may_unify(*rcvr_ty, impl_ty) + DeepRejectCtxt::relate_rigid_infer(self.tcx).types_may_unify(*rcvr_ty, impl_ty) }) .map_or(impl_ty, |(ty, _)| ty) .peel_refs(); @@ -2497,7 +2497,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .into_iter() .any(|info| self.associated_value(info.def_id, item_name).is_some()); let found_assoc = |ty: Ty<'tcx>| { - simplify_type(tcx, ty, TreatParams::AsCandidateKey) + simplify_type(tcx, ty, TreatParams::InstantiateWithInfer) .and_then(|simp| { tcx.incoherent_impls(simp) .into_iter() @@ -3962,7 +3962,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // cases where a positive bound implies a negative impl. (candidates, Vec::new()) } else if let Some(simp_rcvr_ty) = - simplify_type(self.tcx, rcvr_ty, TreatParams::ForLookup) + simplify_type(self.tcx, rcvr_ty, TreatParams::AsRigid) { let mut potential_candidates = Vec::new(); let mut explicitly_negative = Vec::new(); @@ -3980,7 +3980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .any(|header| { let imp = header.trait_ref.instantiate_identity(); let imp_simp = - simplify_type(self.tcx, imp.self_ty(), TreatParams::ForLookup); + simplify_type(self.tcx, imp.self_ty(), TreatParams::AsRigid); imp_simp.is_some_and(|s| s == simp_rcvr_ty) }) { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index fb0d30d5b0ef2..c74270fc0e6a4 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -17,6 +17,7 @@ use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{FulfillmentError, ObligationCtxt}; use rustc_type_ir::TyKind::*; +use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; use super::method::MethodCallee; diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 206de455cd575..25f9340eeb717 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -24,6 +24,7 @@ use rustc_span::{BytePos, Span, DUMMY_SP}; use rustc_target::abi::FieldIdx; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; +use tracing::{debug, instrument, trace}; use ty::VariantDef; use super::report_unexpected_variant_res; diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index bb22727258423..e7f47ee56c9b2 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -9,6 +9,7 @@ use rustc_middle::ty::adjustment::{ use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; +use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; use crate::method::MethodCallee; diff --git a/compiler/rustc_hir_typeck/src/rvalue_scopes.rs b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs index fb0fe23be65fc..d65aaef4e8b3f 100644 --- a/compiler/rustc_hir_typeck/src/rvalue_scopes.rs +++ b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs @@ -4,6 +4,7 @@ use rustc_hir as hir; use rustc_middle::bug; use rustc_middle::middle::region::{RvalueCandidateType, Scope, ScopeTree}; use rustc_middle::ty::RvalueScopes; +use tracing::debug; use super::FnCtxt; diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index e6b8da3e5d6ba..52fa30a352f84 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -15,6 +15,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ self, FulfillmentError, PredicateObligation, TraitEngine, TraitEngineExt as _, }; +use tracing::{debug, instrument}; use super::callee::DeferredCallResolution; diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 5350affb3bc48..7688a63a30a45 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -39,7 +39,6 @@ use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirId; -use rustc_infer::infer::UpvarRegion; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind}; use rustc_middle::mir::FakeReadCause; use rustc_middle::traits::ObligationCauseCode; @@ -52,6 +51,7 @@ use rustc_session::lint; use rustc_span::{sym, BytePos, Pos, Span, Symbol}; use rustc_target::abi::FIRST_VARIANT; use rustc_trait_selection::infer::InferCtxtExt; +use tracing::{debug, instrument}; use super::FnCtxt; use crate::expr_use_visitor as euv; @@ -227,7 +227,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // coroutine bodies can't borrow from their parent closure. To fix this, // we force the inner coroutine to also be `move`. This only matters for // coroutine-closures that are `move` since otherwise they themselves will - // be borrowing from the outer environment, so there's no self-borrows occuring. + // be borrowing from the outer environment, so there's no self-borrows occurring. if let UpvarArgs::Coroutine(..) = args && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) = self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind") @@ -424,7 +424,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx, upvar_ty, capture, - if needs_ref { Some(closure_env_region) } else { child_capture.region }, + if needs_ref { + closure_env_region + } else { + self.tcx.lifetimes.re_erased + }, ); }, ), @@ -586,7 +590,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!(?captured_place.place, ?upvar_ty, ?capture, ?captured_place.mutability); - apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture, captured_place.region) + apply_capture_kind_on_capture_ty( + self.tcx, + upvar_ty, + capture, + self.tcx.lifetimes.re_erased, + ) }) .collect() } @@ -774,13 +783,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(min_cap_list) = root_var_min_capture_list.get_mut(&var_hir_id) else { let mutability = self.determine_capture_mutability(&typeck_results, &place); - let min_cap_list = vec![ty::CapturedPlace { - var_ident, - place, - info: capture_info, - mutability, - region: None, - }]; + let min_cap_list = + vec![ty::CapturedPlace { var_ident, place, info: capture_info, mutability }]; root_var_min_capture_list.insert(var_hir_id, min_cap_list); continue; }; @@ -873,34 +877,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Only need to insert when we don't have an ancestor in the existing min capture list if !ancestor_found { let mutability = self.determine_capture_mutability(&typeck_results, &place); - let captured_place = ty::CapturedPlace { - var_ident, - place, - info: updated_capture_info, - mutability, - region: None, - }; + let captured_place = + ty::CapturedPlace { var_ident, place, info: updated_capture_info, mutability }; min_cap_list.push(captured_place); } } - // For each capture that is determined to be captured by ref, add region info. - for (_, captures) in &mut root_var_min_capture_list { - for capture in captures { - match capture.info.capture_kind { - ty::UpvarCapture::ByRef(_) => { - let PlaceBase::Upvar(upvar_id) = capture.place.base else { - bug!("expected upvar") - }; - let origin = UpvarRegion(upvar_id, closure_span); - let upvar_region = self.next_region_var(origin); - capture.region = Some(upvar_region); - } - _ => (), - } - } - } - debug!( "For closure={:?}, min_captures before sorting={:?}", closure_def_id, root_var_min_capture_list @@ -1194,7 +1176,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx, ty, max_capture_info.capture_kind, - Some(self.tcx.lifetimes.re_erased), + self.tcx.lifetimes.re_erased, ) } }; @@ -1216,7 +1198,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx, capture.place.ty(), capture.info.capture_kind, - Some(self.tcx.lifetimes.re_erased), + self.tcx.lifetimes.re_erased, ); // Checks if a capture implements any of the auto traits @@ -1934,13 +1916,11 @@ fn apply_capture_kind_on_capture_ty<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, capture_kind: UpvarCapture, - region: Option>, + region: ty::Region<'tcx>, ) -> Ty<'tcx> { match capture_kind { ty::UpvarCapture::ByValue => ty, - ty::UpvarCapture::ByRef(kind) => { - Ty::new_ref(tcx, region.unwrap(), ty, kind.to_mutbl_lossy()) - } + ty::UpvarCapture::ByRef(kind) => Ty::new_ref(tcx, region, ty, kind.to_mutbl_lossy()), } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 0853ed9b05bfb..c2555d2bb47dc 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -19,6 +19,7 @@ use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::solve; +use tracing::{debug, instrument}; use crate::FnCtxt; diff --git a/compiler/rustc_index/Cargo.toml b/compiler/rustc_index/Cargo.toml index 92ea3f278dc43..f7d18f84e3452 100644 --- a/compiler/rustc_index/Cargo.toml +++ b/compiler/rustc_index/Cargo.toml @@ -20,4 +20,5 @@ nightly = [ "dep:rustc_macros", "rustc_index_macros/nightly", ] +rustc_randomized_layouts = [] # tidy-alphabetical-end diff --git a/compiler/rustc_index/src/interval.rs b/compiler/rustc_index/src/interval.rs index be028feca605f..34f541a8cc639 100644 --- a/compiler/rustc_index/src/interval.rs +++ b/compiler/rustc_index/src/interval.rs @@ -17,8 +17,8 @@ mod tests; /// first value of the following element. #[derive(Debug, Clone)] pub struct IntervalSet { - // Start, end - map: SmallVec<[(u32, u32); 4]>, + // Start, end (both inclusive) + map: SmallVec<[(u32, u32); 2]>, domain: usize, _data: PhantomData, } diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index f773b5b46adae..52f354b8ecaff 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -33,8 +33,19 @@ pub use vec::IndexVec; /// /// #[macro_export] +#[cfg(not(feature = "rustc_randomized_layouts"))] macro_rules! static_assert_size { ($ty:ty, $size:expr) => { const _: [(); $size] = [(); ::std::mem::size_of::<$ty>()]; }; } + +#[macro_export] +#[cfg(feature = "rustc_randomized_layouts")] +macro_rules! static_assert_size { + ($ty:ty, $size:expr) => { + // no effect other than using the statements. + // struct sizes are not deterministic under randomized layouts + const _: (usize, usize) = ($size, ::std::mem::size_of::<$ty>()); + }; +} diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index db5139172b022..1930e357fc9cc 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::{ self, BoundVar, GenericArg, InferConst, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt, }; use smallvec::SmallVec; +use tracing::debug; use crate::infer::canonical::{ Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, OriginalQueryValues, @@ -50,7 +51,7 @@ impl<'tcx> InferCtxt<'tcx> { query_state, |tcx, param_env, query_state| { // FIXME(#118965): We don't canonicalize the static lifetimes that appear in the - // `param_env` beacause they are treated differently by trait selection. + // `param_env` because they are treated differently by trait selection. Canonicalizer::canonicalize( param_env, None, diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 85e3cfbcce1c1..95d7bb7eb3830 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -17,6 +17,7 @@ use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, BoundVar, GenericArg, GenericArgKind, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; +use tracing::{debug, instrument}; use crate::infer::canonical::instantiate::{instantiate_value, CanonicalExt}; use crate::infer::canonical::{ diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index 9a5674369daba..5452ba67497bc 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -5,6 +5,7 @@ use rustc_data_structures::transitive_relation::TransitiveRelation; use rustc_middle::ty::{Region, TyCtxt}; +use tracing::debug; /// Combines a `FreeRegionMap` and a `TyCtxt`. /// 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 e5533213400c6..aab64883bc50f 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -16,6 +16,7 @@ use rustc_middle::ty::{ }; use rustc_middle::{bug, span_bug}; use rustc_span::Span; +use tracing::{debug, instrument}; use super::outlives::test_type_match; use crate::infer::region_constraints::{ diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 5aa7f25968534..63a729b13dc40 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -44,6 +44,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Symbol; use rustc_span::Span; use snapshot::undo_log::InferCtxtUndoLogs; +use tracing::{debug, instrument}; use type_variable::TypeVariableOrigin; pub use BoundRegionConversionTime::*; pub use RegionVariableOrigin::*; @@ -457,8 +458,8 @@ pub enum RegionVariableOrigin { PatternRegion(Span), /// Regions created by `&` operator. - /// - AddrOfRegion(Span), + BorrowRegion(Span), + /// Regions created as part of an autoref of a method receiver. Autoref(Span), @@ -1740,7 +1741,7 @@ impl RegionVariableOrigin { match *self { MiscVariable(a) | PatternRegion(a) - | AddrOfRegion(a) + | BorrowRegion(a) | Autoref(a) | Coercion(a) | RegionParameterDefinition(a, ..) diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index e9726ee8ebf3b..5ceaaf1a3c4fe 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -11,6 +11,7 @@ use rustc_middle::ty::{ TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; use rustc_span::Span; +use tracing::{debug, instrument}; use crate::errors::OpaqueHiddenTypeDiag; use crate::infer::{InferCtxt, InferOk}; @@ -212,7 +213,7 @@ impl<'tcx> InferCtxt<'tcx> { /// ``` /// /// As indicating in the comments above, each of those references - /// is (in the compiler) basically generic paramters (`args`) + /// is (in the compiler) basically generic parameters (`args`) /// applied to the type of a suitable `def_id` (which identifies /// `Foo1` or `Foo2`). /// diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs index 7b4e546d8318f..4aa2ccab0e7bb 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/table.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs @@ -1,6 +1,7 @@ use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::bug; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty}; +use tracing::instrument; use super::{OpaqueTypeDecl, OpaqueTypeMap}; use crate::infer::snapshot::undo_log::{InferCtxtUndoLogs, UndoLog}; diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index cc763707c9cfb..77c711e57bb62 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -2,6 +2,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::transitive_relation::TransitiveRelationBuilder; use rustc_middle::bug; use rustc_middle::ty::{self, Region}; +use tracing::{debug, instrument}; use super::explicit_outlives_bounds; use crate::infer::free_regions::FreeRegionMap; diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index e4eefbc7a1a8b..f5c873b037552 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -2,6 +2,7 @@ use rustc_middle::traits::query::{NoSolution, OutlivesBound}; use rustc_middle::ty; +use tracing::instrument; use self::env::OutlivesEnvironment; use super::region_constraints::RegionConstraintData; diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 88b004adc9418..0c397350067c3 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -70,6 +70,7 @@ use rustc_middle::ty::{ use rustc_span::DUMMY_SP; use rustc_type_ir::outlives::{push_outlives_components, Component}; use smallvec::smallvec; +use tracing::{debug, instrument}; use super::env::OutlivesEnvironment; use crate::infer::outlives::env::RegionBoundPairs; diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index 835e34a3535bf..bfdd282d7e11d 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -3,6 +3,7 @@ use std::collections::hash_map::Entry; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use tracing::instrument; use crate::infer::region_constraints::VerifyIfEq; use crate::infer::relate::{self as relate, Relate, RelateResult, TypeRelation}; diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 1908e1e09c3d5..711f20444defc 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -3,6 +3,7 @@ use std::assert_matches::assert_matches; use rustc_middle::ty::{self, OutlivesPredicate, Ty, TyCtxt}; use rustc_type_ir::outlives::{compute_alias_components_recursive, Component}; use smallvec::smallvec; +use tracing::{debug, instrument, trace}; use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::region_constraints::VerifyIfEq; diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index 3d2a0a3356fd7..7913f0e340e09 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -4,6 +4,7 @@ use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_index::Idx; use rustc_middle::span_bug; use rustc_middle::ty::error::TypeError; +use tracing::{debug, instrument}; use super::*; use crate::infer::relate::RelateResult; diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 6ee95c73cfbd9..82f7668b2d2ec 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -13,6 +13,7 @@ use rustc_middle::infer::unify_key::{RegionVariableValue, RegionVidKey}; use rustc_middle::ty::{self, ReBound, ReStatic, ReVar, Region, RegionVid, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::Span; +use tracing::{debug, instrument}; use self::CombineMapType::*; use self::UndoLog::*; diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index 5751ce466d982..f2ec1dd3df74a 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -24,6 +24,7 @@ use rustc_middle::traits::solve::Goal; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, InferConst, IntType, Ty, TyCtxt, TypeVisitableExt, UintType, Upcast}; pub use rustc_next_trait_solver::relate::combine::*; +use tracing::debug; use super::glb::Glb; use super::lub::Lub; @@ -152,7 +153,7 @@ impl<'tcx> InferCtxt<'tcx> { // During coherence, opaque types should be treated as *possibly* // equal to any other type (except for possibly itself). This is an - // extremely heavy hammer, but can be relaxed in a fowards-compatible + // extremely heavy hammer, but can be relaxed in a forwards-compatible // way later. (&ty::Alias(ty::Opaque, _), _) | (_, &ty::Alias(ty::Opaque, _)) if self.intercrate => { relation.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]); diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 6fa10a95313c9..f2a511d7a886b 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -11,6 +11,7 @@ use rustc_middle::ty::{ self, AliasRelationDirection, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt, }; use rustc_span::Span; +use tracing::{debug, instrument, warn}; use super::{ PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation, @@ -119,7 +120,7 @@ impl<'tcx> InferCtxt<'tcx> { } else { // NOTE: The `instantiation_variance` is not the same variance as // used by the relation. When instantiating `b`, `target_is_expected` - // is flipped and the `instantion_variance` is also flipped. To + // is flipped and the `instantiation_variance` is also flipped. To // constrain the `generalized_ty` while using the original relation, // we therefore only have to flip the arguments. // diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs index 4f2cf2c43e7cc..7b12003643eb1 100644 --- a/compiler/rustc_infer/src/infer/relate/glb.rs +++ b/compiler/rustc_infer/src/infer/relate/glb.rs @@ -4,6 +4,7 @@ use rustc_middle::traits::solve::Goal; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::Span; +use tracing::{debug, instrument}; use super::combine::{CombineFields, PredicateEmittingRelation}; use super::lattice::{self, LatticeDir}; diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs index c808ab5e6dd10..ab6a19265f3fc 100644 --- a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs +++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs @@ -3,6 +3,7 @@ use rustc_middle::ty::fold::FnMutDelegate; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use tracing::{debug, instrument}; use super::RelateResult; use crate::infer::snapshot::CombinedSnapshot; diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index 1eafbb9acb330..1d3f45465d63d 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -19,6 +19,7 @@ use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::{self, Ty, TyVar}; +use tracing::instrument; use super::combine::PredicateEmittingRelation; use crate::infer::{DefineOpaqueTypes, InferCtxt}; diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs index 046e93b63e4cb..bf4d68b942863 100644 --- a/compiler/rustc_infer/src/infer/relate/lub.rs +++ b/compiler/rustc_infer/src/infer/relate/lub.rs @@ -4,6 +4,7 @@ use rustc_middle::traits::solve::Goal; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::Span; +use tracing::{debug, instrument}; use super::combine::{CombineFields, PredicateEmittingRelation}; use super::lattice::{self, LatticeDir}; diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index ec600c60b240b..be16fca45216a 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -4,6 +4,7 @@ use rustc_middle::ty::relate::{ }; use rustc_middle::ty::{self, Ty, TyCtxt, TyVar}; use rustc_span::Span; +use tracing::{debug, instrument}; use super::combine::CombineFields; use crate::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases}; diff --git a/compiler/rustc_infer/src/infer/snapshot/fudge.rs b/compiler/rustc_infer/src/infer/snapshot/fudge.rs index fdf55447f7983..8e330a084c65d 100644 --- a/compiler/rustc_infer/src/infer/snapshot/fudge.rs +++ b/compiler/rustc_infer/src/infer/snapshot/fudge.rs @@ -4,6 +4,7 @@ use rustc_data_structures::{snapshot_vec as sv, unify as ut}; use rustc_middle::infer::unify_key::{ConstVariableValue, ConstVidKey}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; +use tracing::instrument; use ut::UnifyKey; use crate::infer::type_variable::TypeVariableOrigin; diff --git a/compiler/rustc_infer/src/infer/snapshot/mod.rs b/compiler/rustc_infer/src/infer/snapshot/mod.rs index d76b9b00001a2..20db6583a2ba1 100644 --- a/compiler/rustc_infer/src/infer/snapshot/mod.rs +++ b/compiler/rustc_infer/src/infer/snapshot/mod.rs @@ -1,5 +1,6 @@ use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::ty; +use tracing::{debug, instrument}; use super::region_constraints::RegionSnapshot; use super::InferCtxt; diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 366af913ddc1c..9aa02f89d2a76 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -4,6 +4,7 @@ use rustc_data_structures::undo_log::{Rollback, UndoLogs}; use rustc_data_structures::{snapshot_vec as sv, unify as ut}; use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey, RegionVidKey}; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey}; +use tracing::debug; use crate::infer::{region_constraints, type_variable, InferCtxtInner}; use crate::traits; diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index f022b8ab637eb..7eb2c20e0d8e7 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -9,6 +9,7 @@ use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::ty::{self, Ty, TyVid}; use rustc_span::Span; +use tracing::debug; use crate::infer::InferCtxtUndoLogs; diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 94aa2ddd92fcc..051bba5851869 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -31,9 +31,6 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end -#[macro_use] -extern crate tracing; - mod errors; pub mod infer; pub mod traits; diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs index 3d4ec9e51db23..f6b05340952a9 100644 --- a/compiler/rustc_infer/src/traits/project.rs +++ b/compiler/rustc_infer/src/traits/project.rs @@ -4,6 +4,7 @@ use rustc_data_structures::snapshot_map::{self, SnapshotMapRef, SnapshotMapStora use rustc_data_structures::undo_log::Rollback; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty; +use tracing::{debug, info}; use super::PredicateObligation; use crate::infer::snapshot::undo_log::InferCtxtUndoLogs; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 779b98d073dab..c4a38047b5e3b 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -435,7 +435,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P escape_dep_filename(&file.prefer_local().to_string()) }; - // The entries will be used to declare dependencies beween files in a + // The entries will be used to declare dependencies between files in a // Makefile-like output, so the iteration order does not matter. #[allow(rustc::potential_query_instability)] let extra_tracked_files = diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 844d8ef02e06e..50422df8ee648 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -12,9 +12,10 @@ use rustc_session::config::{ CollapseMacroDebuginfo, CoverageLevel, CoverageOptions, DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs, FmtDebug, FunctionReturn, InliningThreshold, Input, InstrumentCoverage, InstrumentXRay, LinkSelfContained, - LinkerPluginLto, LocationDetail, LtoCli, NextSolverConfig, OomStrategy, Options, OutFileName, - OutputType, OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry, Polonius, - ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, + LinkerPluginLto, LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, OomStrategy, + Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, + PatchableFunctionEntry, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, + SymbolManglingVersion, WasiExecModel, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; @@ -705,7 +706,7 @@ fn test_unstable_options_tracking_hash() { untracked!(ls, vec!["all".to_owned()]); untracked!(macro_backtrace, true); untracked!(meta_stats, true); - untracked!(mir_include_spans, true); + untracked!(mir_include_spans, MirIncludeSpans::On); untracked!(nll_facts, true); untracked!(no_analysis, true); untracked!(no_leak_check, true); @@ -807,7 +808,7 @@ fn test_unstable_options_tracking_hash() { tracked!(mir_opt_level, Some(4)); tracked!(move_size_limit, Some(4096)); tracked!(mutable_noalias, false); - tracked!(next_solver, Some(NextSolverConfig { coherence: true, globally: false })); + tracked!(next_solver, NextSolverConfig { coherence: true, globally: true }); tracked!(no_generate_arange_section, true); tracked!(no_jump_tables, true); tracked!(no_link, true); diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 31fdd2d7cec70..60aab668cbaa6 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -91,6 +91,15 @@ pub enum TokenKind { /// tokens. UnknownPrefix, + /// An unknown prefix in a lifetime, like `'foo#`. + /// + /// Note that like above, only the `'` and prefix are included in the token + /// and not the separator. + UnknownPrefixLifetime, + + /// `'r#lt`, which in edition < 2021 is split into several tokens: `'r # lt`. + RawLifetime, + /// Similar to the above, but *always* an error on every edition. This is used /// for emoji identifier recovery, as those are not meant to be ever accepted. InvalidPrefix, @@ -677,9 +686,17 @@ impl Cursor<'_> { return Literal { kind, suffix_start }; } + if self.first() == 'r' && self.second() == '#' && is_id_start(self.third()) { + // Eat "r" and `#`, and identifier start characters. + self.bump(); + self.bump(); + self.bump(); + self.eat_while(is_id_continue); + return RawLifetime; + } + // Either a lifetime or a character literal with // length greater than 1. - let starts_with_number = self.first().is_ascii_digit(); // Skip the literal contents. @@ -688,15 +705,17 @@ impl Cursor<'_> { self.bump(); self.eat_while(is_id_continue); - // Check if after skipping literal contents we've met a closing - // single quote (which means that user attempted to create a - // string with single quotes). - if self.first() == '\'' { - self.bump(); - let kind = Char { terminated: true }; - Literal { kind, suffix_start: self.pos_within_token() } - } else { - Lifetime { starts_with_number } + match self.first() { + // Check if after skipping literal contents we've met a closing + // single quote (which means that user attempted to create a + // string with single quotes). + '\'' => { + self.bump(); + let kind = Char { terminated: true }; + Literal { kind, suffix_start: self.pos_within_token() } + } + '#' if !starts_with_number => UnknownPrefixLifetime, + _ => Lifetime { starts_with_number }, } } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 08a50050a36d5..7d4dee45c261b 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -252,6 +252,11 @@ lint_duplicate_macro_attribute = lint_duplicate_matcher_binding = duplicate matcher binding +lint_elided_named_lifetime = elided lifetime has a name + .label_elided = this elided lifetime gets resolved as `{$name}` + .label_named = lifetime `{$name}` declared here + .suggestion = consider specifying it explicitly + lint_enum_intrinsics_mem_discriminant = the return value of `mem::discriminant` is unspecified when called with a non-enum type .note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum @@ -695,11 +700,18 @@ lint_ptr_null_checks_ref = references are not nullable, so checking them for nul lint_query_instability = 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 +lint_query_untracked = `{$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 + lint_range_endpoint_out_of_range = range endpoint is out of range for `{$ty}` lint_range_use_inclusive_range = use an inclusive range instead +lint_raw_prefix = prefix `'r` is reserved + .label = reserved prefix + .suggestion = insert whitespace here to avoid this being parsed as a prefix in Rust 2021 + lint_reason_must_be_string_literal = reason must be a string literal lint_reason_must_come_last = reason in lint attribute must come last @@ -779,6 +791,9 @@ lint_tykind = usage of `ty::TyKind` lint_tykind_kind = usage of `ty::TyKind::` .suggestion = try using `ty::` directly +lint_type_ir_inherent_usage = 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 + lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing .label = argument has type `{$arg_ty}` .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index d8482567bbe5b..7de92a43a9ab4 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -426,12 +426,6 @@ impl MissingDoc { article: &'static str, desc: &'static str, ) { - // If we're building a test harness, then warning about - // documentation is probably not really relevant right now. - if cx.sess().opts.test { - return; - } - // Only check publicly-visible items, using the result from the privacy pass. // It's an option so the crate root can also use this function (it doesn't // have a `NodeId`). @@ -1851,9 +1845,16 @@ impl KeywordIdents { TokenTree::Token(token, _) => { if let Some((ident, token::IdentIsRaw::No)) = token.ident() { if !prev_dollar { - self.check_ident_token(cx, UnderMacro(true), ident); + self.check_ident_token(cx, UnderMacro(true), ident, ""); } - } else if *token == TokenKind::Dollar { + } else if let Some((ident, token::IdentIsRaw::No)) = token.lifetime() { + self.check_ident_token( + cx, + UnderMacro(true), + ident.without_first_quote(), + "'", + ); + } else if token.kind == TokenKind::Dollar { prev_dollar = true; continue; } @@ -1869,6 +1870,7 @@ impl KeywordIdents { cx: &EarlyContext<'_>, UnderMacro(under_macro): UnderMacro, ident: Ident, + prefix: &'static str, ) { let (lint, edition) = match ident.name { kw::Async | kw::Await | kw::Try => (KEYWORD_IDENTS_2018, Edition::Edition2018), @@ -1902,7 +1904,7 @@ impl KeywordIdents { cx.emit_span_lint( lint, ident.span, - BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span }, + BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span, prefix }, ); } } @@ -1915,7 +1917,11 @@ impl EarlyLintPass for KeywordIdents { self.check_tokens(cx, &mac.args.tokens); } fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { - self.check_ident_token(cx, UnderMacro(false), ident); + if ident.name.as_str().starts_with('\'') { + self.check_ident_token(cx, UnderMacro(false), ident.without_first_quote(), "'"); + } else { + self.check_ident_token(cx, UnderMacro(false), ident, ""); + } } } diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index f289d4c81b3c1..fd13c418c09aa 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -8,12 +8,13 @@ use rustc_errors::{ elided_lifetime_in_path_suggestion, Applicability, Diag, DiagArgValue, LintDiagnostic, }; use rustc_middle::middle::stability; -use rustc_session::lint::BuiltinLintDiag; +use rustc_session::lint::{BuiltinLintDiag, ElidedLifetimeResolution}; use rustc_session::Session; +use rustc_span::symbol::kw; use rustc_span::BytePos; use tracing::debug; -use crate::lints; +use crate::lints::{self, ElidedNamedLifetime}; mod check_cfg; @@ -171,6 +172,10 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & } .decorate_lint(diag); } + BuiltinLintDiag::RawPrefix(label_span) => { + lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() } + .decorate_lint(diag); + } BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => { lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag); } @@ -441,5 +446,16 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => { lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag) } + BuiltinLintDiag::ElidedNamedLifetimes { elided: (span, kind), resolution } => { + match resolution { + ElidedLifetimeResolution::Static => { + ElidedNamedLifetime { span, kind, name: kw::StaticLifetime, declaration: None } + } + ElidedLifetimeResolution::Param(name, declaration) => { + ElidedNamedLifetime { span, kind, name, declaration: Some(declaration) } + } + } + .decorate_lint(diag) + } } } diff --git a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs index fb3f40aa27108..ddaa819df1413 100644 --- a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs @@ -267,7 +267,7 @@ pub(super) fn unexpected_cfg_value( // encouraged to do so. let can_suggest_adding_value = !sess.psess.check_config.well_known_names.contains(&name) // Except when working on rustc or the standard library itself, in which case we want to - // suggest adding these cfgs to the "normal" place because of bootstraping reasons. As a + // suggest adding these cfgs to the "normal" place because of bootstrapping reasons. As a // basic heuristic, we use the "cheat" unstable feature enable method and the // non-ui-testing enabled option. || (matches!(sess.psess.unstable_features, rustc_feature::UnstableFeatures::Cheat) diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 42b33f9882d76..d8afba3d5053c 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -1,21 +1,66 @@ +use rustc_data_structures::fx::FxIndexMap; +use rustc_hir::{HirId, CRATE_OWNER_ID}; +use rustc_middle::lint::LintExpectation; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS; -use rustc_session::lint::LintExpectationId; +use rustc_session::lint::{Level, LintExpectationId}; use rustc_span::Symbol; use crate::lints::{Expectation, ExpectationNote}; pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { check_expectations, ..*providers }; + *providers = Providers { lint_expectations, check_expectations, ..*providers }; +} + +fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> { + let krate = tcx.hir_crate_items(()); + + let mut expectations = Vec::new(); + let mut unstable_to_stable_ids = FxIndexMap::default(); + + let mut record_stable = |attr_id, hir_id, attr_index| { + let expect_id = LintExpectationId::Stable { hir_id, attr_index, lint_index: None }; + unstable_to_stable_ids.entry(attr_id).or_insert(expect_id); + }; + let mut push_expectations = |owner| { + let lints = tcx.shallow_lint_levels_on(owner); + if lints.expectations.is_empty() { + return; + } + + expectations.extend_from_slice(&lints.expectations); + + let attrs = tcx.hir_attrs(owner); + for &(local_id, attrs) in attrs.map.iter() { + // Some attributes appear multiple times in HIR, to ensure they are correctly taken + // into account where they matter. This means we cannot just associate the AttrId to + // the first HirId where we see it, but need to check it actually appears in a lint + // level. + // FIXME(cjgillot): Can this cause an attribute to appear in multiple expectation ids? + if !lints.specs.contains_key(&local_id) { + continue; + } + for (attr_index, attr) in attrs.iter().enumerate() { + let Some(Level::Expect(_)) = Level::from_attr(attr) else { continue }; + record_stable(attr.id, HirId { owner, local_id }, attr_index.try_into().unwrap()); + } + } + }; + + push_expectations(CRATE_OWNER_ID); + for owner in krate.owners() { + push_expectations(owner); + } + + tcx.dcx().update_unstable_expectation_id(unstable_to_stable_ids); + expectations } fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option) { let lint_expectations = tcx.lint_expectations(()); let fulfilled_expectations = tcx.dcx().steal_fulfilled_expectation_ids(); - tracing::debug!(?lint_expectations, ?fulfilled_expectations); - for (id, expectation) in lint_expectations { // This check will always be true, since `lint_expectations` only // holds stable ids diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 8824e1dfe50d8..c43c650a9f912 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -1,19 +1,29 @@ -use rustc_data_structures::fx::FxIndexSet; +use std::assert_matches::debug_assert_matches; +use std::cell::LazyCell; + +use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::UnordSet; use rustc_errors::{Applicability, LintDiagnostic}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::TyCtxtInferExt; use rustc_macros::LintDiagnostic; -use rustc_middle::bug; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; +use rustc_middle::ty::relate::{ + structurally_relate_consts, structurally_relate_tys, Relate, RelateResult, TypeRelation, +}; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; +use rustc_middle::{bug, span_bug}; use rustc_session::lint::FutureIncompatibilityReason; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::edition::Edition; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; +use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt; +use rustc_trait_selection::traits::ObligationCtxt; use crate::{fluent_generated as fluent, LateContext, LateLintPass}; @@ -119,20 +129,41 @@ impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures { } } +#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] +enum ParamKind { + // Early-bound var. + Early(Symbol, u32), + // Late-bound var on function, not within a binder. We can capture these. + Free(DefId, Symbol), + // Late-bound var in a binder. We can't capture these yet. + Late, +} + fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) { let sig = tcx.fn_sig(parent_def_id).instantiate_identity(); - let mut in_scope_parameters = FxIndexSet::default(); + let mut in_scope_parameters = FxIndexMap::default(); // Populate the in_scope_parameters list first with all of the generics in scope let mut current_def_id = Some(parent_def_id.to_def_id()); while let Some(def_id) = current_def_id { let generics = tcx.generics_of(def_id); for param in &generics.own_params { - in_scope_parameters.insert(param.def_id); + in_scope_parameters.insert(param.def_id, ParamKind::Early(param.name, param.index)); } current_def_id = generics.parent; } + for bound_var in sig.bound_vars() { + let ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(def_id, name)) = bound_var + else { + span_bug!(tcx.def_span(parent_def_id), "unexpected non-lifetime binder on fn sig"); + }; + + in_scope_parameters.insert(def_id, ParamKind::Free(def_id, name)); + } + + let sig = tcx.liberate_late_bound_regions(parent_def_id.to_def_id(), sig); + // Then visit the signature to walk through all the binders (incl. the late-bound // vars on the function itself, which we need to count too). sig.visit_with(&mut VisitOpaqueTypes { @@ -140,21 +171,45 @@ fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) { parent_def_id, in_scope_parameters, seen: Default::default(), + // Lazily compute these two, since they're likely a bit expensive. + variances: LazyCell::new(|| { + let mut functional_variances = FunctionalVariances { + tcx: tcx, + variances: FxHashMap::default(), + ambient_variance: ty::Covariant, + generics: tcx.generics_of(parent_def_id), + }; + functional_variances.relate(sig, sig).unwrap(); + functional_variances.variances + }), + outlives_env: LazyCell::new(|| { + let param_env = tcx.param_env(parent_def_id); + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + let assumed_wf_tys = ocx.assumed_wf_types(param_env, parent_def_id).unwrap_or_default(); + let implied_bounds = + infcx.implied_bounds_tys_compat(param_env, parent_def_id, &assumed_wf_tys, false); + OutlivesEnvironment::with_bounds(param_env, implied_bounds) + }), }); } -struct VisitOpaqueTypes<'tcx> { +struct VisitOpaqueTypes<'tcx, VarFn, OutlivesFn> { tcx: TyCtxt<'tcx>, parent_def_id: LocalDefId, - in_scope_parameters: FxIndexSet, + in_scope_parameters: FxIndexMap, + variances: LazyCell, VarFn>, + outlives_env: LazyCell, OutlivesFn>, seen: FxIndexSet, } -impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { - fn visit_binder>>( - &mut self, - t: &ty::Binder<'tcx, T>, - ) -> Self::Result { +impl<'tcx, VarFn, OutlivesFn> TypeVisitor> + for VisitOpaqueTypes<'tcx, VarFn, OutlivesFn> +where + VarFn: FnOnce() -> FxHashMap, + OutlivesFn: FnOnce() -> OutlivesEnvironment<'tcx>, +{ + fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { // When we get into a binder, we need to add its own bound vars to the scope. let mut added = vec![]; for arg in t.bound_vars() { @@ -163,8 +218,8 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(def_id, ..)) | ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, _)) => { added.push(def_id); - let unique = self.in_scope_parameters.insert(def_id); - assert!(unique); + let unique = self.in_scope_parameters.insert(def_id, ParamKind::Late); + assert_eq!(unique, None); } _ => { self.tcx.dcx().span_delayed_bug( @@ -184,7 +239,7 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { } } - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, t: Ty<'tcx>) { if !t.has_aliases() { return; } @@ -207,89 +262,126 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { && let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin && parent_def_id == self.parent_def_id { - // Compute the set of args that are captured by the opaque... - let mut captured = FxIndexSet::default(); - let variances = self.tcx.variances_of(opaque_def_id); - let mut current_def_id = Some(opaque_def_id.to_def_id()); - while let Some(def_id) = current_def_id { - let generics = self.tcx.generics_of(def_id); - for param in &generics.own_params { - // A param is captured if it's invariant. - if variances[param.index as usize] != ty::Invariant { - continue; - } - // We need to turn all `ty::Param`/`ConstKind::Param` and - // `ReEarlyParam`/`ReBound` into def ids. - captured.insert(extract_def_id_from_arg( - self.tcx, - generics, - opaque_ty.args[param.index as usize], - )); - } - current_def_id = generics.parent; - } - - // Compute the set of in scope params that are not captured. Get their spans, - // since that's all we really care about them for emitting the diagnostic. - let uncaptured_spans: Vec<_> = self - .in_scope_parameters - .iter() - .filter(|def_id| !captured.contains(*def_id)) - .map(|def_id| self.tcx.def_span(def_id)) - .collect(); - let opaque_span = self.tcx.def_span(opaque_def_id); let new_capture_rules = opaque_span.at_least_rust_2024() || self.tcx.features().lifetime_capture_rules_2024; - - // If we have uncaptured args, and if the opaque doesn't already have - // `use<>` syntax on it, and we're < edition 2024, then warn the user. if !new_capture_rules && !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..))) - && !uncaptured_spans.is_empty() { - let suggestion = if let Ok(snippet) = - self.tcx.sess.source_map().span_to_snippet(opaque_span) - && snippet.starts_with("impl ") - { - let (lifetimes, others): (Vec<_>, Vec<_>) = captured - .into_iter() - .partition(|def_id| self.tcx.def_kind(*def_id) == DefKind::LifetimeParam); - // Take all lifetime params first, then all others (ty/ct). - let generics: Vec<_> = lifetimes - .into_iter() - .chain(others) - .map(|def_id| self.tcx.item_name(def_id).to_string()) - .collect(); - // Make sure that we're not trying to name any APITs - if generics.iter().all(|name| !name.starts_with("impl ")) { - Some(( - format!(" + use<{}>", generics.join(", ")), - opaque_span.shrink_to_hi(), - )) + // Compute the set of args that are captured by the opaque... + let mut captured = FxIndexSet::default(); + let mut captured_regions = FxIndexSet::default(); + let variances = self.tcx.variances_of(opaque_def_id); + let mut current_def_id = Some(opaque_def_id.to_def_id()); + while let Some(def_id) = current_def_id { + let generics = self.tcx.generics_of(def_id); + for param in &generics.own_params { + // A param is captured if it's invariant. + if variances[param.index as usize] != ty::Invariant { + continue; + } + + let arg = opaque_ty.args[param.index as usize]; + // We need to turn all `ty::Param`/`ConstKind::Param` and + // `ReEarlyParam`/`ReBound` into def ids. + captured.insert(extract_def_id_from_arg(self.tcx, generics, arg)); + + captured_regions.extend(arg.as_region()); + } + current_def_id = generics.parent; + } + + // Compute the set of in scope params that are not captured. + let mut uncaptured_args: FxIndexSet<_> = self + .in_scope_parameters + .iter() + .filter(|&(def_id, _)| !captured.contains(def_id)) + .collect(); + // Remove the set of lifetimes that are in-scope that outlive some other captured + // lifetime and are contravariant (i.e. covariant in argument position). + uncaptured_args.retain(|&(def_id, kind)| { + let Some(ty::Bivariant | ty::Contravariant) = self.variances.get(def_id) else { + // Keep all covariant/invariant args. Also if variance is `None`, + // then that means it's either not a lifetime, or it didn't show up + // anywhere in the signature. + return true; + }; + // We only computed variance of lifetimes... + 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, + ty::EarlyParamRegion { name, index }, + ), + ParamKind::Free(def_id, name) => ty::Region::new_late_param( + self.tcx, + self.parent_def_id.to_def_id(), + ty::BoundRegionKind::BrNamed(def_id, name), + ), + // Totally ignore late bound args from binders. + ParamKind::Late => return true, + }; + // Does this region outlive any captured region? + !captured_regions.iter().any(|r| { + self.outlives_env + .free_region_map() + .sub_free_regions(self.tcx, *r, uncaptured) + }) + }); + + // If we have uncaptured args, and if the opaque doesn't already have + // `use<>` syntax on it, and we're < edition 2024, then warn the user. + if !uncaptured_args.is_empty() { + let suggestion = if let Ok(snippet) = + self.tcx.sess.source_map().span_to_snippet(opaque_span) + && snippet.starts_with("impl ") + { + let (lifetimes, others): (Vec<_>, Vec<_>) = + captured.into_iter().partition(|def_id| { + self.tcx.def_kind(*def_id) == DefKind::LifetimeParam + }); + // Take all lifetime params first, then all others (ty/ct). + let generics: Vec<_> = lifetimes + .into_iter() + .chain(others) + .map(|def_id| self.tcx.item_name(def_id).to_string()) + .collect(); + // Make sure that we're not trying to name any APITs + if generics.iter().all(|name| !name.starts_with("impl ")) { + Some(( + format!(" + use<{}>", generics.join(", ")), + opaque_span.shrink_to_hi(), + )) + } else { + None + } } else { None - } - } else { - None - }; - - self.tcx.emit_node_span_lint( - IMPL_TRAIT_OVERCAPTURES, - self.tcx.local_def_id_to_hir_id(opaque_def_id), - opaque_span, - ImplTraitOvercapturesLint { - self_ty: t, - num_captured: uncaptured_spans.len(), - uncaptured_spans, - suggestion, - }, - ); + }; + + let uncaptured_spans: Vec<_> = uncaptured_args + .into_iter() + .map(|(def_id, _)| self.tcx.def_span(def_id)) + .collect(); + + self.tcx.emit_node_span_lint( + IMPL_TRAIT_OVERCAPTURES, + self.tcx.local_def_id_to_hir_id(opaque_def_id), + opaque_span, + ImplTraitOvercapturesLint { + self_ty: t, + num_captured: uncaptured_spans.len(), + uncaptured_spans, + suggestion, + }, + ); + } } + // Otherwise, if we are edition 2024, have `use<>` syntax, and // have no uncaptured args, then we should warn to the user that // it's redundant to capture all args explicitly. - else if new_capture_rules + if new_capture_rules && let Some((captured_args, capturing_span)) = opaque.bounds.iter().find_map(|bound| match *bound { hir::GenericBound::Use(a, s) => Some((a, s)), @@ -327,7 +419,7 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { if self .in_scope_parameters .iter() - .all(|def_id| explicitly_captured.contains(def_id)) + .all(|(def_id, _)| explicitly_captured.contains(def_id)) { self.tcx.emit_node_span_lint( IMPL_TRAIT_REDUNDANT_CAPTURES, @@ -396,7 +488,11 @@ fn extract_def_id_from_arg<'tcx>( ty::ReBound( _, ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, ..), .. }, - ) => def_id, + ) + | ty::ReLateParam(ty::LateParamRegion { + scope: _, + bound_region: ty::BoundRegionKind::BrNamed(def_id, ..), + }) => def_id, _ => unreachable!(), }, ty::GenericArgKind::Type(ty) => { @@ -413,3 +509,106 @@ fn extract_def_id_from_arg<'tcx>( } } } + +/// Computes the variances of regions that appear in the type, but considering +/// late-bound regions too, which don't have their variance computed usually. +/// +/// Like generalization, this is a unary operation implemented on top of the binary +/// relation infrastructure, mostly because it's much easier to have the relation +/// track the variance for you, rather than having to do it yourself. +struct FunctionalVariances<'tcx> { + tcx: TyCtxt<'tcx>, + variances: FxHashMap, + ambient_variance: ty::Variance, + generics: &'tcx ty::Generics, +} + +impl<'tcx> TypeRelation> for FunctionalVariances<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn relate_with_variance>>( + &mut self, + variance: rustc_type_ir::Variance, + _: ty::VarianceDiagInfo>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + let old_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + self.relate(a, b).unwrap(); + self.ambient_variance = old_variance; + Ok(a) + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + structurally_relate_tys(self, a, b).unwrap(); + Ok(a) + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + _: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + let def_id = match *a { + ty::ReEarlyParam(ebr) => self.generics.region_param(ebr, self.tcx).def_id, + ty::ReBound( + _, + ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, ..), .. }, + ) + | ty::ReLateParam(ty::LateParamRegion { + scope: _, + bound_region: ty::BoundRegionKind::BrNamed(def_id, ..), + }) => def_id, + _ => { + return Ok(a); + } + }; + + if let Some(variance) = self.variances.get_mut(&def_id) { + *variance = unify(*variance, self.ambient_variance); + } else { + self.variances.insert(def_id, self.ambient_variance); + } + + Ok(a) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + structurally_relate_consts(self, a, b).unwrap(); + Ok(a) + } + + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate>, + { + self.relate(a.skip_binder(), b.skip_binder()).unwrap(); + Ok(a) + } +} + +/// What is the variance that satisfies the two variances? +fn unify(a: ty::Variance, b: ty::Variance) -> ty::Variance { + match (a, b) { + // Bivariance is lattice bottom. + (ty::Bivariant, other) | (other, ty::Bivariant) => other, + // Invariant is lattice top. + (ty::Invariant, _) | (_, ty::Invariant) => ty::Invariant, + // If type is required to be covariant and contravariant, then it's invariant. + (ty::Contravariant, ty::Covariant) | (ty::Covariant, ty::Contravariant) => ty::Invariant, + // Otherwise, co + co = co, contra + contra = contra. + (ty::Contravariant, ty::Contravariant) => ty::Contravariant, + (ty::Covariant, ty::Covariant) => ty::Covariant, + } +} diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 65571815019ea..9d637c1eb7f84 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -17,8 +17,8 @@ use tracing::debug; use crate::lints::{ BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword, - NonGlobImportTypeIrInherent, QueryInstability, SpanUseEqCtxtDiag, TyQualified, TykindDiag, - TykindKind, UntranslatableDiag, + NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag, TyQualified, + TykindDiag, TykindKind, TypeIrInherentUsage, UntranslatableDiag, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; @@ -88,7 +88,18 @@ declare_tool_lint! { report_in_external_macro: true } -declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY]); +declare_tool_lint! { + /// The `untracked_query_information` lint detects use of methods which leak information not + /// tracked by the query system, such as whether a `Steal` value has already been stolen. In + /// order not to break incremental compilation, such methods must be used very carefully or not + /// at all. + pub rustc::UNTRACKED_QUERY_INFORMATION, + Allow, + "require explicit opt-in when accessing information not tracked by the query system", + report_in_external_macro: true +} + +declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]); impl LateLintPass<'_> for QueryStability { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { @@ -102,6 +113,13 @@ impl LateLintPass<'_> for QueryStability { QueryInstability { query: cx.tcx.item_name(def_id) }, ); } + if cx.tcx.has_attr(def_id, sym::rustc_lint_untracked_query_information) { + cx.emit_span_lint( + UNTRACKED_QUERY_INFORMATION, + span, + QueryUntracked { method: cx.tcx.item_name(def_id) }, + ); + } } } } @@ -277,13 +295,39 @@ declare_tool_lint! { report_in_external_macro: true } -declare_lint_pass!(TypeIr => [NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT]); +declare_tool_lint! { + /// The `usage_of_type_ir_inherent` lint detects usage `rustc_type_ir::inherent`. + /// + /// This module should only be used within the trait solver. + pub rustc::USAGE_OF_TYPE_IR_INHERENT, + Allow, + "usage `rustc_type_ir::inherent` outside of trait system", + report_in_external_macro: true +} + +declare_lint_pass!(TypeIr => [NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_INHERENT]); impl<'tcx> LateLintPass<'tcx> for TypeIr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { let rustc_hir::ItemKind::Use(path, kind) = item.kind else { return }; let is_mod_inherent = |def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id); + + // Path segments except for the final. + if let Some(seg) = + path.segments.iter().find(|seg| seg.res.opt_def_id().is_some_and(is_mod_inherent)) + { + cx.emit_span_lint(USAGE_OF_TYPE_IR_INHERENT, seg.ident.span, TypeIrInherentUsage); + } + // Final path resolutions, like `use rustc_type_ir::inherent` + else if path.res.iter().any(|res| res.opt_def_id().is_some_and(is_mod_inherent)) { + cx.emit_span_lint( + USAGE_OF_TYPE_IR_INHERENT, + path.segments.last().unwrap().ident.span, + TypeIrInherentUsage, + ); + } + let (lo, hi, snippet) = match path.segments { [.., penultimate, segment] if penultimate.res.opt_def_id().is_some_and(is_mod_inherent) => diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 07ac63ec96cf1..65289de980e13 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -115,34 +115,6 @@ impl LintLevelSets { } } -fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> { - let store = unerased_lint_store(tcx.sess); - - let mut builder = LintLevelsBuilder { - sess: tcx.sess, - features: tcx.features(), - provider: QueryMapExpectationsWrapper { - tcx, - cur: hir::CRATE_HIR_ID, - specs: ShallowLintLevelMap::default(), - expectations: Vec::new(), - unstable_to_stable_ids: FxIndexMap::default(), - empty: FxIndexMap::default(), - }, - lint_added_lints: false, - store, - registered_tools: tcx.registered_tools(()), - }; - - builder.add_command_line(); - builder.add_id(hir::CRATE_HIR_ID); - tcx.hir().walk_toplevel_module(&mut builder); - - tcx.dcx().update_unstable_expectation_id(&builder.provider.unstable_to_stable_ids); - - builder.provider.expectations -} - #[instrument(level = "trace", skip(tcx), ret)] fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap { let store = unerased_lint_store(tcx.sess); @@ -207,7 +179,7 @@ pub trait LintLevelsProvider { fn current_specs(&self) -> &FxIndexMap; fn insert(&mut self, id: LintId, lvl: LevelAndSource); fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource; - fn push_expectation(&mut self, _id: LintExpectationId, _expectation: LintExpectation) {} + fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation); } impl LintLevelsProvider for TopDown { @@ -222,6 +194,8 @@ impl LintLevelsProvider for TopDown { fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource { self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), sess) } + + fn push_expectation(&mut self, _: LintExpectationId, _: LintExpectation) {} } struct LintLevelQueryMap<'tcx> { @@ -243,47 +217,8 @@ impl LintLevelsProvider for LintLevelQueryMap<'_> { fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource { self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur) } -} - -struct QueryMapExpectationsWrapper<'tcx> { - tcx: TyCtxt<'tcx>, - /// HirId of the currently investigated element. - cur: HirId, - /// Level map for `cur`. - specs: ShallowLintLevelMap, - expectations: Vec<(LintExpectationId, LintExpectation)>, - unstable_to_stable_ids: FxIndexMap, - /// Empty hash map to simplify code. - empty: FxIndexMap, -} - -impl LintLevelsProvider for QueryMapExpectationsWrapper<'_> { - fn current_specs(&self) -> &FxIndexMap { - self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty) - } - fn insert(&mut self, id: LintId, lvl: LevelAndSource) { - self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl); - } - fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource { - // We cannot use `tcx.lint_level_at_node` because we want to know in which order the - // attributes have been inserted, in particular whether an `expect` follows a `forbid`. - self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur) - } fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) { - let LintExpectationId::Stable { attr_id: Some(attr_id), hir_id, attr_index, .. } = id - else { - bug!("unstable expectation id should already be mapped") - }; - let key = LintExpectationId::Unstable { attr_id, lint_index: None }; - - self.unstable_to_stable_ids.entry(key).or_insert(LintExpectationId::Stable { - hir_id, - attr_index, - lint_index: None, - attr_id: None, - }); - - self.expectations.push((id.normalize(), expectation)); + self.specs.expectations.push((id, expectation)) } } @@ -368,80 +303,6 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { } } -impl<'tcx> LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> { - fn add_id(&mut self, hir_id: HirId) { - // Change both the `HirId` and the associated specs. - self.provider.cur = hir_id; - self.provider.specs.specs.clear(); - self.add(self.provider.tcx.hir().attrs(hir_id), hir_id == hir::CRATE_HIR_ID, Some(hir_id)); - } -} - -impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> { - type NestedFilter = nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.provider.tcx.hir() - } - - fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - self.add_id(param.hir_id); - intravisit::walk_param(self, param); - } - - fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) { - self.add_id(it.hir_id()); - intravisit::walk_item(self, it); - } - - fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) { - self.add_id(it.hir_id()); - intravisit::walk_foreign_item(self, it); - } - - fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) { - // We will call `add_id` when we walk - // the `StmtKind`. The outer statement itself doesn't - // define the lint levels. - intravisit::walk_stmt(self, e); - } - - fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - self.add_id(e.hir_id); - intravisit::walk_expr(self, e); - } - - fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) { - self.add_id(s.hir_id); - intravisit::walk_field_def(self, s); - } - - fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) { - self.add_id(v.hir_id); - intravisit::walk_variant(self, v); - } - - fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { - self.add_id(l.hir_id); - intravisit::walk_local(self, l); - } - - fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) { - self.add_id(a.hir_id); - intravisit::walk_arm(self, a); - } - - fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { - self.add_id(trait_item.hir_id()); - intravisit::walk_trait_item(self, trait_item); - } - - fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { - self.add_id(impl_item.hir_id()); - intravisit::walk_impl_item(self, impl_item); - } -} - pub struct LintLevelsBuilder<'s, P> { sess: &'s Session, features: &'s Features, @@ -625,13 +486,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful /// (e.g. if a forbid was already inserted on the same scope), then emits a /// diagnostic with no change to `specs`. - fn insert_spec(&mut self, id: LintId, (mut level, src): LevelAndSource) { + fn insert_spec(&mut self, id: LintId, (level, src): LevelAndSource) { let (old_level, old_src) = self.provider.get_lint_level(id.lint, self.sess); - if let Level::Expect(id) = &mut level - && let LintExpectationId::Stable { .. } = id - { - *id = id.normalize(); - } + // Setting to a non-forbid level is an error if the lint previously had // a forbid level. Note that this is not necessarily true even with a // `#[forbid(..)]` attribute present, as that is overridden by `--cap-lints`. @@ -743,17 +600,15 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // This is the only lint level with a `LintExpectationId` that can be created from // an attribute. Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => { - let LintExpectationId::Unstable { attr_id, lint_index } = unstable_id else { + let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id + else { bug!("stable id Level::from_attr") }; let stable_id = LintExpectationId::Stable { hir_id, attr_index: attr_index.try_into().unwrap(), - lint_index, - // We pass the previous unstable attr_id such that we can trace the ast id - // when building a map to go from unstable to stable id. - attr_id: Some(attr_id), + lint_index: None, }; Level::Expect(stable_id) @@ -840,59 +695,20 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let name = pprust::path_to_string(&meta_item.path); let lint_result = self.store.check_lint_name(&name, tool_name, self.registered_tools); - match &lint_result { + + let (ids, name) = match lint_result { CheckLintNameResult::Ok(ids) => { - // This checks for instances where the user writes - // `#[expect(unfulfilled_lint_expectations)]` in that case we want to avoid - // overriding the lint level but instead add an expectation that can't be - // fulfilled. The lint message will include an explanation, that the - // `unfulfilled_lint_expectations` lint can't be expected. - if let Level::Expect(expect_id) = level { - // The `unfulfilled_lint_expectations` lint is not part of any lint - // groups. Therefore. we only need to check the slice if it contains a - // single lint. - let is_unfulfilled_lint_expectations = match ids { - [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS), - _ => false, - }; - self.provider.push_expectation( - expect_id, - LintExpectation::new( - reason, - sp, - is_unfulfilled_lint_expectations, - tool_name, - ), - ); - } - let src = LintLevelSource::Node { - name: meta_item - .path - .segments - .last() - .expect("empty lint name") - .ident - .name, - span: sp, - reason, - }; - for &id in *ids { - if self.check_gated_lint(id, attr.span, false) { - self.insert_spec(id, (level, src)); - } - } + let name = + meta_item.path.segments.last().expect("empty lint name").ident.name; + (ids, name) } CheckLintNameResult::Tool(ids, new_lint_name) => { - let src = match new_lint_name { + let name = match new_lint_name { None => { let complete_name = &format!("{}::{}", tool_ident.unwrap().name, name); - LintLevelSource::Node { - name: Symbol::intern(complete_name), - span: sp, - reason, - } + Symbol::intern(complete_name) } Some(new_lint_name) => { self.emit_span_lint( @@ -901,27 +717,13 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { DeprecatedLintName { name, suggestion: sp, - replace: new_lint_name, + replace: &new_lint_name, }, ); - LintLevelSource::Node { - name: Symbol::intern(new_lint_name), - span: sp, - reason, - } + Symbol::intern(&new_lint_name) } }; - for &id in *ids { - if self.check_gated_lint(id, attr.span, false) { - self.insert_spec(id, (level, src)); - } - } - if let Level::Expect(expect_id) = level { - self.provider.push_expectation( - expect_id, - LintExpectation::new(reason, sp, false, tool_name), - ); - } + (ids, name) } CheckLintNameResult::MissingTool => { @@ -929,9 +731,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // exist in the tool or the code was not compiled with the tool and // therefore the lint was never added to the `LintStore`. To detect // this is the responsibility of the lint tool. + continue; } - &CheckLintNameResult::NoTool => { + CheckLintNameResult::NoTool => { sess.dcx().emit_err(UnknownToolInScopedLint { span: tool_ident.map(|ident| ident.span), tool_name: tool_name.unwrap(), @@ -941,58 +744,88 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { continue; } - _ if !self.lint_added_lints => {} - CheckLintNameResult::Renamed(ref replace) => { - let suggestion = - RenamedLintSuggestion::WithSpan { suggestion: sp, replace }; - let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); - let lint = RenamedLint { name: name.as_str(), suggestion }; - self.emit_span_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint); + if self.lint_added_lints { + let suggestion = + RenamedLintSuggestion::WithSpan { suggestion: sp, replace }; + let name = + tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); + let lint = RenamedLint { name: name.as_str(), suggestion }; + self.emit_span_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint); + } + + // If this lint was renamed, apply the new lint instead of ignoring the + // attribute. Ignore any errors or warnings that happen because the new + // name is inaccurate. + // NOTE: `new_name` already includes the tool name, so we don't + // have to add it again. + let CheckLintNameResult::Ok(ids) = + self.store.check_lint_name(replace, None, self.registered_tools) + else { + panic!("renamed lint does not exist: {replace}"); + }; + + (ids, Symbol::intern(&replace)) } CheckLintNameResult::Removed(ref reason) => { - 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); + 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); + } + continue; } CheckLintNameResult::NoLint(suggestion) => { - let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); - let suggestion = suggestion.map(|(replace, from_rustc)| { - UnknownLintSuggestion::WithSpan { suggestion: sp, replace, from_rustc } - }); - let lint = UnknownLint { name, suggestion }; - self.emit_span_lint(UNKNOWN_LINTS, sp.into(), lint); - } - } - // If this lint was renamed, apply the new lint instead of ignoring the attribute. - // This happens outside of the match because the new lint should be applied even if - // we don't warn about the name change. - if let CheckLintNameResult::Renamed(new_name) = lint_result { - // Ignore any errors or warnings that happen because the new name is inaccurate - // NOTE: `new_name` already includes the tool name, so we don't have to add it - // again. - let CheckLintNameResult::Ok(ids) = - self.store.check_lint_name(&new_name, None, self.registered_tools) - else { - panic!("renamed lint does not exist: {new_name}"); - }; - - let src = - LintLevelSource::Node { name: Symbol::intern(&new_name), span: sp, reason }; - for &id in ids { - if self.check_gated_lint(id, attr.span, false) { - self.insert_spec(id, (level, src)); + if self.lint_added_lints { + let name = + tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); + let suggestion = suggestion.map(|(replace, from_rustc)| { + UnknownLintSuggestion::WithSpan { + suggestion: sp, + replace, + from_rustc, + } + }); + let lint = UnknownLint { name, suggestion }; + self.emit_span_lint(UNKNOWN_LINTS, sp.into(), lint); } + continue; } - if let Level::Expect(expect_id) = level { - self.provider.push_expectation( - expect_id, - LintExpectation::new(reason, sp, false, tool_name), - ); + }; + + let src = LintLevelSource::Node { name, span: sp, reason }; + for &id in ids { + if self.check_gated_lint(id, attr.span, false) { + self.insert_spec(id, (level, src)); } } + + // This checks for instances where the user writes + // `#[expect(unfulfilled_lint_expectations)]` in that case we want to avoid + // overriding the lint level but instead add an expectation that can't be + // fulfilled. The lint message will include an explanation, that the + // `unfulfilled_lint_expectations` lint can't be expected. + if let Level::Expect(expect_id) = level { + // The `unfulfilled_lint_expectations` lint is not part of any lint + // groups. Therefore. we only need to check the slice if it contains a + // single lint. + let is_unfulfilled_lint_expectations = match ids { + [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS), + _ => false, + }; + self.provider.push_expectation( + expect_id, + LintExpectation::new( + reason, + sp, + is_unfulfilled_lint_expectations, + tool_name, + ), + ); + } } } @@ -1100,7 +933,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { shallow_lint_levels_on, lint_expectations, ..*providers }; + *providers = Providers { shallow_lint_levels_on, ..*providers }; } pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option, &str) { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index c5a5c5b30afef..a9627e9b78907 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -30,6 +30,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] +#![feature(assert_matches)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extract_if)] @@ -609,6 +610,7 @@ fn register_internals(store: &mut LintStore) { vec![ LintId::of(DEFAULT_HASH_TYPES), LintId::of(POTENTIAL_QUERY_INSTABILITY), + LintId::of(UNTRACKED_QUERY_INFORMATION), LintId::of(USAGE_OF_TY_TYKIND), LintId::of(PASS_BY_VALUE), LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index c6bcb1f3e8334..e49b102cb391c 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -9,7 +9,7 @@ use rustc_errors::{ }; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir}; +use rustc_hir::{self as hir, MissingLifetimeKind}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::inhabitedness::InhabitedPredicate; use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt}; @@ -363,8 +363,9 @@ pub(crate) enum BuiltinEllipsisInclusiveRangePatternsLint { pub(crate) struct BuiltinKeywordIdents { pub kw: Ident, pub next: Edition, - #[suggestion(code = "r#{kw}", applicability = "machine-applicable")] + #[suggestion(code = "{prefix}r#{kw}", applicability = "machine-applicable")] pub suggestion: Span, + pub prefix: &'static str, } #[derive(LintDiagnostic)] @@ -894,6 +895,13 @@ pub(crate) struct QueryInstability { pub query: Symbol, } +#[derive(LintDiagnostic)] +#[diag(lint_query_untracked)] +#[note] +pub(crate) struct QueryUntracked { + pub method: Symbol, +} + #[derive(LintDiagnostic)] #[diag(lint_span_use_eq_ctxt)] pub(crate) struct SpanUseEqCtxtDiag; @@ -918,6 +926,11 @@ pub(crate) struct TyQualified { pub suggestion: Span, } +#[derive(LintDiagnostic)] +#[diag(lint_type_ir_inherent_usage)] +#[note] +pub(crate) struct TypeIrInherentUsage; + #[derive(LintDiagnostic)] #[diag(lint_non_glob_import_type_ir_inherent)] pub(crate) struct NonGlobImportTypeIrInherent { @@ -2611,6 +2624,58 @@ pub(crate) struct ElidedLifetimesInPaths { pub subdiag: ElidedLifetimeInPathSubdiag, } +pub(crate) struct ElidedNamedLifetime { + pub span: Span, + pub kind: MissingLifetimeKind, + pub name: Symbol, + pub declaration: Option, +} + +impl LintDiagnostic<'_, G> for ElidedNamedLifetime { + fn decorate_lint(self, diag: &mut rustc_errors::Diag<'_, G>) { + let Self { span, kind, name, declaration } = self; + diag.primary_message(fluent::lint_elided_named_lifetime); + diag.arg("name", name); + diag.span_label(span, fluent::lint_label_elided); + if let Some(declaration) = declaration { + diag.span_label(declaration, fluent::lint_label_named); + } + // FIXME(GrigorenkoPV): this `if` and `return` should be removed, + // but currently this lint's suggestions can conflict with those of `clippy::needless_lifetimes`: + // https://github.com/rust-lang/rust/pull/129840#issuecomment-2323349119 + // HACK: `'static` suggestions will never sonflict, emit only those for now. + if name != rustc_span::symbol::kw::StaticLifetime { + return; + } + match kind { + MissingLifetimeKind::Underscore => diag.span_suggestion_verbose( + span, + fluent::lint_suggestion, + format!("{name}"), + Applicability::MachineApplicable, + ), + MissingLifetimeKind::Ampersand => diag.span_suggestion_verbose( + span.shrink_to_hi(), + fluent::lint_suggestion, + format!("{name} "), + Applicability::MachineApplicable, + ), + MissingLifetimeKind::Comma => diag.span_suggestion_verbose( + span.shrink_to_hi(), + fluent::lint_suggestion, + format!("{name}, "), + Applicability::MachineApplicable, + ), + MissingLifetimeKind::Brackets => diag.span_suggestion_verbose( + span.shrink_to_hi(), + fluent::lint_suggestion, + format!("<{name}>"), + Applicability::MachineApplicable, + ), + }; + } +} + #[derive(LintDiagnostic)] #[diag(lint_invalid_crate_type_value)] pub(crate) struct UnknownCrateTypes { @@ -2750,6 +2815,15 @@ pub(crate) struct ReservedPrefix { pub prefix: String, } +#[derive(LintDiagnostic)] +#[diag(lint_raw_prefix)] +pub(crate) struct RawPrefix { + #[label] + pub label: Span, + #[suggestion(code = " ", applicability = "machine-applicable")] + pub suggestion: Span, +} + #[derive(LintDiagnostic)] #[diag(lint_unused_builtin_attribute)] pub(crate) struct UnusedBuiltinAttribute { diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 13a3c741fe39b..1546d79e4fd91 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { // > same expression-containing item. // // To achieve this we get try to get the paths of the _Trait_ and - // _Type_, and we look inside thoses paths to try a find in one + // _Type_, and we look inside those paths to try a find in one // of them a type whose parent is the same as the impl definition. // // If that's the case this means that this impl block declaration diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs index bb9c7d85c2efc..bb122509d0a89 100644 --- a/compiler/rustc_lint/src/shadowed_into_iter.rs +++ b/compiler/rustc_lint/src/shadowed_into_iter.rs @@ -94,12 +94,9 @@ impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter { fn is_ref_to_array(ty: Ty<'_>) -> bool { if let ty::Ref(_, pointee_ty, _) = *ty.kind() { pointee_ty.is_array() } else { false } } - fn is_boxed_slice(ty: Ty<'_>) -> bool { - ty.is_box() && ty.boxed_ty().is_slice() - } fn is_ref_to_boxed_slice(ty: Ty<'_>) -> bool { if let ty::Ref(_, pointee_ty, _) = *ty.kind() { - is_boxed_slice(pointee_ty) + pointee_ty.boxed_ty().is_some_and(Ty::is_slice) } else { false } @@ -119,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter { .iter() .copied() .take_while(|ty| !is_ref_to_boxed_slice(*ty)) - .position(|ty| is_boxed_slice(ty)) + .position(|ty| ty.boxed_ty().is_some_and(Ty::is_slice)) { (BOXED_SLICE_INTO_ITER, "Box<[T]>", "2024", idx == 0) } else { diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index f2f7c0eaa4df8..b5e501b92f0d6 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1304,8 +1304,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match *ty.kind() { ty::Adt(def, args) => { - if def.is_box() && matches!(self.mode, CItemKind::Definition) { - if ty.boxed_ty().is_sized(tcx, self.cx.param_env) { + if let Some(boxed) = ty.boxed_ty() + && matches!(self.mode, CItemKind::Definition) + { + if boxed.is_sized(tcx, self.cx.param_env) { return FfiSafe; } else { return FfiUnsafe { diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index c3b80e01c3624..b7f7b782c7722 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -283,9 +283,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } match *ty.kind() { - ty::Adt(..) if ty.is_box() => { - let boxed_ty = ty.boxed_ty(); - is_ty_must_use(cx, boxed_ty, expr, span) + 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) => { @@ -734,7 +733,7 @@ trait UnusedDelimLint { return false; } - // Check if we need parens for `match &( Struct { feild: }) {}`. + // Check if we need parens for `match &( Struct { field: }) {}`. { let mut innermost = inner; loop { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 04fd7c9c627e4..25d33126754af 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -42,6 +42,7 @@ declare_lint_pass! { DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, ELIDED_LIFETIMES_IN_PATHS, + ELIDED_NAMED_LIFETIMES, EXPLICIT_BUILTIN_CFGS_IN_FLAGS, EXPORTED_PRIVATE_DEPENDENCIES, FFI_UNWIND_CALLS, @@ -1862,6 +1863,38 @@ declare_lint! { "hidden lifetime parameters in types are deprecated" } +declare_lint! { + /// The `elided_named_lifetimes` lint detects when an elided + /// lifetime ends up being a named lifetime, such as `'static` + /// or some lifetime parameter `'a`. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(elided_named_lifetimes)] + /// struct Foo; + /// impl Foo { + /// pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 { + /// unsafe { &mut *(x as *mut _) } + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Lifetime elision is quite useful, because it frees you from having + /// to give each lifetime its own name, but sometimes it can produce + /// somewhat surprising resolutions. In safe code, it is mostly okay, + /// because the borrow checker prevents any unsoundness, so the worst + /// case scenario is you get a confusing error message in some other place. + /// But with `unsafe` code, such unexpected resolutions may lead to unsound code. + pub ELIDED_NAMED_LIFETIMES, + Warn, + "detects when an elided lifetime gets resolved to be `'static` or some named parameter" +} + declare_lint! { /// The `bare_trait_objects` lint suggests using `dyn Trait` for trait /// objects. @@ -3673,7 +3706,7 @@ declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]); declare_lint! { /// The `missing_abi` lint detects cases where the ABI is omitted from - /// extern declarations. + /// `extern` declarations. /// /// ### Example /// @@ -3687,10 +3720,12 @@ declare_lint! { /// /// ### Explanation /// - /// Historically, Rust implicitly selected C as the ABI for extern - /// declarations. We expect to add new ABIs, like `C-unwind`, in the future, - /// though this has not yet happened, and especially with their addition - /// seeing the ABI easily will make code review easier. + /// For historic reasons, Rust implicitly selects `C` as the default ABI for + /// `extern` declarations. [Other ABIs] like `C-unwind` and `system` have + /// been added since then, and especially with their addition seeing the ABI + /// easily makes code review easier. + /// + /// [Other ABIs]: https://doc.rust-lang.org/reference/items/external-blocks.html#abi pub MISSING_ABI, Allow, "No declared ABI for extern declaration" diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index c17b85db3b044..5c4b7e5664d33 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -10,7 +10,7 @@ use rustc_data_structures::stable_hasher::{ }; use rustc_error_messages::{DiagMessage, MultiSpan}; use rustc_hir::def::Namespace; -use rustc_hir::{HashStableContext, HirId}; +use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::Edition; use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent}; @@ -98,7 +98,7 @@ pub enum LintExpectationId { /// stable and can be cached. The additional index ensures that nodes with /// several expectations can correctly match diagnostics to the individual /// expectation. - Stable { hir_id: HirId, attr_index: u16, lint_index: Option, attr_id: Option }, + Stable { hir_id: HirId, attr_index: u16, lint_index: Option }, } impl LintExpectationId { @@ -122,31 +122,13 @@ impl LintExpectationId { *lint_index = new_lint_index } - - /// Prepares the id for hashing. Removes references to the ast. - /// Should only be called when the id is stable. - pub fn normalize(self) -> Self { - match self { - Self::Stable { hir_id, attr_index, lint_index, .. } => { - Self::Stable { hir_id, attr_index, lint_index, attr_id: None } - } - Self::Unstable { .. } => { - unreachable!("`normalize` called when `ExpectationId` is unstable") - } - } - } } impl HashStable for LintExpectationId { #[inline] fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { match self { - LintExpectationId::Stable { - hir_id, - attr_index, - lint_index: Some(lint_index), - attr_id: _, - } => { + LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { hir_id.hash_stable(hcx, hasher); attr_index.hash_stable(hcx, hasher); lint_index.hash_stable(hcx, hasher); @@ -166,12 +148,9 @@ impl ToStableHashKey for LintExpectation #[inline] fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { match self { - LintExpectationId::Stable { - hir_id, - attr_index, - lint_index: Some(lint_index), - attr_id: _, - } => (*hir_id, *attr_index, *lint_index), + LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { + (*hir_id, *attr_index, *lint_index) + } _ => { unreachable!("HashStable should only be called for a filled `LintExpectationId`") } @@ -577,6 +556,12 @@ pub enum DeprecatedSinceKind { InVersion(String), } +#[derive(Debug)] +pub enum ElidedLifetimeResolution { + Static, + Param(Symbol, Span), +} + // This could be a closure, but then implementing derive trait // becomes hacky (and it gets allocated). #[derive(Debug)] @@ -589,6 +574,10 @@ pub enum BuiltinLintDiag { }, MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), + ElidedNamedLifetimes { + elided: (Span, MissingLifetimeKind), + resolution: ElidedLifetimeResolution, + }, UnknownCrateTypes { span: Span, candidate: Option, @@ -623,6 +612,8 @@ pub enum BuiltinLintDiag { LegacyDeriveHelpers(Span), OrPatternsBackCompat(Span, String), ReservedPrefix(Span, String), + /// `'r#` in edition < 2021. + RawPrefix(Span), TrailingMacro(bool, Ident), BreakWithLabelAndLoop(Span), UnicodeTextFlow(Span, String), diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index c7306b0516fa0..da27db29c87ad 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1212,7 +1212,11 @@ struct LLVMRustThinLTOData { // Not 100% sure what these are, but they impact what's internalized and // what's inlined across modules, I believe. #if LLVM_VERSION_GE(18, 0) +#if LLVM_VERSION_GE(20, 0) + FunctionImporter::ImportListsTy ImportLists; +#else DenseMap ImportLists; +#endif DenseMap ExportLists; DenseMap ModuleToDefinedGVSummaries; #else @@ -1421,13 +1425,13 @@ LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, return true; } -extern "C" bool LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, +extern "C" bool LLVMRustPrepareThinLTOImport(LLVMRustThinLTOData *Data, LLVMModuleRef M, LLVMTargetMachineRef TM) { Module &Mod = *unwrap(M); TargetMachine &Target = *unwrap(TM); - const auto &ImportList = Data->ImportLists.lookup(Mod.getModuleIdentifier()); + const auto &ImportList = Data->ImportLists[Mod.getModuleIdentifier()]; auto Loader = [&](StringRef Identifier) { const auto &Memory = Data->ModuleMap.lookup(Identifier); auto &Context = Mod.getContext(); @@ -1610,7 +1614,7 @@ extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut, LLVMRustThinLTOData *Data) { SmallString<40> Key; llvm::lto::Config conf; - const auto &ImportList = Data->ImportLists.lookup(ModId); + const auto &ImportList = Data->ImportLists[ModId]; const auto &ExportList = Data->ExportLists.lookup(ModId); const auto &ResolvedODR = Data->ResolvedODR.lookup(ModId); const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(ModId); diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index 3532d50c64e87..055d2bd5bc976 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -1,6 +1,5 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![cfg_attr(bootstrap, feature(unsafe_attributes, unsafe_extern_blocks))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(rustdoc_internals)] diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a82340e3d6130..53da07aeaa695 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -71,8 +71,8 @@ impl<'a, 'tcx, T: Copy + Decodable>> ProcessQueryValue<' for Option> { #[inline(always)] - fn process_decoded(self, tcx: TyCtxt<'tcx>, _err: impl Fn() -> !) -> &'tcx [T] { - if let Some(iter) = self { tcx.arena.alloc_from_iter(iter) } else { &[] } + fn process_decoded(self, tcx: TyCtxt<'tcx>, err: impl Fn() -> !) -> &'tcx [T] { + if let Some(iter) = self { tcx.arena.alloc_from_iter(iter) } else { err() } } } @@ -84,12 +84,12 @@ impl<'a, 'tcx, T: Copy + Decodable>> fn process_decoded( self, tcx: TyCtxt<'tcx>, - _err: impl Fn() -> !, + err: impl Fn() -> !, ) -> ty::EarlyBinder<'tcx, &'tcx [T]> { ty::EarlyBinder::bind(if let Some(iter) = self { tcx.arena.alloc_from_iter(iter) } else { - &[] + err() }) } } @@ -247,14 +247,13 @@ provide! { tcx, def_id, other, cdata, explicit_predicates_of => { table } generics_of => { table } inferred_outlives_of => { table_defaulted_array } - explicit_super_predicates_of => { table } - explicit_implied_predicates_of => { table } + explicit_super_predicates_of => { table_defaulted_array } + explicit_implied_predicates_of => { table_defaulted_array } type_of => { table } type_alias_is_lazy => { table_direct } variances_of => { table } fn_sig => { table } codegen_fn_attrs => { table } - struct_target_features => { table } impl_trait_header => { table } const_param_default => { table } object_lifetime_default => { table } @@ -301,7 +300,20 @@ provide! { tcx, def_id, other, cdata, .unwrap_or_else(|| panic!("{def_id:?} does not have eval_static_initializer"))) } trait_def => { table } - deduced_param_attrs => { table } + deduced_param_attrs => { + // FIXME: `deduced_param_attrs` has some sketchy encoding settings, + // where we don't encode unless we're optimizing, doing codegen, + // and not incremental (see `encoder.rs`). I don't think this is right! + cdata + .root + .tables + .deduced_param_attrs + .get(cdata, def_id.index) + .map(|lazy| { + &*tcx.arena.alloc_from_iter(lazy.decode((cdata, tcx))) + }) + .unwrap_or_default() + } is_type_alias_impl_trait => { debug_assert_eq!(tcx.def_kind(def_id), DefKind::OpaqueTy); cdata.root.tables.is_type_alias_impl_trait.get(cdata, def_id.index) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 919623cff609c..b617d5236b980 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -9,6 +9,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; use rustc_data_structures::sync::{join, par_for_each_in, Lrc}; use rustc_data_structures::temp_dir::MaybeTempDir; +use rustc_feature::Features; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LocalDefIdSet, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::DefPathData; @@ -797,9 +798,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } -struct AnalyzeAttrState { +struct AnalyzeAttrState<'a> { is_exported: bool, is_doc_hidden: bool, + features: &'a Features, } /// Returns whether an attribute needs to be recorded in metadata, that is, if it's usable and @@ -812,7 +814,7 @@ struct AnalyzeAttrState { /// visibility: this is a piece of data that can be computed once per defid, and not once per /// attribute. Some attributes would only be usable downstream if they are public. #[inline] -fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState) -> bool { +fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState<'_>) -> bool { let mut should_encode = false; if !rustc_feature::encode_cross_crate(attr.name_or_empty()) { // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates. @@ -837,6 +839,9 @@ fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState) -> bool { } } } + } else if attr.path().starts_with(&[sym::diagnostic]) && attr.path().len() == 2 { + should_encode = + rustc_feature::is_stable_diagnostic_attribute(attr.path()[1], state.features); } else { should_encode = true; } @@ -1343,6 +1348,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let mut state = AnalyzeAttrState { is_exported: tcx.effective_visibilities(()).is_exported(def_id), is_doc_hidden: false, + features: &tcx.features(), }; let attr_iter = tcx .hir() @@ -1392,9 +1398,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if def_kind.has_codegen_attrs() { record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id)); } - if def_kind.has_struct_target_features() { - record_array!(self.tables.struct_target_features[def_id] <- self.tcx.struct_target_features(def_id)); - } if should_encode_visibility(def_kind) { let vis = self.tcx.local_visibility(local_id).map_id(|def_id| def_id.local_def_index); @@ -1446,9 +1449,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::Trait = def_kind { record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); - record_array!(self.tables.explicit_super_predicates_of[def_id] <- + record_defaulted_array!(self.tables.explicit_super_predicates_of[def_id] <- self.tcx.explicit_super_predicates_of(def_id).skip_binder()); - record_array!(self.tables.explicit_implied_predicates_of[def_id] <- + record_defaulted_array!(self.tables.explicit_implied_predicates_of[def_id] <- self.tcx.explicit_implied_predicates_of(def_id).skip_binder()); let module_children = self.tcx.module_children_local(local_id); @@ -1457,9 +1460,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::TraitAlias = def_kind { record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); - record_array!(self.tables.explicit_super_predicates_of[def_id] <- + record_defaulted_array!(self.tables.explicit_super_predicates_of[def_id] <- self.tcx.explicit_super_predicates_of(def_id).skip_binder()); - record_array!(self.tables.explicit_implied_predicates_of[def_id] <- + record_defaulted_array!(self.tables.explicit_implied_predicates_of[def_id] <- self.tcx.explicit_implied_predicates_of(def_id).skip_binder()); } if let DefKind::Trait | DefKind::Impl { .. } = def_kind { @@ -2036,7 +2039,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let simplified_self_ty = fast_reject::simplify_type( self.tcx, trait_ref.self_ty(), - TreatParams::AsCandidateKey, + TreatParams::InstantiateWithInfer, ); trait_impls .entry(trait_ref.def_id) diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 987ee3f07e924..8180a507a514d 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -19,7 +19,7 @@ use rustc_macros::{ Decodable, Encodable, MetadataDecodable, MetadataEncodable, TyDecodable, TyEncodable, }; use rustc_middle::metadata::ModChild; -use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::middle::lib_features::FeatureStability; @@ -390,6 +390,8 @@ define_tables! { explicit_item_bounds: Table, Span)>>, explicit_item_super_predicates: Table, Span)>>, inferred_outlives_of: Table, Span)>>, + explicit_super_predicates_of: Table, Span)>>, + explicit_implied_predicates_of: Table, Span)>>, inherent_impls: Table>, associated_types_for_impl_traits_in_associated_fn: Table>, associated_type_for_effects: Table>>, @@ -419,15 +421,10 @@ define_tables! { lookup_deprecation_entry: Table>, explicit_predicates_of: Table>>, generics_of: Table>, - explicit_super_predicates_of: Table, Span)>>, - // As an optimization, we only store this for trait aliases, - // since it's identical to explicit_super_predicates_of for traits. - explicit_implied_predicates_of: Table, Span)>>, type_of: Table>>>, variances_of: Table>, fn_sig: Table>>>, codegen_fn_attrs: Table>, - struct_target_features: Table>, impl_trait_header: Table>>, const_param_default: Table>>>, object_lifetime_default: Table>, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 69e3b703ccee1..b23589afb5874 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -40,5 +40,6 @@ tracing = "0.1" [features] # tidy-alphabetical-start +rustc_randomized_layouts = [] rustc_use_parallel_compiler = ["dep:rustc-rayon-core"] # tidy-alphabetical-end diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 2a85f85a9f4eb..fd6e2ad79b11d 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -28,7 +28,6 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::potential_query_instability)] #![allow(rustc::untranslatable_diagnostic)] -#![cfg_attr(bootstrap, feature(min_exhaustive_patterns, unsafe_extern_blocks))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(allocator_api)] diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 8c27cac1ea858..70da66af64be8 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -6,7 +6,7 @@ use rustc_errors::{Diag, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; use rustc_macros::HashStable; use rustc_session::lint::builtin::{self, FORBIDDEN_LINT_GROUPS}; -use rustc_session::lint::{FutureIncompatibilityReason, Level, Lint, LintId}; +use rustc_session::lint::{FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId}; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::{symbol, DesugaringKind, Span, Symbol, DUMMY_SP}; @@ -61,6 +61,7 @@ pub type LevelAndSource = (Level, LintLevelSource); /// by the attributes for *a single HirId*. #[derive(Default, Debug, HashStable)] pub struct ShallowLintLevelMap { + pub expectations: Vec<(LintExpectationId, LintExpectation)>, pub specs: SortedMap>, } diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index c098a73959230..b7d290e58d22b 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -26,8 +26,8 @@ pub struct CodegenFnAttrs { /// be set when `link_name` is set. This is for foreign items with the /// "raw-dylib" kind. pub link_ordinal: Option, - /// All the target features that are enabled for this function. Some features might be enabled - /// implicitly. + /// The `#[target_feature(enable = "...")]` attribute and the enabled + /// features (only enabled features are supported right now). pub target_features: Vec, /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found. pub linkage: Option, @@ -55,8 +55,8 @@ pub struct CodegenFnAttrs { pub struct TargetFeature { /// The name of the target feature (e.g. "avx") pub name: Symbol, - /// The feature is implied by another feature or by an argument, rather than explicitly - /// added by the `#[target_feature]` attribute + /// The feature is implied by another feature, rather than explicitly added by the + /// `#[target_feature]` attribute pub implied: bool, } diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 563647ad4e678..c6105d1f383b1 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -240,7 +240,7 @@ impl<'tcx> Const<'tcx> { match self { Const::Ty(ty, ct) => { match ct.kind() { - // Dont use the outter ty as on invalid code we can wind up with them not being the same. + // Dont use the outer ty as on invalid code we can wind up with them not being the same. // this then results in allowing const eval to add `1_i64 + 1_usize` in cases where the mir // was originally `({N: usize} + 1_usize)` under `generic_const_exprs`. ty::ConstKind::Value(ty, _) => ty, diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 3e101c0c6354d..cd56d0edc0585 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -358,10 +358,11 @@ impl Allocation { pub fn adjust_from_tcx( &self, cx: &impl HasDataLayout, + mut alloc_bytes: impl FnMut(&[u8], Align) -> Result, mut adjust_ptr: impl FnMut(Pointer) -> Result, Err>, ) -> Result, Err> { // Copy the data. - let mut bytes = Bytes::from_bytes(Cow::Borrowed(&*self.bytes), self.align); + let mut bytes = alloc_bytes(&*self.bytes, self.align)?; // Adjust provenance of pointers stored in this allocation. let mut new_provenance = Vec::with_capacity(self.provenance.ptrs().len()); let ptr_size = cx.data_layout().pointer_size.bytes_usize(); @@ -643,6 +644,12 @@ impl Allocation return Ok(()); } + /// Remove all provenance in the given memory range. + pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult { + self.provenance.clear(range, cx)?; + return Ok(()); + } + /// Applies a previously prepared provenance copy. /// The affected range, as defined in the parameters to `provenance().prepare_copy` is expected /// to be clear of provenance. diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 69ce3e087350b..8c89c15f9611d 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -91,9 +91,9 @@ pub type EvalToAllocationRawResult<'tcx> = Result, ErrorHandled pub type EvalStaticInitializerRawResult<'tcx> = Result, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; /// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed -/// because the value containts something of type `ty` that is not valtree-compatible. +/// 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 -/// necssary context to give good user-facing errors for this case. +/// necessary context to give good user-facing errors for this case. pub type EvalToValTreeResult<'tcx> = Result, Ty<'tcx>>, ErrorHandled>; #[cfg(target_pointer_width = "64")] @@ -231,7 +231,7 @@ pub enum CheckInAllocMsg { pub enum CheckAlignMsg { /// The accessed pointer did not have proper alignment. AccessedPtr, - /// The access ocurred with a place that was based on a misaligned pointer. + /// The access occurred with a place that was based on a misaligned pointer. BasedOn, } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 84c17b39a623e..989f03d3d1399 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -307,6 +307,13 @@ impl<'tcx, Prov: Provenance> Scalar { } } + pub fn clear_provenance(&mut self) -> InterpResult<'tcx> { + if matches!(self, Scalar::Ptr(..)) { + *self = self.to_scalar_int()?.into(); + } + Ok(()) + } + #[inline(always)] pub fn to_scalar_int(self) -> InterpResult<'tcx, ScalarInt> { self.try_to_scalar_int().map_err(|_| err_unsup!(ReadPointerAsInt(None)).into()) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 7b90191503702..081a23b6ff317 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -3,8 +3,6 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html use std::borrow::Cow; -use std::cell::RefCell; -use std::collections::hash_map::Entry; use std::fmt::{self, Debug, Formatter}; use std::ops::{Index, IndexMut}; use std::{iter, mem}; @@ -26,7 +24,6 @@ use rustc_index::bit_set::BitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_serialize::{Decodable, Encodable}; -use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; @@ -106,65 +103,6 @@ impl<'tcx> HasLocalDecls<'tcx> for Body<'tcx> { } } -thread_local! { - static PASS_NAMES: RefCell> = { - RefCell::new(FxHashMap::default()) - }; -} - -/// Converts a MIR pass name into a snake case form to match the profiling naming style. -fn to_profiler_name(type_name: &'static str) -> &'static str { - PASS_NAMES.with(|names| match names.borrow_mut().entry(type_name) { - Entry::Occupied(e) => *e.get(), - Entry::Vacant(e) => { - let snake_case: String = type_name - .chars() - .flat_map(|c| { - if c.is_ascii_uppercase() { - vec!['_', c.to_ascii_lowercase()] - } else if c == '-' { - vec!['_'] - } else { - vec![c] - } - }) - .collect(); - let result = &*String::leak(format!("mir_pass{}", snake_case)); - e.insert(result); - result - } - }) -} - -/// A streamlined trait that you can implement to create a pass; the -/// pass will be named after the type, and it will consist of a main -/// loop that goes over each available MIR and applies `run_pass`. -pub trait MirPass<'tcx> { - fn name(&self) -> &'static str { - // FIXME Simplify the implementation once more `str` methods get const-stable. - // See copypaste in `MirLint` - const { - let name = std::any::type_name::(); - crate::util::common::c_name(name) - } - } - - fn profiler_name(&self) -> &'static str { - to_profiler_name(self.name()) - } - - /// Returns `true` if this pass is enabled with the current combination of compiler flags. - fn is_enabled(&self, _sess: &Session) -> bool { - true - } - - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>); - - fn is_mir_dump_enabled(&self) -> bool { - true - } -} - impl MirPhase { /// Gets the index of the current MirPhase within the set of all `MirPhase`s. /// diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 6785805c27df3..a98e6943d68ee 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -43,8 +43,23 @@ pub enum PassWhere { AfterTerminator(BasicBlock), } -/// If the session is properly configured, dumps a human-readable -/// representation of the mir into: +/// Cosmetic options for pretty-printing the MIR contents, gathered from the CLI. Each pass can +/// override these when dumping its own specific MIR information with [`dump_mir_with_options`]. +#[derive(Copy, Clone)] +pub struct PrettyPrintMirOptions { + /// Whether to include extra comments, like span info. From `-Z mir-include-spans`. + pub include_extra_comments: bool, +} + +impl PrettyPrintMirOptions { + /// Create the default set of MIR pretty-printing options from the CLI flags. + pub fn from_cli(tcx: TyCtxt<'_>) -> Self { + Self { include_extra_comments: tcx.sess.opts.unstable_opts.mir_include_spans.is_enabled() } + } +} + +/// If the session is properly configured, dumps a human-readable representation of the MIR (with +/// default pretty-printing options) into: /// /// ```text /// rustc.node... @@ -77,12 +92,40 @@ pub fn dump_mir<'tcx, F>( extra_data: F, ) where F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, +{ + dump_mir_with_options( + tcx, + pass_num, + pass_name, + disambiguator, + body, + extra_data, + PrettyPrintMirOptions::from_cli(tcx), + ); +} + +/// If the session is properly configured, dumps a human-readable representation of the MIR, with +/// the given [pretty-printing options][PrettyPrintMirOptions]. +/// +/// See [`dump_mir`] for more details. +/// +#[inline] +pub fn dump_mir_with_options<'tcx, F>( + tcx: TyCtxt<'tcx>, + pass_num: bool, + pass_name: &str, + disambiguator: &dyn Display, + body: &Body<'tcx>, + extra_data: F, + options: PrettyPrintMirOptions, +) where + F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, { if !dump_enabled(tcx, pass_name, body.source.def_id()) { return; } - dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data); + dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data, options); } pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool { @@ -112,6 +155,7 @@ fn dump_matched_mir_node<'tcx, F>( disambiguator: &dyn Display, body: &Body<'tcx>, mut extra_data: F, + options: PrettyPrintMirOptions, ) where F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, { @@ -133,7 +177,7 @@ fn dump_matched_mir_node<'tcx, F>( writeln!(file)?; extra_data(PassWhere::BeforeCFG, &mut file)?; write_user_type_annotations(tcx, body, &mut file)?; - write_mir_fn(tcx, body, &mut extra_data, &mut file)?; + write_mir_fn(tcx, body, &mut extra_data, &mut file, options)?; extra_data(PassWhere::AfterCFG, &mut file)?; }; @@ -243,12 +287,15 @@ pub fn create_dump_file<'tcx>( /////////////////////////////////////////////////////////////////////////// // Whole MIR bodies -/// Write out a human-readable textual representation for the given MIR. +/// Write out a human-readable textual representation for the given MIR, with the default +/// [PrettyPrintMirOptions]. pub fn write_mir_pretty<'tcx>( tcx: TyCtxt<'tcx>, single: Option, w: &mut dyn io::Write, ) -> io::Result<()> { + let options = PrettyPrintMirOptions::from_cli(tcx); + writeln!(w, "// WARNING: This output format is intended for human consumers only")?; writeln!(w, "// and is subject to change without notice. Knock yourself out.")?; @@ -262,11 +309,11 @@ pub fn write_mir_pretty<'tcx>( } let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> { - write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?; + write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?; for body in tcx.promoted_mir(def_id) { writeln!(w)?; - write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?; + write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?; } Ok(()) }; @@ -278,7 +325,7 @@ pub fn write_mir_pretty<'tcx>( writeln!(w, "// MIR FOR CTFE")?; // Do not use `render_body`, as that would render the promoteds again, but these // are shared between mir_for_ctfe and optimized_mir - write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w)?; + write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w, options)?; } else { let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id)); render_body(w, instance_mir)?; @@ -293,14 +340,15 @@ pub fn write_mir_fn<'tcx, F>( body: &Body<'tcx>, extra_data: &mut F, w: &mut dyn io::Write, + options: PrettyPrintMirOptions, ) -> io::Result<()> where F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, { - write_mir_intro(tcx, body, w)?; + write_mir_intro(tcx, body, w, options)?; for block in body.basic_blocks.indices() { extra_data(PassWhere::BeforeBlock(block), w)?; - write_basic_block(tcx, block, body, extra_data, w)?; + write_basic_block(tcx, block, body, extra_data, w, options)?; if block.index() + 1 != body.basic_blocks.len() { writeln!(w)?; } @@ -321,6 +369,7 @@ fn write_scope_tree( w: &mut dyn io::Write, parent: SourceScope, depth: usize, + options: PrettyPrintMirOptions, ) -> io::Result<()> { let indent = depth * INDENT.len(); @@ -333,7 +382,7 @@ fn write_scope_tree( let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info); - if tcx.sess.opts.unstable_opts.mir_include_spans { + if options.include_extra_comments { writeln!( w, "{0:1$} // in {2}", @@ -373,7 +422,7 @@ fn write_scope_tree( let local_name = if local == RETURN_PLACE { " return place" } else { "" }; - if tcx.sess.opts.unstable_opts.mir_include_spans { + if options.include_extra_comments { writeln!( w, "{0:1$} //{2} in {3}", @@ -410,7 +459,7 @@ fn write_scope_tree( let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special); - if tcx.sess.opts.unstable_opts.mir_include_spans { + if options.include_extra_comments { if let Some(span) = span { writeln!( w, @@ -426,7 +475,7 @@ fn write_scope_tree( writeln!(w, "{indented_header}")?; } - write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?; + write_scope_tree(tcx, body, scope_tree, w, child, depth + 1, options)?; writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?; } @@ -449,10 +498,11 @@ impl Debug for VarDebugInfo<'_> { /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its /// local variables (both user-defined bindings and compiler temporaries). -pub fn write_mir_intro<'tcx>( +fn write_mir_intro<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'_>, w: &mut dyn io::Write, + options: PrettyPrintMirOptions, ) -> io::Result<()> { write_mir_sig(tcx, body, w)?; writeln!(w, "{{")?; @@ -468,7 +518,7 @@ pub fn write_mir_intro<'tcx>( } } - write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?; + write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1, options)?; // Add an empty line before the first block is printed. writeln!(w)?; @@ -562,7 +612,9 @@ fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io: let def_id = body.source.def_id(); let kind = tcx.def_kind(def_id); let is_function = match kind { - DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true, + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::SyntheticCoroutineBody => { + true + } _ => tcx.is_closure_like(def_id), }; match (kind, body.source.promoted) { @@ -651,12 +703,13 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option) -> Vec { // Basic blocks and their parts (statements, terminators, ...) /// Write out a human-readable textual representation for the given basic block. -pub fn write_basic_block<'tcx, F>( +fn write_basic_block<'tcx, F>( tcx: TyCtxt<'tcx>, block: BasicBlock, body: &Body<'tcx>, extra_data: &mut F, w: &mut dyn io::Write, + options: PrettyPrintMirOptions, ) -> io::Result<()> where F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, @@ -672,7 +725,7 @@ where for statement in &data.statements { extra_data(PassWhere::BeforeLocation(current_location), w)?; let indented_body = format!("{INDENT}{INDENT}{statement:?};"); - if tcx.sess.opts.unstable_opts.mir_include_spans { + if options.include_extra_comments { writeln!( w, "{:A$} // {}{}", @@ -689,9 +742,14 @@ where writeln!(w, "{indented_body}")?; } - write_extra(tcx, w, |visitor| { - visitor.visit_statement(statement, current_location); - })?; + write_extra( + tcx, + w, + |visitor| { + visitor.visit_statement(statement, current_location); + }, + options, + )?; extra_data(PassWhere::AfterLocation(current_location), w)?; @@ -701,7 +759,7 @@ where // Terminator at the bottom. extra_data(PassWhere::BeforeLocation(current_location), w)?; let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind); - if tcx.sess.opts.unstable_opts.mir_include_spans { + if options.include_extra_comments { writeln!( w, "{:A$} // {}{}", @@ -718,9 +776,14 @@ where writeln!(w, "{indented_terminator}")?; } - write_extra(tcx, w, |visitor| { - visitor.visit_terminator(data.terminator(), current_location); - })?; + write_extra( + tcx, + w, + |visitor| { + visitor.visit_terminator(data.terminator(), current_location); + }, + options, + )?; extra_data(PassWhere::AfterLocation(current_location), w)?; extra_data(PassWhere::AfterTerminator(block), w)?; @@ -1271,11 +1334,12 @@ fn write_extra<'tcx, F>( tcx: TyCtxt<'tcx>, write: &mut dyn io::Write, mut visit_op: F, + options: PrettyPrintMirOptions, ) -> io::Result<()> where F: FnMut(&mut ExtraComments<'tcx>), { - if tcx.sess.opts.unstable_opts.mir_include_spans { + if options.include_extra_comments { let mut extra_comments = ExtraComments { tcx, comments: vec![] }; visit_op(&mut extra_comments); for comment in extra_comments.comments { @@ -1890,7 +1954,7 @@ pub(crate) fn pretty_print_const_value<'tcx>( /////////////////////////////////////////////////////////////////////////// // Miscellaneous -/// Calc converted u64 decimal into hex and return it's length in chars +/// Calc converted u64 decimal into hex and return its length in chars. /// /// ```ignore (cannot-test-private-function) /// assert_eq!(1, hex_number_length(0)); diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 748ca047754a9..22a4b688c517c 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1307,6 +1307,9 @@ pub enum Rvalue<'tcx> { /// If the type of the place is an array, this is the array length. For slices (`[T]`, not /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is /// ill-formed for places of other types. + /// + /// This cannot be a `UnOp(PtrMetadata, _)` because that expects a value, and we only + /// have a place, and `UnOp(PtrMetadata, RawPtr(place))` is not a thing. Len(Place<'tcx>), /// Performs essentially all of the casts that can be performed via `as`. diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b6443778c936b..969374cb0e540 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -47,7 +47,7 @@ use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; -use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature}; +use crate::middle::codegen_fn_attrs::CodegenFnAttrs; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use crate::middle::lib_features::LibFeatures; @@ -327,7 +327,7 @@ rustc_queries! { } } - /// Returns the list of bounds that are required to be satsified + /// Returns the list of bounds that are required to be satisfied /// by a implementation or definition. For associated types, these /// must be satisfied for an implementation to be well-formed, /// and for opaque types, these are required to be satisfied by @@ -1249,11 +1249,6 @@ rustc_queries! { feedable } - query struct_target_features(def_id: DefId) -> &'tcx [TargetFeature] { - separate_provide_extern - desc { |tcx| "computing target features for struct `{}`", tcx.def_path_str(def_id) } - } - query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet { desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) } } diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index c9bd702cce340..0320a91d142b7 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -337,6 +337,7 @@ macro_rules! define_callbacks { // 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 mem::size_of::>() > 64 { panic!("{}", concat!( diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 8eb3c0156796a..809801c33e1ad 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -88,9 +88,6 @@ pub struct CapturedPlace<'tcx> { /// Represents if `place` can be mutated or not. pub mutability: hir::Mutability, - - /// Region of the resulting reference if the upvar is captured by ref. - pub region: Option>, } impl<'tcx> CapturedPlace<'tcx> { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index d6dbad9dab845..56fcfe8e798b1 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -181,6 +181,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } } + fn evaluation_is_concurrent(&self) -> bool { + self.sess.threads() > 1 + } + fn expand_abstract_consts>>(self, t: T) -> T { self.expand_abstract_consts(t) } @@ -426,7 +430,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { let simp = ty::fast_reject::simplify_type( tcx, self_ty, - ty::fast_reject::TreatParams::ForLookup, + ty::fast_reject::TreatParams::AsRigid, ) .unwrap(); consider_impls_for_simplified_type(simp); @@ -1475,7 +1479,7 @@ impl<'tcx> TyCtxt<'tcx> { /// provides a `TyCtxt`. /// /// By only providing the `TyCtxt` inside of the closure we enforce that the type - /// context and any interned alue (types, args, etc.) can only be used while `ty::tls` + /// context and any interned value (types, args, etc.) can only be used while `ty::tls` /// has a valid reference to the context, to allow formatting values that need it. pub fn create_global_ctxt( s: &'tcx Session, @@ -3093,10 +3097,7 @@ impl<'tcx> TyCtxt<'tcx> { Some(stability) if stability.is_const_unstable() => { // has a `rustc_const_unstable` attribute, check whether the user enabled the // corresponding feature gate. - self.features() - .declared_lib_features - .iter() - .any(|&(sym, _)| sym == stability.feature) + self.features().declared(stability.feature) } // functions without const stability are either stable user written // const fn or the user is using feature gates and we thus don't @@ -3131,11 +3132,11 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn next_trait_solver_globally(self) -> bool { - self.sess.opts.unstable_opts.next_solver.map_or(false, |c| c.globally) + self.sess.opts.unstable_opts.next_solver.globally } pub fn next_trait_solver_in_coherence(self) -> bool { - self.sess.opts.unstable_opts.next_solver.map_or(false, |c| c.coherence) + self.sess.opts.unstable_opts.next_solver.coherence } pub fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { @@ -3186,8 +3187,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Whether this is a trait implementation that has `#[diagnostic::do_not_recommend]` pub fn do_not_recommend_impl(self, def_id: DefId) -> bool { - matches!(self.def_kind(def_id), DefKind::Impl { of_trait: true }) - && self.impl_trait_header(def_id).is_some_and(|header| header.do_not_recommend) + self.get_diagnostic_attr(def_id, sym::do_not_recommend).is_some() } } diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index 91344c4e39c8b..2945a0be424f8 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -3,6 +3,14 @@ pub use rustc_type_ir::fast_reject::*; use super::TyCtxt; -pub type DeepRejectCtxt<'tcx> = rustc_type_ir::fast_reject::DeepRejectCtxt>; +pub type DeepRejectCtxt< + 'tcx, + const INSTANTIATE_LHS_WITH_INFER: bool, + const INSTANTIATE_RHS_WITH_INFER: bool, +> = rustc_type_ir::fast_reject::DeepRejectCtxt< + TyCtxt<'tcx>, + INSTANTIATE_LHS_WITH_INFER, + INSTANTIATE_RHS_WITH_INFER, +>; pub type SimplifiedType = rustc_type_ir::fast_reject::SimplifiedType; diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 698104b0462e3..dd00db8635f4d 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -81,10 +81,6 @@ impl<'tcx> VariantDef { adt: ty::AdtDef<'_>, ) -> InhabitedPredicate<'tcx> { debug_assert!(!adt.is_union()); - if self.is_field_list_non_exhaustive() && !self.def_id.is_local() { - // Non-exhaustive variants from other crates are always considered inhabited. - return InhabitedPredicate::True; - } InhabitedPredicate::all( tcx, self.fields.iter().map(|field| { diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 7d5f0f1e9c415..072951d131917 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -38,7 +38,7 @@ pub struct Instance<'tcx> { pub args: GenericArgsRef<'tcx>, } -/// Describes why a `ReifyShim` was created. This is needed to distingish a ReifyShim created to +/// Describes why a `ReifyShim` was created. This is needed to distinguish a ReifyShim created to /// adjust for things like `#[track_caller]` in a vtable from a `ReifyShim` created to produce a /// function pointer from a vtable entry. /// Currently, this is only used when KCFI is enabled, as only KCFI needs to treat those two diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index d0a9039441dc4..8cec8eac1898a 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1075,11 +1075,13 @@ where // 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 && this.ty.is_box() { + 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 && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()), + unpin: optimize && boxed_ty.is_unpin(tcx, cx.param_env()), global: this.ty.is_box_global(tcx), }); } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e41ea7507efff..e637ced7139a6 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -35,6 +35,7 @@ use rustc_data_structures::tagged_ptr::CopyTaggedPtr; use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; +use rustc_hir::LangItem; use rustc_index::IndexVec; use rustc_macros::{ extension, Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, @@ -262,7 +263,6 @@ pub struct ImplTraitHeader<'tcx> { pub trait_ref: ty::EarlyBinder<'tcx, ty::TraitRef<'tcx>>, pub polarity: ImplPolarity, pub safety: hir::Safety, - pub do_not_recommend: bool, } #[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)] @@ -1570,8 +1570,15 @@ impl<'tcx> TyCtxt<'tcx> { flags.insert(ReprFlags::RANDOMIZE_LAYOUT); } + // box is special, on the one hand the compiler assumes an ordered layout, with the pointer + // always at offset zero. On the other hand we want scalar abi optimizations. + let is_box = self.is_lang_item(did.to_def_id(), LangItem::OwnedBox); + // This is here instead of layout because the choice must make it into metadata. - if !self.consider_optimizing(|| format!("Reorder fields of {:?}", self.def_path_str(did))) { + if is_box + || !self + .consider_optimizing(|| format!("Reorder fields of {:?}", self.def_path_str(did))) + { flags.insert(ReprFlags::IS_LINEAR); } @@ -1789,6 +1796,37 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Get an attribute from the diagnostic attribute namespace + /// + /// This function requests an attribute with the following structure: + /// + /// `#[diagnostic::$attr]` + /// + /// This function performs feature checking, so if an attribute is returned + /// it can be used by the consumer + pub fn get_diagnostic_attr( + self, + did: impl Into, + attr: Symbol, + ) -> Option<&'tcx ast::Attribute> { + let did: DefId = did.into(); + if did.as_local().is_some() { + // it's a crate local item, we need to check feature flags + if rustc_feature::is_stable_diagnostic_attribute(attr, self.features()) { + self.get_attrs_by_path(did, &[sym::diagnostic, sym::do_not_recommend]).next() + } else { + None + } + } else { + // we filter out unstable diagnostic attributes before + // encoding attributes + debug_assert!(rustc_feature::encode_cross_crate(attr)); + self.item_attrs(did) + .iter() + .find(|a| matches!(a.path().as_ref(), [sym::diagnostic, a] if *a == attr)) + } + } + pub fn get_attrs_by_path<'attr>( self, did: DefId, @@ -1819,7 +1857,7 @@ impl<'tcx> TyCtxt<'tcx> { self.get_attrs(did, attr).next().is_some() } - /// Determines whether an item is annotated with a multi-segement attribute + /// Determines whether an item is annotated with a multi-segment attribute pub fn has_attrs_with_path(self, did: impl Into, attrs: &[Symbol]) -> bool { self.get_attrs_by_path(did.into(), attrs).next().is_some() } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index be611e19b49a7..7e1255f606c35 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -59,7 +59,6 @@ trivially_parameterized_over_tcx! { std::string::String, crate::metadata::ModChild, crate::middle::codegen_fn_attrs::CodegenFnAttrs, - crate::middle::codegen_fn_attrs::TargetFeature, crate::middle::debugger_visualizer::DebuggerVisualizerFile, crate::middle::exported_symbols::SymbolExportInfo, crate::middle::lib_features::FeatureStability, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c6621a7a64331..730ba265b19d6 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1136,6 +1136,7 @@ impl<'tcx> Ty<'tcx> { } /// Tests if this is any kind of primitive pointer type (reference, raw pointer, fn pointer). + /// `Box` is *not* considered a pointer here! #[inline] pub fn is_any_ptr(self) -> bool { self.is_ref() || self.is_unsafe_ptr() || self.is_fn_ptr() @@ -1149,7 +1150,10 @@ impl<'tcx> Ty<'tcx> { } } - /// Tests whether this is a Box using the global allocator. + /// Tests whether this is a Box definitely using the global allocator. + /// + /// If the allocator is still generic, the answer is `false`, but it may + /// later turn out that it does use the global allocator. #[inline] pub fn is_box_global(self, tcx: TyCtxt<'tcx>) -> bool { match self.kind() { @@ -1167,14 +1171,19 @@ impl<'tcx> Ty<'tcx> { } } - /// Panics if called on any type other than `Box`. - pub fn boxed_ty(self) -> Ty<'tcx> { + pub fn boxed_ty(self) -> Option> { match self.kind() { - Adt(def, args) if def.is_box() => args.type_at(0), - _ => bug!("`boxed_ty` is called on non-box type {:?}", self), + Adt(def, args) if def.is_box() => Some(args.type_at(0)), + _ => None, } } + /// Panics if called on any type other than `Box`. + pub fn expect_boxed_ty(self) -> Ty<'tcx> { + self.boxed_ty() + .unwrap_or_else(|| bug!("`expect_boxed_ty` is called on non-box type {:?}", self)) + } + /// A scalar type is one that denotes an atomic datum, with no sub-components. /// (A RawPtr is scalar because it represents a non-managed pointer, so its /// contents are abstract to rustc.) @@ -1320,7 +1329,7 @@ impl<'tcx> Ty<'tcx> { /// Some types -- notably unsafe ptrs -- can only be dereferenced explicitly. pub fn builtin_deref(self, explicit: bool) -> Option> { match *self.kind() { - Adt(def, _) if def.is_box() => Some(self.boxed_ty()), + _ if let Some(boxed) = self.boxed_ty() => Some(boxed), Ref(_, ty, _) => Some(ty), RawPtr(ty, _) if explicit => Some(ty), _ => None, @@ -1927,7 +1936,7 @@ impl<'tcx> Ty<'tcx> { /// Returns `true` when the outermost type cannot be further normalized, /// resolved, or instantiated. This includes all primitive types, but also - /// things like ADTs and trait objects, sice even if their arguments or + /// things like ADTs and trait objects, since even if their arguments or /// nested types may be further simplified, the outermost [`TyKind`] or /// type constructor remains the same. pub fn is_known_rigid(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index dfb137f738f1e..82690f70e5f18 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -168,9 +168,9 @@ impl<'tcx> TyCtxt<'tcx> { // whose outer level is not a parameter or projection. Especially for things like // `T: Clone` this is incredibly useful as we would otherwise look at all the impls // of `Clone` for `Option`, `Vec`, `ConcreteType` and so on. - // Note that we're using `TreatParams::ForLookup` to query `non_blanket_impls` while using - // `TreatParams::AsCandidateKey` while actually adding them. - if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::ForLookup) { + // Note that we're using `TreatParams::AsRigid` to query `non_blanket_impls` while using + // `TreatParams::InstantiateWithInfer` while actually adding them. + if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::AsRigid) { if let Some(impls) = impls.non_blanket_impls.get(&simp) { for &impl_def_id in impls { f(impl_def_id); @@ -190,7 +190,9 @@ impl<'tcx> TyCtxt<'tcx> { self_ty: Ty<'tcx>, ) -> impl Iterator + 'tcx { let impls = self.trait_impls_of(trait_def_id); - if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::AsCandidateKey) { + if let Some(simp) = + fast_reject::simplify_type(self, self_ty, TreatParams::InstantiateWithInfer) + { if let Some(impls) = impls.non_blanket_impls.get(&simp) { return impls.iter().copied(); } @@ -239,7 +241,7 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity(); if let Some(simplified_self_ty) = - fast_reject::simplify_type(tcx, impl_self_ty, TreatParams::AsCandidateKey) + fast_reject::simplify_type(tcx, impl_self_ty, TreatParams::InstantiateWithInfer) { impls.non_blanket_impls.entry(simplified_self_ty).or_default().push(impl_def_id); } else { diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index efbccca77c1f0..d70ff8258d04c 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1628,7 +1628,7 @@ impl<'tcx> ExplicitSelf<'tcx> { _ if is_self_ty(self_arg_ty) => ByValue, ty::Ref(region, ty, mutbl) if is_self_ty(ty) => ByReference(region, mutbl), ty::RawPtr(ty, mutbl) if is_self_ty(ty) => ByRawPointer(mutbl), - ty::Adt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => ByBox, + _ if self_arg_ty.boxed_ty().is_some_and(is_self_ty) => ByBox, _ => Other, } } diff --git a/compiler/rustc_middle/src/util/common.rs b/compiler/rustc_middle/src/util/common.rs index 2038d3f84484e..223b2b3bfe4de 100644 --- a/compiler/rustc_middle/src/util/common.rs +++ b/compiler/rustc_middle/src/util/common.rs @@ -20,19 +20,3 @@ pub fn to_readable_str(mut val: usize) -> String { groups.join("_") } - -// const wrapper for `if let Some((_, tail)) = name.rsplit_once(':') { tail } else { name }` -pub const fn c_name(name: &'static str) -> &'static str { - // FIXME Simplify the implementation once more `str` methods get const-stable. - // and inline into call site - let bytes = name.as_bytes(); - let mut i = bytes.len(); - while i > 0 && bytes[i - 1] != b':' { - i = i - 1; - } - let (_, bytes) = bytes.split_at(i); - match std::str::from_utf8(bytes) { - Ok(name) => name, - Err(_) => name, - } -} diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index a42e8ff0168b2..7a10e627ccd89 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -125,17 +125,6 @@ mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior .label = initializing type with `rustc_layout_scalar_valid_range` attr -mir_build_initializing_type_with_target_feature_requires_unsafe = - initializing type with `target_feature` attr is unsafe and requires unsafe block - .note = this struct can only be constructed if the corresponding `target_feature`s are available - .label = initializing type with `target_feature` attr - -mir_build_initializing_type_with_target_feature_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - initializing type with `target_feature` attr is unsafe and requires unsafe function or block - .note = this struct can only be constructed if the corresponding `target_feature`s are available - .label = initializing type with `target_feature` attr - - mir_build_inline_assembly_requires_unsafe = use of inline assembly is unsafe and requires unsafe block .note = inline assembly is entirely unchecked and can cause undefined behavior @@ -398,11 +387,6 @@ mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe = .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior .label = initializing type with `rustc_layout_scalar_valid_range` attr -mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_target_feature_requires_unsafe = - initializing type with `target_feature` attr is unsafe and requires unsafe block - .note = this struct can only be constructed if the corresponding `target_feature`s are available - .label = initializing type with `target_feature` attr - mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe = use of inline assembly is unsafe and requires unsafe block .note = inline assembly is entirely unchecked and can cause undefined behavior diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 4ce796cea7ad3..c7fcfe3ce2aa0 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -461,18 +461,14 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { }; self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id)); } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() { - // If the called function has explicit target features the calling function hasn't, + // If the called function has target features the calling function hasn't, // the call requires `unsafe`. Don't check this on wasm // targets, though. For more information on wasm see the // is_like_wasm check in hir_analysis/src/collect.rs - // Implicit target features are OK because they are either a consequence of some - // explicit target feature (which is checked to be present in the caller) or - // come from a witness argument. let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; if !self.tcx.sess.target.options.is_like_wasm && !callee_features.iter().all(|feature| { - feature.implied - || self.body_target_features.iter().any(|f| f.name == feature.name) + self.body_target_features.iter().any(|f| f.name == feature.name) }) { let missing: Vec<_> = callee_features @@ -546,16 +542,10 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { user_ty: _, fields: _, base: _, - }) => { - match self.tcx.layout_scalar_valid_range(adt_def.did()) { - (Bound::Unbounded, Bound::Unbounded) => {} - _ => self.requires_unsafe(expr.span, InitializingTypeWith), - } - if !self.tcx.struct_target_features(adt_def.did()).is_empty() { - self.requires_unsafe(expr.span, ConstructingTargetFeaturesType) - } - } - + }) => match self.tcx.layout_scalar_valid_range(adt_def.did()) { + (Bound::Unbounded, Bound::Unbounded) => {} + _ => self.requires_unsafe(expr.span, InitializingTypeWith), + }, ExprKind::Closure(box ClosureExpr { closure_id, args: _, @@ -657,7 +647,6 @@ enum UnsafeOpKind { CallToUnsafeFunction(Option), UseOfInlineAssembly, InitializingTypeWith, - ConstructingTargetFeaturesType, UseOfMutableStatic, UseOfExternStatic, DerefOfRawPointer, @@ -739,15 +728,6 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }, ), - ConstructingTargetFeaturesType => tcx.emit_node_span_lint( - UNSAFE_OP_IN_UNSAFE_FN, - hir_id, - span, - UnsafeOpInUnsafeFnInitializingTypeWithTargetFeatureRequiresUnsafe { - span, - unsafe_not_inherited_note, - }, - ), UseOfMutableStatic => tcx.emit_node_span_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, @@ -905,20 +885,6 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }); } - ConstructingTargetFeaturesType if unsafe_op_in_unsafe_fn_allowed => { - dcx.emit_err( - InitializingTypeWithTargetFeatureRequiresUnsafeUnsafeOpInUnsafeFnAllowed { - span, - unsafe_not_inherited_note, - }, - ); - } - ConstructingTargetFeaturesType => { - dcx.emit_err(InitializingTypeWithTargetFeatureRequiresUnsafe { - span, - unsafe_not_inherited_note, - }); - } UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => { dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span, diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index e7d6239aa9bf6..7f9eefd1d52e3 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -86,16 +86,6 @@ pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_target_feature_requires_unsafe, code = E0133)] -#[note] -pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithTargetFeatureRequiresUnsafe { - #[label] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) unsafe_not_inherited_note: Option, -} - #[derive(LintDiagnostic)] #[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe, code = E0133)] #[note] @@ -260,17 +250,6 @@ pub(crate) struct InitializingTypeWithRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(Diagnostic)] -#[diag(mir_build_initializing_type_with_target_feature_requires_unsafe, code = E0133)] -#[note] -pub(crate) struct InitializingTypeWithTargetFeatureRequiresUnsafe { - #[primary_span] - #[label] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) unsafe_not_inherited_note: Option, -} - #[derive(Diagnostic)] #[diag( mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, @@ -285,20 +264,6 @@ pub(crate) struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { pub(crate) unsafe_not_inherited_note: Option, } -#[derive(Diagnostic)] -#[diag( - mir_build_initializing_type_with_target_feature_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, - code = E0133 -)] -#[note] -pub(crate) struct InitializingTypeWithTargetFeatureRequiresUnsafeUnsafeOpInUnsafeFnAllowed { - #[primary_span] - #[label] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) unsafe_not_inherited_note: Option, -} - #[derive(Diagnostic)] #[diag(mir_build_mutable_static_requires_unsafe, code = E0133)] #[note] diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index ddfd0739358d1..a88927427bac2 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -51,15 +51,15 @@ use crate::{ /// Similarly, at a given `drop` statement, the set-intersection /// between this data and `MaybeUninitializedPlaces` yields the set of /// places that would require a dynamic drop-flag at that statement. -pub struct MaybeInitializedPlaces<'a, 'mir, 'tcx> { +pub struct MaybeInitializedPlaces<'a, 'tcx> { tcx: TyCtxt<'tcx>, - body: &'mir Body<'tcx>, + body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>, skip_unreachable_unwind: bool, } -impl<'a, 'mir, 'tcx> MaybeInitializedPlaces<'a, 'mir, 'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, body: &'mir Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self { +impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self { MaybeInitializedPlaces { tcx, body, move_data, skip_unreachable_unwind: false } } @@ -85,7 +85,7 @@ impl<'a, 'mir, 'tcx> MaybeInitializedPlaces<'a, 'mir, 'tcx> { } } -impl<'a, 'mir, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'mir, 'tcx> { +impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> { fn move_data(&self) -> &MoveData<'tcx> { self.move_data } @@ -126,17 +126,17 @@ impl<'a, 'mir, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'mir, 'tcx /// Similarly, at a given `drop` statement, the set-intersection /// between this data and `MaybeInitializedPlaces` yields the set of /// places that would require a dynamic drop-flag at that statement. -pub struct MaybeUninitializedPlaces<'a, 'mir, 'tcx> { +pub struct MaybeUninitializedPlaces<'a, 'tcx> { tcx: TyCtxt<'tcx>, - body: &'mir Body<'tcx>, + body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>, mark_inactive_variants_as_uninit: bool, skip_unreachable_unwind: BitSet, } -impl<'a, 'mir, 'tcx> MaybeUninitializedPlaces<'a, 'mir, 'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, body: &'mir Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self { +impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self { MaybeUninitializedPlaces { tcx, body, @@ -165,7 +165,7 @@ impl<'a, 'mir, 'tcx> MaybeUninitializedPlaces<'a, 'mir, 'tcx> { } } -impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, '_, 'tcx> { +impl<'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { fn move_data(&self) -> &MoveData<'tcx> { self.move_data } @@ -251,24 +251,24 @@ impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { /// c = S; // {a, b, c, d } /// } /// ``` -pub struct EverInitializedPlaces<'a, 'mir, 'tcx> { - body: &'mir Body<'tcx>, +pub struct EverInitializedPlaces<'a, 'tcx> { + body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>, } -impl<'a, 'mir, 'tcx> EverInitializedPlaces<'a, 'mir, 'tcx> { - pub fn new(body: &'mir Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self { +impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> { + pub fn new(body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self { EverInitializedPlaces { body, move_data } } } -impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, '_, 'tcx> { +impl<'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'_, 'tcx> { fn move_data(&self) -> &MoveData<'tcx> { self.move_data } } -impl<'a, 'mir, 'tcx> MaybeInitializedPlaces<'a, 'mir, 'tcx> { +impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { fn update_bits( trans: &mut impl GenKill, path: MovePathIndex, @@ -281,7 +281,7 @@ impl<'a, 'mir, 'tcx> MaybeInitializedPlaces<'a, 'mir, 'tcx> { } } -impl<'a, 'tcx> MaybeUninitializedPlaces<'a, '_, 'tcx> { +impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> { fn update_bits( trans: &mut impl GenKill, path: MovePathIndex, @@ -307,7 +307,7 @@ impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { } } -impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, '_, 'tcx> { +impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { /// There can be many more `MovePathIndex` than there are locals in a MIR body. /// We use a chunked bitset to avoid paying too high a memory footprint. type Domain = MaybeReachable>; @@ -329,7 +329,7 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, '_, 'tcx> { } } -impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, '_, 'tcx> { +impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { type Idx = MovePathIndex; fn domain_size(&self, _: &Body<'tcx>) -> usize { @@ -442,7 +442,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, '_, 'tcx> { } } -impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, '_, 'tcx> { +impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { /// There can be many more `MovePathIndex` than there are locals in a MIR body. /// We use a chunked bitset to avoid paying too high a memory footprint. type Domain = ChunkedBitSet; @@ -466,7 +466,7 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, '_, 'tcx> { } } -impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, '_, 'tcx> { +impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { type Idx = MovePathIndex; fn domain_size(&self, _: &Body<'tcx>) -> usize { @@ -643,7 +643,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { } } -impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, '_, 'tcx> { +impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> { /// There can be many more `InitIndex` than there are locals in a MIR body. /// We use a chunked bitset to avoid paying too high a memory footprint. type Domain = ChunkedBitSet; @@ -662,7 +662,7 @@ impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, '_, 'tcx> { } } -impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, '_, 'tcx> { +impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { type Idx = InitIndex; fn domain_size(&self, _: &Body<'tcx>) -> usize { diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 14390723ba4b6..87ea729923895 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -16,6 +16,7 @@ use super::{ struct MoveDataBuilder<'a, 'tcx, F> { body: &'a Body<'tcx>, + loc: Location, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, data: MoveData<'tcx>, @@ -56,6 +57,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { MoveDataBuilder { body, + loc: Location::START, tcx, param_env, data: MoveData { @@ -107,7 +109,7 @@ enum MovePathResult { Error, } -impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { +impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { /// This creates a MovePath for a given place, returning an `MovePathError` /// if that place can't be moved from. /// @@ -116,7 +118,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { /// /// Maybe we should have separate "borrowck" and "moveck" modes. fn move_path_for(&mut self, place: Place<'tcx>) -> MovePathResult { - let data = &mut self.builder.data; + let data = &mut self.data; debug!("lookup({:?})", place); let Some(mut base) = data.rev_lookup.find_local(place.local) else { @@ -131,8 +133,8 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { let mut union_path = None; for (place_ref, elem) in data.rev_lookup.un_derefer.iter_projections(place.as_ref()) { - let body = self.builder.body; - let tcx = self.builder.tcx; + let body = self.body; + let tcx = self.tcx; let place_ty = place_ref.ty(body, tcx).ty; if place_ty.references_error() { return MovePathResult::Error; @@ -238,7 +240,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | ProjectionElem::Downcast(_, _) => (), } let elem_ty = PlaceTy::from_ty(place_ty).projection_ty(tcx, elem).ty; - if !(self.builder.filter)(elem_ty) { + if !(self.filter)(elem_ty) { return MovePathResult::Error; } if union_path.is_none() { @@ -274,7 +276,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { data: MoveData { rev_lookup, move_paths, path_map, init_path_map, .. }, tcx, .. - } = self.builder; + } = self; *rev_lookup.projections.entry((base, elem.lift())).or_insert_with(move || { new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx)) }) @@ -285,9 +287,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { // drop), so this not being a valid move path is OK. let _ = self.move_path_for(place); } -} -impl<'a, 'tcx, F> MoveDataBuilder<'a, 'tcx, F> { fn finalize(self) -> MoveData<'tcx> { debug!("{}", { debug!("moves for {:?}:", self.body.span); @@ -317,12 +317,12 @@ pub(super) fn gather_moves<'tcx>( for (bb, block) in body.basic_blocks.iter_enumerated() { for (i, stmt) in block.statements.iter().enumerate() { - let source = Location { block: bb, statement_index: i }; - builder.gather_statement(source, stmt); + builder.loc = Location { block: bb, statement_index: i }; + builder.gather_statement(stmt); } - let terminator_loc = Location { block: bb, statement_index: block.statements.len() }; - builder.gather_terminator(terminator_loc, block.terminator()); + builder.loc = Location { block: bb, statement_index: block.statements.len() }; + builder.gather_terminator(block.terminator()); } builder.finalize() @@ -345,30 +345,14 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { } } - fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) { - debug!("gather_statement({:?}, {:?})", loc, stmt); - (Gatherer { builder: self, loc }).gather_statement(stmt); - } - - fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) { - debug!("gather_terminator({:?}, {:?})", loc, term); - (Gatherer { builder: self, loc }).gather_terminator(term); - } -} - -struct Gatherer<'b, 'a, 'tcx, F> { - builder: &'b mut MoveDataBuilder<'a, 'tcx, F>, - loc: Location, -} - -impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { fn gather_statement(&mut self, stmt: &Statement<'tcx>) { + debug!("gather_statement({:?}, {:?})", self.loc, stmt); match &stmt.kind { StatementKind::Assign(box (place, Rvalue::CopyForDeref(reffed))) => { let local = place.as_local().unwrap(); - assert!(self.builder.body.local_decls[local].is_deref_temp()); + assert!(self.body.local_decls[local].is_deref_temp()); - let rev_lookup = &mut self.builder.data.rev_lookup; + let rev_lookup = &mut self.data.rev_lookup; rev_lookup.un_derefer.insert(local, reffed.as_ref()); let base_local = rev_lookup.un_derefer.deref_chain(local).first().unwrap().local; @@ -380,7 +364,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { // 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.builder.tcx.mk_place_deref(*place)); + 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); @@ -393,7 +377,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { StatementKind::StorageLive(_) => {} StatementKind::StorageDead(local) => { // DerefTemp locals (results of CopyForDeref) don't actually move anything. - if !self.builder.body.local_decls[*local].is_deref_temp() { + if !self.body.local_decls[*local].is_deref_temp() { self.gather_move(Place::from(*local)); } } @@ -443,6 +427,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { } fn gather_terminator(&mut self, term: &Terminator<'tcx>) { + debug!("gather_terminator({:?}, {:?})", self.loc, term); match term.kind { TerminatorKind::Goto { target: _ } | TerminatorKind::FalseEdge { .. } @@ -551,7 +536,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { // `ConstIndex` patterns. This is done to ensure that all move paths // are disjoint, which is expected by drop elaboration. let base_place = - Place { local: place.local, projection: self.builder.tcx.mk_place_elems(base) }; + Place { local: place.local, projection: self.tcx.mk_place_elems(base) }; let base_path = match self.move_path_for(base_place) { MovePathResult::Path(path) => path, MovePathResult::Union(path) => { @@ -562,11 +547,9 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { return; } }; - let base_ty = base_place.ty(self.builder.body, self.builder.tcx).ty; + let base_ty = base_place.ty(self.body, self.tcx).ty; let len: u64 = match base_ty.kind() { - ty::Array(_, size) => { - size.eval_target_usize(self.builder.tcx, self.builder.param_env) - } + ty::Array(_, size) => size.eval_target_usize(self.tcx, self.param_env), _ => bug!("from_end: false slice pattern of non-array type"), }; for offset in from..to { @@ -587,13 +570,13 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { } fn record_move(&mut self, place: Place<'tcx>, path: MovePathIndex) { - let move_out = self.builder.data.moves.push(MoveOut { path, source: self.loc }); + let move_out = self.data.moves.push(MoveOut { path, source: self.loc }); debug!( "gather_move({:?}, {:?}): adding move {:?} of {:?}", self.loc, place, move_out, path ); - self.builder.data.path_map[path].push(move_out); - self.builder.data.loc_map[self.loc].push(move_out); + self.data.path_map[path].push(move_out); + self.data.loc_map[self.loc].push(move_out); } fn gather_init(&mut self, place: PlaceRef<'tcx>, kind: InitKind) { @@ -604,13 +587,13 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { // Check if we are assigning into a field of a union, if so, lookup the place // of the union so it is marked as initialized again. if let Some((place_base, ProjectionElem::Field(_, _))) = place.last_projection() { - if place_base.ty(self.builder.body, self.builder.tcx).ty.is_union() { + if place_base.ty(self.body, self.tcx).ty.is_union() { place = place_base; } } - if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(place) { - let init = self.builder.data.inits.push(Init { + if let LookupResult::Exact(path) = self.data.rev_lookup.find(place) { + let init = self.data.inits.push(Init { location: InitLocation::Statement(self.loc), path, kind, @@ -621,8 +604,8 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { self.loc, place, init, path ); - self.builder.data.init_path_map[path].push(init); - self.builder.data.init_loc_map[self.loc].push(init); + self.data.init_path_map[path].push(init); + self.data.init_loc_map[self.loc].push(init); } } } diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 0171cc8591809..8c3e6f49b1618 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -1,7 +1,7 @@ use rustc_ast::MetaItem; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; -use rustc_middle::mir::{self, Body, Local, Location, MirPass}; +use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; @@ -18,8 +18,6 @@ use crate::impls::{ use crate::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; use crate::{Analysis, JoinSemiLattice, ResultsCursor}; -pub struct SanityCheck; - fn has_rustc_mir_with(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Option { for attr in tcx.get_attrs(def_id, sym::rustc_mir) { let items = attr.meta_item_list(); @@ -33,53 +31,50 @@ fn has_rustc_mir_with(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Option MirPass<'tcx> for SanityCheck { - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let def_id = body.source.def_id(); - if !tcx.has_attr(def_id, sym::rustc_mir) { - debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); - return; - } else { - debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); - } +pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let def_id = body.source.def_id(); + if !tcx.has_attr(def_id, sym::rustc_mir) { + debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); + return; + } else { + debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); + } - let param_env = tcx.param_env(def_id); - let move_data = MoveData::gather_moves(body, tcx, param_env, |_| true); + let param_env = tcx.param_env(def_id); + let move_data = MoveData::gather_moves(body, tcx, param_env, |_| true); - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { - let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) - .into_engine(tcx, body) - .iterate_to_fixpoint(); + if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { + let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) + .into_engine(tcx, body) + .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, flow_inits.into_results_cursor(body)); - } + sanity_check_via_rustc_peek(tcx, flow_inits.into_results_cursor(body)); + } - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() { - let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data) - .into_engine(tcx, body) - .iterate_to_fixpoint(); + if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() { + let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data) + .into_engine(tcx, body) + .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body)); - } + sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body)); + } - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_definite_init).is_some() { - let flow_def_inits = DefinitelyInitializedPlaces::new(body, &move_data) - .into_engine(tcx, body) - .iterate_to_fixpoint(); + if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_definite_init).is_some() { + let flow_def_inits = DefinitelyInitializedPlaces::new(body, &move_data) + .into_engine(tcx, body) + .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, flow_def_inits.into_results_cursor(body)); - } + sanity_check_via_rustc_peek(tcx, flow_def_inits.into_results_cursor(body)); + } - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() { - let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint(); + if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() { + let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, flow_liveness.into_results_cursor(body)); - } + sanity_check_via_rustc_peek(tcx, flow_liveness.into_results_cursor(body)); + } - if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() { - tcx.dcx().emit_fatal(StopAfterDataFlowEndedCompilation); - } + if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() { + tcx.dcx().emit_fatal(StopAfterDataFlowEndedCompilation); } } diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 2b20a35b61e29..0540436632014 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -923,14 +923,14 @@ impl<'tcx> Map<'tcx> { } } -struct PlaceCollector<'a, 'b, 'tcx> { +struct PlaceCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, - body: &'b Body<'tcx>, + body: &'a Body<'tcx>, map: &'a mut Map<'tcx>, assignments: FxIndexSet<(PlaceIndex, PlaceIndex)>, } -impl<'tcx> PlaceCollector<'_, '_, 'tcx> { +impl<'tcx> PlaceCollector<'_, 'tcx> { #[tracing::instrument(level = "trace", skip(self))] fn register_place(&mut self, place: Place<'tcx>) -> Option { // Create a place for this projection. @@ -967,7 +967,7 @@ impl<'tcx> PlaceCollector<'_, '_, 'tcx> { } } -impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, '_, 'tcx> { +impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, 'tcx> { #[tracing::instrument(level = "trace", skip(self))] fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, _: Location) { if !ctxt.is_use() { diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs index edb6bc4fbea2f..8291df9be5349 100644 --- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs +++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs @@ -20,9 +20,9 @@ use rustc_target::spec::PanicStrategy; /// This forces all unwinds, in panic=abort mode happening in foreign code, to /// trigger a process abort. #[derive(PartialEq)] -pub struct AbortUnwindingCalls; +pub(super) struct AbortUnwindingCalls; -impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls { +impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let def_id = body.source.def_id(); let kind = tcx.def_kind(def_id); @@ -50,9 +50,7 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls { // with a function call, and whose function we're calling may unwind. // This will filter to functions with `extern "C-unwind"` ABIs, for // example. - let mut calls_to_terminate = Vec::new(); - let mut cleanups_to_remove = Vec::new(); - for (id, block) in body.basic_blocks.iter_enumerated() { + for block in body.basic_blocks.as_mut() { if block.is_cleanup { continue; } @@ -61,7 +59,7 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls { let call_can_unwind = match &terminator.kind { TerminatorKind::Call { func, .. } => { - let ty = func.ty(body, tcx); + let ty = func.ty(&body.local_decls, tcx); let sig = ty.fn_sig(tcx); let fn_def_id = match ty.kind() { ty::FnPtr(..) => None, @@ -86,33 +84,22 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls { _ => continue, }; - // If this function call can't unwind, then there's no need for it - // to have a landing pad. This means that we can remove any cleanup - // registered for it. if !call_can_unwind { - cleanups_to_remove.push(id); - continue; - } - - // Otherwise if this function can unwind, then if the outer function - // can also unwind there's nothing to do. If the outer function - // can't unwind, however, we need to change the landing pad for this - // function call to one that aborts. - if !body_can_unwind { - calls_to_terminate.push(id); + // If this function call can't unwind, then there's no need for it + // to have a landing pad. This means that we can remove any cleanup + // registered for it. + let cleanup = block.terminator_mut().unwind_mut().unwrap(); + *cleanup = UnwindAction::Unreachable; + } else if !body_can_unwind { + // Otherwise if this function can unwind, then if the outer function + // can also unwind there's nothing to do. If the outer function + // can't unwind, however, we need to change the landing pad for this + // function call to one that aborts. + let cleanup = block.terminator_mut().unwind_mut().unwrap(); + *cleanup = UnwindAction::Terminate(UnwindTerminateReason::Abi); } } - for id in calls_to_terminate { - let cleanup = body.basic_blocks_mut()[id].terminator_mut().unwind_mut().unwrap(); - *cleanup = UnwindAction::Terminate(UnwindTerminateReason::Abi); - } - - for id in cleanups_to_remove { - let cleanup = body.basic_blocks_mut()[id].terminator_mut().unwind_mut().unwrap(); - *cleanup = UnwindAction::Unreachable; - } - // We may have invalidated some `cleanup` blocks so clean those up now. super::simplify::remove_dead_blocks(body); } diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs index a47c8d94bba22..24c955c0c78c6 100644 --- a/compiler/rustc_mir_transform/src/add_call_guards.rs +++ b/compiler/rustc_mir_transform/src/add_call_guards.rs @@ -1,13 +1,14 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use tracing::debug; #[derive(PartialEq)] -pub enum AddCallGuards { +pub(super) enum AddCallGuards { AllCallEdges, CriticalCallEdges, } -pub use self::AddCallGuards::*; +pub(super) use self::AddCallGuards::*; /** * Breaks outgoing critical edges for call terminators in the MIR. @@ -29,14 +30,8 @@ pub use self::AddCallGuards::*; * */ -impl<'tcx> MirPass<'tcx> for AddCallGuards { +impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - self.add_call_guards(body); - } -} - -impl AddCallGuards { - pub fn add_call_guards(&self, body: &mut Body<'_>) { let mut pred_count: IndexVec<_, _> = body.basic_blocks.predecessors().iter().map(|ps| ps.len()).collect(); pred_count[START_BLOCK] += 1; diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs index cd850e2d73189..74df5f7479e06 100644 --- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs +++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs @@ -1,6 +1,7 @@ use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use tracing::debug; use crate::util; @@ -34,40 +35,39 @@ use crate::util; /// /// The storage instructions are required to avoid stack space /// blowup. -pub struct AddMovesForPackedDrops; +pub(super) struct AddMovesForPackedDrops; -impl<'tcx> MirPass<'tcx> for AddMovesForPackedDrops { +impl<'tcx> crate::MirPass<'tcx> for AddMovesForPackedDrops { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!("add_moves_for_packed_drops({:?} @ {:?})", body.source, body.span); - add_moves_for_packed_drops(tcx, body); - } -} - -pub fn add_moves_for_packed_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let patch = add_moves_for_packed_drops_patch(tcx, body); - patch.apply(body); -} -fn add_moves_for_packed_drops_patch<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> MirPatch<'tcx> { - let def_id = body.source.def_id(); - let mut patch = MirPatch::new(body); - let param_env = tcx.param_env(def_id); + let def_id = body.source.def_id(); + let mut patch = MirPatch::new(body); + let param_env = tcx.param_env(def_id); - for (bb, data) in body.basic_blocks.iter_enumerated() { - let loc = Location { block: bb, statement_index: data.statements.len() }; - let terminator = data.terminator(); + for (bb, data) in body.basic_blocks.iter_enumerated() { + let loc = Location { block: bb, statement_index: data.statements.len() }; + let terminator = data.terminator(); - match terminator.kind { - TerminatorKind::Drop { place, .. } - if util::is_disaligned(tcx, body, param_env, place) => - { - add_move_for_packed_drop(tcx, body, &mut patch, terminator, loc, data.is_cleanup); + match terminator.kind { + TerminatorKind::Drop { place, .. } + if util::is_disaligned(tcx, body, param_env, place) => + { + add_move_for_packed_drop( + tcx, + body, + &mut patch, + terminator, + loc, + data.is_cleanup, + ); + } + _ => {} } - _ => {} } - } - patch + patch.apply(body); + } } fn add_move_for_packed_drop<'tcx>( @@ -85,7 +85,7 @@ fn add_move_for_packed_drop<'tcx>( let source_info = terminator.source_info; let ty = place.ty(body, tcx).ty; - let temp = patch.new_temp(ty, terminator.source_info.span); + let temp = patch.new_temp(ty, source_info.span); let storage_dead_block = patch.new_block(BasicBlockData { statements: vec![Statement { source_info, kind: StatementKind::StorageDead(temp) }], diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index 12a68790374e5..8ad364ed05502 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -8,7 +8,7 @@ use rustc_hir::LangItem; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; -pub struct AddRetag; +pub(super) struct AddRetag; /// Determine whether this type may contain a reference (or box), and thus needs retagging. /// We will only recurse `depth` times into Tuples/ADTs to bound the cost of this. @@ -48,7 +48,7 @@ fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> b } } -impl<'tcx> MirPass<'tcx> for AddRetag { +impl<'tcx> crate::MirPass<'tcx> for AddRetag { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.opts.unstable_opts.mir_emit_retag } @@ -60,7 +60,9 @@ impl<'tcx> MirPass<'tcx> for AddRetag { let basic_blocks = body.basic_blocks.as_mut(); let local_decls = &body.local_decls; let needs_retag = |place: &Place<'tcx>| { - !place.is_indirect_first_projection() // we're not really interested in stores to "outside" locations, they are hard to keep track of anyway + // We're not really interested in stores to "outside" locations, they are hard to keep + // track of anyway. + !place.is_indirect_first_projection() && may_contain_reference(place.ty(&*local_decls, tcx).ty, /*depth*/ 3, tcx) && !local_decls[place.local].is_deref_temp() }; @@ -129,14 +131,16 @@ impl<'tcx> MirPass<'tcx> for AddRetag { StatementKind::Assign(box (ref place, ref rvalue)) => { let add_retag = match rvalue { // Ptr-creating operations already do their own internal retagging, no - // need to also add a retag statement. - // *Except* if we are deref'ing a Box, because those get desugared to directly working - // with the inner raw pointer! That's relevant for `RawPtr` as Miri otherwise makes it + // need to also add a retag statement. *Except* if we are deref'ing a + // Box, because those get desugared to directly working with the inner + // raw pointer! That's relevant for `RawPtr` as Miri otherwise makes it // a NOP when the original pointer is already raw. Rvalue::RawPtr(_mutbl, place) => { // Using `is_box_global` here is a bit sketchy: if this code is // generic over the allocator, we'll not add a retag! This is a hack // to make Stacked Borrows compatible with custom allocator code. + // It means the raw pointer inherits the tag of the box, which mostly works + // but can sometimes lead to unexpected aliasing errors. // Long-term, we'll want to move to an aliasing model where "cast to // raw pointer" is a complete NOP, and then this will no longer be // an issue. diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs index 04204c68f7b76..e585e338613d8 100644 --- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs +++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs @@ -1,15 +1,14 @@ -use rustc_index::IndexVec; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -pub struct Subtyper; +pub(super) struct Subtyper; -pub struct SubTypeChecker<'a, 'tcx> { +struct SubTypeChecker<'a, 'tcx> { tcx: TyCtxt<'tcx>, patcher: MirPatch<'tcx>, - local_decls: &'a IndexVec>, + local_decls: &'a LocalDecls<'tcx>, } impl<'a, 'tcx> MutVisitor<'tcx> for SubTypeChecker<'a, 'tcx> { @@ -52,18 +51,14 @@ impl<'a, 'tcx> MutVisitor<'tcx> for SubTypeChecker<'a, 'tcx> { // // gets transformed to // let temp: rval_ty = rval; // let place: place_ty = temp as place_ty; -pub fn subtype_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let patch = MirPatch::new(body); - let mut checker = SubTypeChecker { tcx, patcher: patch, local_decls: &body.local_decls }; - - for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() { - checker.visit_basic_block_data(bb, data); - } - checker.patcher.apply(body); -} - -impl<'tcx> MirPass<'tcx> for Subtyper { +impl<'tcx> crate::MirPass<'tcx> for Subtyper { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - subtype_finder(tcx, body); + let patch = MirPatch::new(body); + let mut checker = SubTypeChecker { tcx, patcher: patch, local_decls: &body.local_decls }; + + for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() { + checker.visit_basic_block_data(bb, data); + } + checker.patcher.apply(body); } } diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index 5dfdcfc8b9446..e1d5152ae5150 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -5,10 +5,11 @@ use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceC use rustc_middle::mir::*; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; use rustc_session::Session; +use tracing::{debug, trace}; -pub struct CheckAlignment; +pub(super) struct CheckAlignment; -impl<'tcx> MirPass<'tcx> for CheckAlignment { +impl<'tcx> crate::MirPass<'tcx> for CheckAlignment { fn is_enabled(&self, sess: &Session) -> bool { // FIXME(#112480) MSVC and rustc disagree on minimum stack alignment on x86 Windows if sess.target.llvm_target == "i686-pc-windows-msvc" { diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs index 1f615c9d8d1a2..048dd9ccb8f3c 100644 --- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs +++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs @@ -6,11 +6,11 @@ use rustc_session::lint::builtin::CONST_ITEM_MUTATION; use rustc_span::def_id::DefId; use rustc_span::Span; -use crate::{errors, MirLint}; +use crate::errors; -pub struct CheckConstItemMutation; +pub(super) struct CheckConstItemMutation; -impl<'tcx> MirLint<'tcx> for CheckConstItemMutation { +impl<'tcx> crate::MirLint<'tcx> for CheckConstItemMutation { fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let mut checker = ConstMutationChecker { body, tcx, target_local: None }; checker.visit_body(body); @@ -123,6 +123,7 @@ impl<'tcx> Visitor<'tcx> for ConstMutationChecker<'_, 'tcx> { self.super_statement(stmt, loc); self.target_local = None; } + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, loc: Location) { if let Rvalue::Ref(_, BorrowKind::Mut { .. }, place) = rvalue { let local = place.local; diff --git a/compiler/rustc_mir_transform/src/check_packed_ref.rs b/compiler/rustc_mir_transform/src/check_packed_ref.rs index 9902002580aed..1922d4fef25de 100644 --- a/compiler/rustc_mir_transform/src/check_packed_ref.rs +++ b/compiler/rustc_mir_transform/src/check_packed_ref.rs @@ -3,11 +3,11 @@ use rustc_middle::mir::*; use rustc_middle::span_bug; use rustc_middle::ty::{self, TyCtxt}; -use crate::{errors, util, MirLint}; +use crate::{errors, util}; -pub struct CheckPackedRef; +pub(super) struct CheckPackedRef; -impl<'tcx> MirLint<'tcx> for CheckPackedRef { +impl<'tcx> crate::MirLint<'tcx> for CheckPackedRef { fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let param_env = tcx.param_env(body.source.def_id()); let source_info = SourceInfo::outermost(body.span); @@ -37,24 +37,17 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> { } fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { - if context.is_borrow() { - if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { - let def_id = self.body.source.instance.def_id(); - if let Some(impl_def_id) = self.tcx.impl_of_method(def_id) - && self.tcx.is_builtin_derived(impl_def_id) - { - // If we ever reach here it means that the generated derive - // code is somehow doing an unaligned reference, which it - // shouldn't do. - span_bug!( - self.source_info.span, - "builtin derive created an unaligned reference" - ); - } else { - self.tcx - .dcx() - .emit_err(errors::UnalignedPackedRef { span: self.source_info.span }); - } + if context.is_borrow() && util::is_disaligned(self.tcx, self.body, self.param_env, *place) { + let def_id = self.body.source.instance.def_id(); + if let Some(impl_def_id) = self.tcx.impl_of_method(def_id) + && self.tcx.is_builtin_derived(impl_def_id) + { + // If we ever reach here it means that the generated derive + // code is somehow doing an unaligned reference, which it + // shouldn't do. + span_bug!(self.source_info.span, "builtin derive created an unaligned reference"); + } else { + self.tcx.dcx().emit_err(errors::UnalignedPackedRef { span: self.source_info.span }); } } } diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index 08c9f9f08e6b2..97ce16c06616f 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -21,11 +21,9 @@ use rustc_middle::mir::{Body, BorrowKind, CastKind, Rvalue, StatementKind, Termi use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::TyCtxt; -use crate::MirPass; +pub(super) struct CleanupPostBorrowck; -pub struct CleanupPostBorrowck; - -impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck { +impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck { fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { for basic_block in body.basic_blocks.as_mut() { for statement in basic_block.statements.iter_mut() { diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs index c1f9313a377de..b3db566912108 100644 --- a/compiler/rustc_mir_transform/src/copy_prop.rs +++ b/compiler/rustc_mir_transform/src/copy_prop.rs @@ -3,6 +3,7 @@ use rustc_index::IndexSlice; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use tracing::{debug, instrument}; use crate::ssa::SsaLocals; @@ -16,9 +17,9 @@ use crate::ssa::SsaLocals; /// where each of the locals is only assigned once. /// /// We want to replace all those locals by `_a`, either copied or moved. -pub struct CopyProp; +pub(super) struct CopyProp; -impl<'tcx> MirPass<'tcx> for CopyProp { +impl<'tcx> crate::MirPass<'tcx> for CopyProp { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() >= 1 } @@ -26,37 +27,34 @@ impl<'tcx> MirPass<'tcx> for CopyProp { #[instrument(level = "trace", skip(self, tcx, body))] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!(def_id = ?body.source.def_id()); - propagate_ssa(tcx, body); - } -} -fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); - let ssa = SsaLocals::new(tcx, body, param_env); + let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); + let ssa = SsaLocals::new(tcx, body, param_env); - let fully_moved = fully_moved_locals(&ssa, body); - debug!(?fully_moved); + let fully_moved = fully_moved_locals(&ssa, body); + debug!(?fully_moved); - let mut storage_to_remove = BitSet::new_empty(fully_moved.domain_size()); - for (local, &head) in ssa.copy_classes().iter_enumerated() { - if local != head { - storage_to_remove.insert(head); + let mut storage_to_remove = BitSet::new_empty(fully_moved.domain_size()); + for (local, &head) in ssa.copy_classes().iter_enumerated() { + if local != head { + storage_to_remove.insert(head); + } } - } - let any_replacement = ssa.copy_classes().iter_enumerated().any(|(l, &h)| l != h); + let any_replacement = ssa.copy_classes().iter_enumerated().any(|(l, &h)| l != h); - Replacer { - tcx, - copy_classes: ssa.copy_classes(), - fully_moved, - borrowed_locals: ssa.borrowed_locals(), - storage_to_remove, - } - .visit_body_preserves_cfg(body); + Replacer { + tcx, + copy_classes: ssa.copy_classes(), + fully_moved, + borrowed_locals: ssa.borrowed_locals(), + storage_to_remove, + } + .visit_body_preserves_cfg(body); - if any_replacement { - crate::simplify::remove_unused_definitions(body); + if any_replacement { + crate::simplify::remove_unused_definitions(body); + } } } @@ -139,7 +137,8 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { fn visit_operand(&mut self, operand: &mut Operand<'tcx>, loc: Location) { if let Operand::Move(place) = *operand - // A move out of a projection of a copy is equivalent to a copy of the original projection. + // A move out of a projection of a copy is equivalent to a copy of the original + // projection. && !place.is_indirect_first_projection() && !self.fully_moved.contains(place.local) { diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index d9d2abc554a65..90243cd291082 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -53,7 +53,7 @@ mod by_move_body; use std::{iter, ops}; -pub use by_move_body::coroutine_by_move_body_def_id; +pub(super) use by_move_body::coroutine_by_move_body_def_id; use rustc_data_structures::fx::FxHashSet; use rustc_errors::pluralize; use rustc_hir as hir; @@ -63,7 +63,9 @@ use rustc_index::bit_set::{BitMatrix, BitSet, GrowableBitSet}; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; -use rustc_middle::ty::{self, CoroutineArgs, CoroutineArgsExt, InstanceKind, Ty, TyCtxt}; +use rustc_middle::ty::{ + self, CoroutineArgs, CoroutineArgsExt, GenericArgsRef, InstanceKind, Ty, TyCtxt, +}; use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::impls::{ MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive, @@ -78,11 +80,12 @@ use rustc_target::spec::PanicStrategy; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::TyCtxtInferExt as _; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; +use tracing::{debug, instrument, trace}; use crate::deref_separator::deref_finder; use crate::{abort_unwinding_calls, errors, pass_manager as pm, simplify}; -pub struct StateTransform; +pub(super) struct StateTransform; struct RenameLocalVisitor<'tcx> { from: Local, @@ -112,47 +115,18 @@ impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor<'tcx> { } } -struct DerefArgVisitor<'tcx> { +struct SelfArgVisitor<'tcx> { tcx: TyCtxt<'tcx>, + new_base: Place<'tcx>, } -impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx +impl<'tcx> SelfArgVisitor<'tcx> { + fn new(tcx: TyCtxt<'tcx>, elem: ProjectionElem>) -> Self { + Self { tcx, new_base: Place { local: SELF_ARG, projection: tcx.mk_place_elems(&[elem]) } } } - - fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) { - assert_ne!(*local, SELF_ARG); - } - - fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { - if place.local == SELF_ARG { - replace_base( - place, - Place { - local: SELF_ARG, - projection: self.tcx().mk_place_elems(&[ProjectionElem::Deref]), - }, - self.tcx, - ); - } else { - self.visit_local(&mut place.local, context, location); - - for elem in place.projection.iter() { - if let PlaceElem::Index(local) = elem { - assert_ne!(local, SELF_ARG); - } - } - } - } -} - -struct PinArgVisitor<'tcx> { - ref_coroutine_ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, } -impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> { +impl<'tcx> MutVisitor<'tcx> for SelfArgVisitor<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -163,17 +137,7 @@ impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> { fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { if place.local == SELF_ARG { - replace_base( - place, - Place { - local: SELF_ARG, - projection: self.tcx().mk_place_elems(&[ProjectionElem::Field( - FieldIdx::ZERO, - self.ref_coroutine_ty, - )]), - }, - self.tcx, - ); + replace_base(place, self.new_base, self.tcx); } else { self.visit_local(&mut place.local, context, location); @@ -197,15 +161,6 @@ fn replace_base<'tcx>(place: &mut Place<'tcx>, new_base: Place<'tcx>, tcx: TyCtx const SELF_ARG: Local = Local::from_u32(1); -/// Coroutine has not been resumed yet. -const UNRESUMED: usize = CoroutineArgs::UNRESUMED; -/// Coroutine has returned / is completed. -const RETURNED: usize = CoroutineArgs::RETURNED; -/// Coroutine has panicked and is poisoned. -const POISONED: usize = CoroutineArgs::POISONED; -/// Number of reserved variants of coroutine state. -const RESERVED_VARIANTS: usize = CoroutineArgs::RESERVED_VARIANTS; - /// A `yield` point in the coroutine. struct SuspensionPoint<'tcx> { /// State discriminant used when suspending or resuming at this point. @@ -260,14 +215,10 @@ impl<'tcx> TransformVisitor<'tcx> { // `gen` continues return `None` CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => { let option_def_id = self.tcx.require_lang_item(LangItem::Option, None); - Rvalue::Aggregate( - Box::new(AggregateKind::Adt( - option_def_id, - VariantIdx::ZERO, - self.tcx.mk_args(&[self.old_yield_ty.into()]), - None, - None, - )), + make_aggregate_adt( + option_def_id, + VariantIdx::ZERO, + self.tcx.mk_args(&[self.old_yield_ty.into()]), IndexVec::new(), ) } @@ -316,64 +267,28 @@ impl<'tcx> TransformVisitor<'tcx> { is_return: bool, statements: &mut Vec>, ) { + const ZERO: VariantIdx = VariantIdx::ZERO; + const ONE: VariantIdx = VariantIdx::from_usize(1); let rvalue = match self.coroutine_kind { CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => { let poll_def_id = self.tcx.require_lang_item(LangItem::Poll, None); let args = self.tcx.mk_args(&[self.old_ret_ty.into()]); - if is_return { - // Poll::Ready(val) - Rvalue::Aggregate( - Box::new(AggregateKind::Adt( - poll_def_id, - VariantIdx::ZERO, - args, - None, - None, - )), - IndexVec::from_raw(vec![val]), - ) + let (variant_idx, operands) = if is_return { + (ZERO, IndexVec::from_raw(vec![val])) // Poll::Ready(val) } else { - // Poll::Pending - Rvalue::Aggregate( - Box::new(AggregateKind::Adt( - poll_def_id, - VariantIdx::from_usize(1), - args, - None, - None, - )), - IndexVec::new(), - ) - } + (ONE, IndexVec::new()) // Poll::Pending + }; + make_aggregate_adt(poll_def_id, variant_idx, args, operands) } CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => { let option_def_id = self.tcx.require_lang_item(LangItem::Option, None); let args = self.tcx.mk_args(&[self.old_yield_ty.into()]); - if is_return { - // None - Rvalue::Aggregate( - Box::new(AggregateKind::Adt( - option_def_id, - VariantIdx::ZERO, - args, - None, - None, - )), - IndexVec::new(), - ) + let (variant_idx, operands) = if is_return { + (ZERO, IndexVec::new()) // None } else { - // Some(val) - Rvalue::Aggregate( - Box::new(AggregateKind::Adt( - option_def_id, - VariantIdx::from_usize(1), - args, - None, - None, - )), - IndexVec::from_raw(vec![val]), - ) - } + (ONE, IndexVec::from_raw(vec![val])) // Some(val) + }; + make_aggregate_adt(option_def_id, variant_idx, args, operands) } CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _) => { if is_return { @@ -399,31 +314,17 @@ impl<'tcx> TransformVisitor<'tcx> { let coroutine_state_def_id = self.tcx.require_lang_item(LangItem::CoroutineState, None); let args = self.tcx.mk_args(&[self.old_yield_ty.into(), self.old_ret_ty.into()]); - if is_return { - // CoroutineState::Complete(val) - Rvalue::Aggregate( - Box::new(AggregateKind::Adt( - coroutine_state_def_id, - VariantIdx::from_usize(1), - args, - None, - None, - )), - IndexVec::from_raw(vec![val]), - ) + let variant_idx = if is_return { + ONE // CoroutineState::Complete(val) } else { - // CoroutineState::Yielded(val) - Rvalue::Aggregate( - Box::new(AggregateKind::Adt( - coroutine_state_def_id, - VariantIdx::ZERO, - args, - None, - None, - )), - IndexVec::from_raw(vec![val]), - ) - } + ZERO // CoroutineState::Yielded(val) + }; + make_aggregate_adt( + coroutine_state_def_id, + variant_idx, + args, + IndexVec::from_raw(vec![val]), + ) } }; @@ -516,7 +417,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { self.make_state(v, source_info, is_return, &mut data.statements); let state = if let Some((resume, mut resume_arg)) = resume { // Yield - let state = RESERVED_VARIANTS + self.suspension_points.len(); + let state = CoroutineArgs::RESERVED_VARIANTS + self.suspension_points.len(); // The resume arg target location might itself be remapped if its base local is // live across a yield. @@ -549,7 +450,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { VariantIdx::new(state) } else { // Return - VariantIdx::new(RETURNED) // state for returned + VariantIdx::new(CoroutineArgs::RETURNED) // state for returned }; data.statements.push(self.set_discr(state, source_info)); data.terminator_mut().kind = TerminatorKind::Return; @@ -559,6 +460,15 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { } } +fn make_aggregate_adt<'tcx>( + def_id: DefId, + variant_idx: VariantIdx, + args: GenericArgsRef<'tcx>, + operands: IndexVec>, +) -> Rvalue<'tcx> { + Rvalue::Aggregate(Box::new(AggregateKind::Adt(def_id, variant_idx, args, None, None)), operands) +} + fn make_coroutine_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let coroutine_ty = body.local_decls.raw[1].ty; @@ -568,7 +478,7 @@ fn make_coroutine_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Bo body.local_decls.raw[1].ty = ref_coroutine_ty; // Add a deref to accesses of the coroutine state - DerefArgVisitor { tcx }.visit_body(body); + SelfArgVisitor::new(tcx, ProjectionElem::Deref).visit_body(body); } fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { @@ -583,7 +493,8 @@ fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body body.local_decls.raw[1].ty = pin_ref_coroutine_ty; // Add the Pin field access to accesses of the coroutine state - PinArgVisitor { ref_coroutine_ty, tcx }.visit_body(body); + SelfArgVisitor::new(tcx, ProjectionElem::Field(FieldIdx::ZERO, ref_coroutine_ty)) + .visit_body(body); } /// Allocates a new local and replaces all references of `local` with it. Returns the new local. @@ -650,8 +561,6 @@ fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let local = eliminate_get_context_call(&mut body[bb]); replace_resume_ty_local(tcx, body, local, context_mut_ref); } - } else { - continue; } } TerminatorKind::Yield { resume_arg, .. } => { @@ -664,24 +573,23 @@ fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { fn eliminate_get_context_call<'tcx>(bb_data: &mut BasicBlockData<'tcx>) -> Local { let terminator = bb_data.terminator.take().unwrap(); - if let TerminatorKind::Call { args, destination, target, .. } = terminator.kind { - let [arg] = *Box::try_from(args).unwrap(); - let local = arg.node.place().unwrap().local; - - let arg = Rvalue::Use(arg.node); - let assign = Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new((destination, arg))), - }; - bb_data.statements.push(assign); - bb_data.terminator = Some(Terminator { - source_info: terminator.source_info, - kind: TerminatorKind::Goto { target: target.unwrap() }, - }); - local - } else { + let TerminatorKind::Call { args, destination, target, .. } = terminator.kind else { bug!(); - } + }; + let [arg] = *Box::try_from(args).unwrap(); + let local = arg.node.place().unwrap().local; + + let arg = Rvalue::Use(arg.node); + let assign = Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new((destination, arg))), + }; + bb_data.statements.push(assign); + bb_data.terminator = Some(Terminator { + source_info: terminator.source_info, + kind: TerminatorKind::Goto { target: target.unwrap() }, + }); + local } #[cfg_attr(not(debug_assertions), allow(unused))] @@ -964,9 +872,9 @@ fn compute_storage_conflicts<'mir, 'tcx>( storage_conflicts } -struct StorageConflictVisitor<'mir, 'tcx, 's> { - body: &'mir Body<'tcx>, - saved_locals: &'s CoroutineSavedLocals, +struct StorageConflictVisitor<'a, 'tcx> { + body: &'a Body<'tcx>, + saved_locals: &'a CoroutineSavedLocals, // FIXME(tmandry): Consider using sparse bitsets here once we have good // benchmarks for coroutines. local_conflicts: BitMatrix, @@ -974,8 +882,8 @@ struct StorageConflictVisitor<'mir, 'tcx, 's> { eligible_storage_live: BitSet, } -impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> - for StorageConflictVisitor<'mir, 'tcx, '_> +impl<'a, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'a, 'tcx, R> + for StorageConflictVisitor<'a, 'tcx> { type FlowState = BitSet; @@ -983,7 +891,7 @@ impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> &mut self, _results: &mut R, state: &Self::FlowState, - _statement: &'mir Statement<'tcx>, + _statement: &'a Statement<'tcx>, loc: Location, ) { self.apply_state(state, loc); @@ -993,14 +901,14 @@ impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> &mut self, _results: &mut R, state: &Self::FlowState, - _terminator: &'mir Terminator<'tcx>, + _terminator: &'a Terminator<'tcx>, loc: Location, ) { self.apply_state(state, loc); } } -impl StorageConflictVisitor<'_, '_, '_> { +impl StorageConflictVisitor<'_, '_> { fn apply_state(&mut self, flow_state: &BitSet, loc: Location) { // Ignore unreachable blocks. if let TerminatorKind::Unreachable = self.body.basic_blocks[loc.block].terminator().kind { @@ -1084,10 +992,11 @@ fn compute_layout<'tcx>( // Build the coroutine variant field list. // Create a map from local indices to coroutine struct indices. let mut variant_fields: IndexVec> = - iter::repeat(IndexVec::new()).take(RESERVED_VARIANTS).collect(); + iter::repeat(IndexVec::new()).take(CoroutineArgs::RESERVED_VARIANTS).collect(); let mut remap = IndexVec::from_elem_n(None, saved_locals.domain_size()); for (suspension_point_idx, live_locals) in live_locals_at_suspension_points.iter().enumerate() { - let variant_index = VariantIdx::from(RESERVED_VARIANTS + suspension_point_idx); + let variant_index = + VariantIdx::from(CoroutineArgs::RESERVED_VARIANTS + suspension_point_idx); let mut fields = IndexVec::new(); for (idx, saved_local) in live_locals.iter().enumerate() { fields.push(saved_local); @@ -1182,12 +1091,10 @@ fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { source_info, kind: TerminatorKind::Drop { place, target, unwind, replace: _ }, } => { - if let Some(local) = place.as_local() { - if local == SELF_ARG { - (target, unwind, source_info) - } else { - continue; - } + if let Some(local) = place.as_local() + && local == SELF_ARG + { + (target, unwind, source_info) } else { continue; } @@ -1236,7 +1143,7 @@ fn create_coroutine_drop_shim<'tcx>( let mut cases = create_cases(&mut body, transform, Operation::Drop); - cases.insert(0, (UNRESUMED, drop_clean)); + cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean)); // The returned state and the poisoned state fall through to the default // case which is just to return @@ -1292,7 +1199,7 @@ fn insert_panic_block<'tcx>( message: AssertMessage<'tcx>, ) -> BasicBlock { let assert_block = BasicBlock::new(body.basic_blocks.len()); - let term = TerminatorKind::Assert { + let kind = TerminatorKind::Assert { cond: Operand::Constant(Box::new(ConstOperand { span: body.span, user_ty: None, @@ -1304,14 +1211,7 @@ fn insert_panic_block<'tcx>( unwind: UnwindAction::Continue, }; - let source_info = SourceInfo::outermost(body.span); - body.basic_blocks_mut().push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { source_info, kind: term }), - is_cleanup: false, - }); - - assert_block + insert_term_block(body, kind) } fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { @@ -1386,7 +1286,9 @@ fn create_coroutine_resume_function<'tcx>( if can_unwind { let source_info = SourceInfo::outermost(body.span); let poison_block = body.basic_blocks_mut().push(BasicBlockData { - statements: vec![transform.set_discr(VariantIdx::new(POISONED), source_info)], + statements: vec![ + transform.set_discr(VariantIdx::new(CoroutineArgs::POISONED), source_info), + ], terminator: Some(Terminator { source_info, kind: TerminatorKind::UnwindResume }), is_cleanup: true, }); @@ -1418,13 +1320,16 @@ fn create_coroutine_resume_function<'tcx>( use rustc_middle::mir::AssertKind::{ResumedAfterPanic, ResumedAfterReturn}; // Jump to the entry point on the unresumed - cases.insert(0, (UNRESUMED, START_BLOCK)); + cases.insert(0, (CoroutineArgs::UNRESUMED, START_BLOCK)); // Panic when resumed on the returned or poisoned state if can_unwind { cases.insert( 1, - (POISONED, insert_panic_block(tcx, body, ResumedAfterPanic(transform.coroutine_kind))), + ( + CoroutineArgs::POISONED, + insert_panic_block(tcx, body, ResumedAfterPanic(transform.coroutine_kind)), + ), ); } @@ -1439,7 +1344,7 @@ fn create_coroutine_resume_function<'tcx>( transform.insert_none_ret_block(body) } }; - cases.insert(1, (RETURNED, block)); + cases.insert(1, (CoroutineArgs::RETURNED, block)); } insert_switch(body, cases, &transform, TerminatorKind::Unreachable); @@ -1623,7 +1528,7 @@ fn check_field_tys_sized<'tcx>( } } -impl<'tcx> MirPass<'tcx> for StateTransform { +impl<'tcx> crate::MirPass<'tcx> for StateTransform { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let Some(old_yield_ty) = body.yield_ty() else { // This only applies to coroutines diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index ebe8d2eff4ff3..6476f3c623841 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -82,12 +82,17 @@ use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::symbol::kw; use rustc_target::abi::{FieldIdx, VariantIdx}; -pub fn coroutine_by_move_body_def_id<'tcx>( +pub(crate) fn coroutine_by_move_body_def_id<'tcx>( tcx: TyCtxt<'tcx>, coroutine_def_id: LocalDefId, ) -> DefId { let body = tcx.mir_built(coroutine_def_id).borrow(); + // If the typeck results are tainted, no need to make a by-ref body. + if body.tainted_by_errors.is_some() { + return coroutine_def_id.to_def_id(); + } + let Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure)) = tcx.coroutine_kind(coroutine_def_id) else { @@ -98,7 +103,9 @@ pub fn coroutine_by_move_body_def_id<'tcx>( // the MIR body will be constructed well. let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; - let ty::Coroutine(_, args) = *coroutine_ty.kind() else { bug!("{body:#?}") }; + let ty::Coroutine(_, args) = *coroutine_ty.kind() else { + bug!("tried to create by-move body of non-coroutine receiver"); + }; let args = args.as_coroutine(); let coroutine_kind = args.kind_ty().to_opt_closure_kind().unwrap(); @@ -107,7 +114,7 @@ pub fn coroutine_by_move_body_def_id<'tcx>( let ty::CoroutineClosure(_, parent_args) = *tcx.type_of(parent_def_id).instantiate_identity().kind() else { - bug!(); + bug!("coroutine's parent was not a coroutine-closure"); }; if parent_args.references_error() { return coroutine_def_id.to_def_id(); @@ -207,11 +214,12 @@ pub fn coroutine_by_move_body_def_id<'tcx>( let mut by_move_body = body.clone(); MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body); - dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(())); - let body_def = tcx.create_def(coroutine_def_id, kw::Empty, DefKind::SyntheticCoroutineBody); + // This will always be `{closure#1}`, since the original coroutine is `{closure#0}`. + let body_def = tcx.create_def(parent_def_id, kw::Empty, DefKind::SyntheticCoroutineBody); by_move_body.source = mir::MirSource::from_instance(InstanceKind::Item(body_def.def_id().to_def_id())); + dump_mir(tcx, false, "built", &"after", &by_move_body, |_, _| Ok(())); // Inherited from the by-ref coroutine. body_def.codegen_fn_attrs(tcx.codegen_fn_attrs(coroutine_def_id).clone()); diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index a1c1422912ee3..59b403538a33d 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -12,7 +12,7 @@ const CONST_SWITCH_BONUS: usize = 10; /// Verify that the callee body is compatible with the caller. #[derive(Clone)] -pub(crate) struct CostChecker<'b, 'tcx> { +pub(super) struct CostChecker<'b, 'tcx> { tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, penalty: usize, @@ -22,7 +22,7 @@ pub(crate) struct CostChecker<'b, 'tcx> { } impl<'b, 'tcx> CostChecker<'b, 'tcx> { - pub fn new( + pub(super) fn new( tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, instance: Option>, @@ -36,7 +36,7 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> { /// Needed because the `CostChecker` is used sometimes for just blocks, /// and even the full `Inline` doesn't call `visit_body`, so there's nowhere /// to put this logic in the visitor. - pub fn add_function_level_costs(&mut self) { + pub(super) fn add_function_level_costs(&mut self) { fn is_call_like(bbd: &BasicBlockData<'_>) -> bool { use TerminatorKind::*; match bbd.terminator().kind { @@ -64,7 +64,7 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> { } } - pub fn cost(&self) -> usize { + pub(super) fn cost(&self) -> usize { usize::saturating_sub(self.penalty, self.bonus) } diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index a8b0f4a8d6df4..1c240366afa2e 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -6,6 +6,7 @@ use rustc_data_structures::graph::DirectedGraph; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op}; +use tracing::{debug, debug_span, instrument}; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverageGraphWithLoops}; @@ -73,12 +74,11 @@ pub(super) struct CoverageCounters { } impl CoverageCounters { - /// Makes [`BcbCounter`] `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or - /// indirectly associated with coverage spans, and accumulates additional `Expression`s - /// representing intermediate values. + /// Ensures that each BCB node needing a counter has one, by creating physical + /// counters or counter expressions for nodes and edges as required. pub(super) fn make_bcb_counters( basic_coverage_blocks: &CoverageGraph, - bcb_has_coverage_spans: impl Fn(BasicCoverageBlock) -> bool, + bcb_needs_counter: impl Fn(BasicCoverageBlock) -> bool, ) -> Self { let num_bcbs = basic_coverage_blocks.num_nodes(); @@ -90,8 +90,7 @@ impl CoverageCounters { expressions_memo: FxHashMap::default(), }; - MakeBcbCounters::new(&mut this, basic_coverage_blocks) - .make_bcb_counters(bcb_has_coverage_spans); + MakeBcbCounters::new(&mut this, basic_coverage_blocks).make_bcb_counters(bcb_needs_counter); this } @@ -240,10 +239,7 @@ impl CoverageCounters { } } -/// Traverse the `CoverageGraph` and add either a `Counter` or `Expression` to every BCB, to be -/// injected with coverage spans. `Expressions` have no runtime overhead, so if a viable expression -/// (adding or subtracting two other counters or expressions) can compute the same result as an -/// embedded counter, an `Expression` should be used. +/// Helper struct that allows counter creation to inspect the BCB graph. struct MakeBcbCounters<'a> { coverage_counters: &'a mut CoverageCounters, basic_coverage_blocks: &'a CoverageGraph, @@ -257,36 +253,21 @@ impl<'a> MakeBcbCounters<'a> { Self { coverage_counters, basic_coverage_blocks } } - /// If two `BasicCoverageBlock`s branch from another `BasicCoverageBlock`, one of the branches - /// can be counted by `Expression` by subtracting the other branch from the branching - /// block. Otherwise, the `BasicCoverageBlock` executed the least should have the `Counter`. - /// One way to predict which branch executes the least is by considering loops. A loop is exited - /// at a branch, so the branch that jumps to a `BasicCoverageBlock` outside the loop is almost - /// always executed less than the branch that does not exit the loop. - fn make_bcb_counters(&mut self, bcb_has_coverage_spans: impl Fn(BasicCoverageBlock) -> bool) { + fn make_bcb_counters(&mut self, bcb_needs_counter: impl Fn(BasicCoverageBlock) -> bool) { debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); - // Walk the `CoverageGraph`. For each `BasicCoverageBlock` node with an associated - // coverage span, add a counter. If the `BasicCoverageBlock` branches, add a counter or - // expression to each branch `BasicCoverageBlock` (if the branch BCB has only one incoming - // edge) or edge from the branching BCB to the branch BCB (if the branch BCB has multiple - // incoming edges). + // Traverse the coverage graph, ensuring that every node that needs a + // coverage counter has one. // - // The `TraverseCoverageGraphWithLoops` traversal ensures that, when a loop is encountered, - // all `BasicCoverageBlock` nodes in the loop are visited before visiting any node outside - // the loop. The `traversal` state includes a `context_stack`, providing a way to know if - // the current BCB is in one or more nested loops or not. + // The traversal tries to ensure that, when a loop is encountered, all + // nodes within the loop are visited before visiting any nodes outside + // the loop. It also keeps track of which loop(s) the traversal is + // currently inside. let mut traversal = TraverseCoverageGraphWithLoops::new(self.basic_coverage_blocks); while let Some(bcb) = traversal.next() { - if bcb_has_coverage_spans(bcb) { - debug!("{:?} has at least one coverage span. Get or make its counter", bcb); - self.make_node_and_branch_counters(&traversal, bcb); - } else { - debug!( - "{:?} does not have any coverage spans. A counter will only be added if \ - and when a covered BCB has an expression dependency.", - bcb, - ); + let _span = debug_span!("traversal", ?bcb).entered(); + if bcb_needs_counter(bcb) { + self.make_node_counter_and_out_edge_counters(&traversal, bcb); } } @@ -297,73 +278,66 @@ impl<'a> MakeBcbCounters<'a> { ); } - fn make_node_and_branch_counters( + /// Make sure the given node has a node counter, and then make sure each of + /// its out-edges has an edge counter (if appropriate). + #[instrument(level = "debug", skip(self, traversal))] + fn make_node_counter_and_out_edge_counters( &mut self, traversal: &TraverseCoverageGraphWithLoops<'_>, from_bcb: BasicCoverageBlock, ) { // First, ensure that this node has a counter of some kind. - // We might also use its term later to compute one of the branch counters. - let from_bcb_operand = self.get_or_make_counter_operand(from_bcb); + // We might also use that counter to compute one of the out-edge counters. + let node_counter = self.get_or_make_node_counter(from_bcb); - let branch_target_bcbs = self.basic_coverage_blocks.successors[from_bcb].as_slice(); + let successors = self.basic_coverage_blocks.successors[from_bcb].as_slice(); // If this node doesn't have multiple out-edges, or all of its out-edges // already have counters, then we don't need to create edge counters. - let needs_branch_counters = branch_target_bcbs.len() > 1 - && branch_target_bcbs - .iter() - .any(|&to_bcb| self.branch_has_no_counter(from_bcb, to_bcb)); - if !needs_branch_counters { + let needs_out_edge_counters = successors.len() > 1 + && successors.iter().any(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb)); + if !needs_out_edge_counters { return; } - debug!( - "{from_bcb:?} has some branch(es) without counters:\n {}", - branch_target_bcbs - .iter() - .map(|&to_bcb| { - format!("{from_bcb:?}->{to_bcb:?}: {:?}", self.branch_counter(from_bcb, to_bcb)) - }) - .collect::>() - .join("\n "), - ); + if tracing::enabled!(tracing::Level::DEBUG) { + let _span = + debug_span!("node has some out-edges without counters", ?from_bcb).entered(); + for &to_bcb in successors { + debug!(?to_bcb, counter=?self.edge_counter(from_bcb, to_bcb)); + } + } - // Of the branch edges that don't have counters yet, one can be given an expression - // (computed from the other edges) instead of a dedicated counter. - let expression_to_bcb = self.choose_preferred_expression_branch(traversal, from_bcb); + // Of the out-edges that don't have counters yet, one can be given an expression + // (computed from the other out-edges) instead of a dedicated counter. + let expression_to_bcb = self.choose_out_edge_for_expression(traversal, from_bcb); - // For each branch arm other than the one that was chosen to get an expression, + // For each out-edge other than the one that was chosen to get an expression, // ensure that it has a counter (existing counter/expression or a new counter), - // and accumulate the corresponding terms into a single sum term. - let sum_of_all_other_branches: BcbCounter = { - let _span = debug_span!("sum_of_all_other_branches", ?expression_to_bcb).entered(); - branch_target_bcbs + // and accumulate the corresponding counters into a single sum expression. + let sum_of_all_other_out_edges: BcbCounter = { + let _span = debug_span!("sum_of_all_other_out_edges", ?expression_to_bcb).entered(); + successors .iter() .copied() - // Skip the chosen branch, since we'll calculate it from the other branches. + // Skip the chosen edge, since we'll calculate its count from this sum. .filter(|&to_bcb| to_bcb != expression_to_bcb) .fold(None, |accum, to_bcb| { let _span = debug_span!("to_bcb", ?accum, ?to_bcb).entered(); - let branch_counter = self.get_or_make_edge_counter_operand(from_bcb, to_bcb); - Some(self.coverage_counters.make_sum_expression(accum, branch_counter)) + let edge_counter = self.get_or_make_edge_counter(from_bcb, to_bcb); + Some(self.coverage_counters.make_sum_expression(accum, edge_counter)) }) - .expect("there must be at least one other branch") + .expect("there must be at least one other out-edge") }; - // For the branch that was chosen to get an expression, create that expression - // by taking the count of the node we're branching from, and subtracting the - // sum of all the other branches. - debug!( - "Making an expression for the selected expression_branch: \ - {expression_to_bcb:?} (expression_branch predecessors: {:?})", - self.bcb_predecessors(expression_to_bcb), - ); + // Now create an expression for the chosen edge, by taking the counter + // for its source node and subtracting the sum of its sibling out-edges. let expression = self.coverage_counters.make_expression( - from_bcb_operand, + node_counter, Op::Subtract, - sum_of_all_other_branches, + sum_of_all_other_out_edges, ); + debug!("{expression_to_bcb:?} gets an expression: {expression:?}"); if self.basic_coverage_blocks.bcb_has_multiple_in_edges(expression_to_bcb) { self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression); @@ -373,7 +347,7 @@ impl<'a> MakeBcbCounters<'a> { } #[instrument(level = "debug", skip(self))] - fn get_or_make_counter_operand(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { + fn get_or_make_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { // If the BCB already has a counter, return it. if let Some(counter_kind) = self.coverage_counters.bcb_counters[bcb] { debug!("{bcb:?} already has a counter: {counter_kind:?}"); @@ -410,7 +384,7 @@ impl<'a> MakeBcbCounters<'a> { .copied() .fold(None, |accum, from_bcb| { let _span = debug_span!("from_bcb", ?accum, ?from_bcb).entered(); - let edge_counter = self.get_or_make_edge_counter_operand(from_bcb, bcb); + let edge_counter = self.get_or_make_edge_counter(from_bcb, bcb); Some(self.coverage_counters.make_sum_expression(accum, edge_counter)) }) .expect("there must be at least one in-edge") @@ -421,7 +395,7 @@ impl<'a> MakeBcbCounters<'a> { } #[instrument(level = "debug", skip(self))] - fn get_or_make_edge_counter_operand( + fn get_or_make_edge_counter( &mut self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, @@ -430,13 +404,13 @@ impl<'a> MakeBcbCounters<'a> { // a node counter instead, since it will have the same value. if !self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) { assert_eq!([from_bcb].as_slice(), self.basic_coverage_blocks.predecessors[to_bcb]); - return self.get_or_make_counter_operand(to_bcb); + return self.get_or_make_node_counter(to_bcb); } // If the source BCB has only one successor (assumed to be the given target), an edge // counter is unnecessary. Just get or make a counter for the source BCB. if self.bcb_successors(from_bcb).len() == 1 { - return self.get_or_make_counter_operand(from_bcb); + return self.get_or_make_node_counter(from_bcb); } // If the edge already has a counter, return it. @@ -454,82 +428,79 @@ impl<'a> MakeBcbCounters<'a> { self.coverage_counters.set_bcb_edge_counter(from_bcb, to_bcb, counter_kind) } - /// Select a branch for the expression, either the recommended `reloop_branch`, or if none was - /// found, select any branch. - fn choose_preferred_expression_branch( + /// Choose one of the out-edges of `from_bcb` to receive an expression + /// instead of a physical counter, and returns that edge's target node. + /// + /// - Precondition: The node must have at least one out-edge without a counter. + /// - Postcondition: The selected edge does not have an edge counter. + fn choose_out_edge_for_expression( &self, traversal: &TraverseCoverageGraphWithLoops<'_>, from_bcb: BasicCoverageBlock, ) -> BasicCoverageBlock { - let good_reloop_branch = self.find_good_reloop_branch(traversal, from_bcb); - if let Some(reloop_target) = good_reloop_branch { - assert!(self.branch_has_no_counter(from_bcb, reloop_target)); + if let Some(reloop_target) = self.find_good_reloop_edge(traversal, from_bcb) { + assert!(self.edge_has_no_counter(from_bcb, reloop_target)); debug!("Selecting reloop target {reloop_target:?} to get an expression"); - reloop_target - } else { - let &branch_without_counter = self - .bcb_successors(from_bcb) - .iter() - .find(|&&to_bcb| self.branch_has_no_counter(from_bcb, to_bcb)) - .expect( - "needs_branch_counters was `true` so there should be at least one \ - branch", - ); - debug!( - "Selecting any branch={:?} that still needs a counter, to get the \ - `Expression` because there was no `reloop_branch`, or it already had a \ - counter", - branch_without_counter - ); - branch_without_counter + return reloop_target; } + + // We couldn't identify a "good" edge, so just choose any edge that + // doesn't already have a counter. + let arbitrary_target = self + .bcb_successors(from_bcb) + .iter() + .copied() + .find(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb)) + .expect("precondition: at least one out-edge without a counter"); + debug!(?arbitrary_target, "selecting arbitrary out-edge to get an expression"); + arbitrary_target } - /// Tries to find a branch that leads back to the top of a loop, and that - /// doesn't already have a counter. Such branches are good candidates to + /// Tries to find an edge that leads back to the top of a loop, and that + /// doesn't already have a counter. Such edges are good candidates to /// be given an expression (instead of a physical counter), because they - /// will tend to be executed more times than a loop-exit branch. - fn find_good_reloop_branch( + /// will tend to be executed more times than a loop-exit edge. + fn find_good_reloop_edge( &self, traversal: &TraverseCoverageGraphWithLoops<'_>, from_bcb: BasicCoverageBlock, ) -> Option { - let branch_target_bcbs = self.bcb_successors(from_bcb); + let successors = self.bcb_successors(from_bcb); // Consider each loop on the current traversal context stack, top-down. for reloop_bcbs in traversal.reloop_bcbs_per_loop() { - let mut all_branches_exit_this_loop = true; + let mut all_edges_exit_this_loop = true; - // Try to find a branch that doesn't exit this loop and doesn't + // Try to find an out-edge that doesn't exit this loop and doesn't // already have a counter. - for &branch_target_bcb in branch_target_bcbs { - // A branch is a reloop branch if it dominates any BCB that has - // an edge back to the loop header. (Other branches are exits.) - let is_reloop_branch = reloop_bcbs.iter().any(|&reloop_bcb| { - self.basic_coverage_blocks.dominates(branch_target_bcb, reloop_bcb) + for &target_bcb in successors { + // An edge is a reloop edge if its target dominates any BCB that has + // an edge back to the loop header. (Otherwise it's an exit edge.) + let is_reloop_edge = reloop_bcbs.iter().any(|&reloop_bcb| { + self.basic_coverage_blocks.dominates(target_bcb, reloop_bcb) }); - if is_reloop_branch { - all_branches_exit_this_loop = false; - if self.branch_has_no_counter(from_bcb, branch_target_bcb) { - // We found a good branch to be given an expression. - return Some(branch_target_bcb); + if is_reloop_edge { + all_edges_exit_this_loop = false; + if self.edge_has_no_counter(from_bcb, target_bcb) { + // We found a good out-edge to be given an expression. + return Some(target_bcb); } - // Keep looking for another reloop branch without a counter. + // Keep looking for another reloop edge without a counter. } else { - // This branch exits the loop. + // This edge exits the loop. } } - if !all_branches_exit_this_loop { - // We found one or more reloop branches, but all of them already - // have counters. Let the caller choose one of the exit branches. - debug!("All reloop branches had counters; skip checking the other loops"); + if !all_edges_exit_this_loop { + // We found one or more reloop edges, but all of them already + // have counters. Let the caller choose one of the other edges. + debug!("All reloop edges had counters; skipping the other loops"); return None; } - // All of the branches exit this loop, so keep looking for a good - // reloop branch for one of the outer loops. + // All of the out-edges exit this loop, so keep looking for a good + // reloop edge for one of the outer loops. } None @@ -546,15 +517,15 @@ impl<'a> MakeBcbCounters<'a> { } #[inline] - fn branch_has_no_counter( + fn edge_has_no_counter( &self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, ) -> bool { - self.branch_counter(from_bcb, to_bcb).is_none() + self.edge_counter(from_bcb, to_bcb).is_none() } - fn branch_counter( + fn edge_counter( &self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 31b207751942b..0d874a6c8bab8 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -10,6 +10,7 @@ use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::mir::{self, BasicBlock, Terminator, TerminatorKind}; +use tracing::debug; /// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s /// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s. diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index af0f1e38a754e..f78fc7931f9f9 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -1,4 +1,4 @@ -pub mod query; +pub(super) mod query; mod counters; mod graph; @@ -23,18 +23,18 @@ use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Pos, RelativeBytePos, Span, Symbol}; +use tracing::{debug, debug_span, instrument, trace}; use crate::coverage::counters::{CounterIncrementSite, CoverageCounters}; use crate::coverage::graph::CoverageGraph; use crate::coverage::mappings::ExtractedMappings; -use crate::MirPass; /// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected /// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen /// to construct the coverage map. -pub struct InstrumentCoverage; +pub(super) struct InstrumentCoverage; -impl<'tcx> MirPass<'tcx> for InstrumentCoverage { +impl<'tcx> crate::MirPass<'tcx> for InstrumentCoverage { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.instrument_coverage() } @@ -279,7 +279,8 @@ fn inject_mcdc_statements<'tcx>( basic_coverage_blocks: &CoverageGraph, extracted_mappings: &ExtractedMappings, ) { - // Inject test vector update first because `inject_statement` always insert new statement at head. + // Inject test vector update first because `inject_statement` always insert new statement at + // head. for &mappings::MCDCDecision { span: _, ref end_bcbs, diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 1fce2abbbbf73..e65a5fdd5e7f1 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -7,6 +7,7 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; use rustc_span::def_id::LocalDefId; use rustc_span::sym; +use tracing::trace; /// Registers query/hook implementations related to coverage. pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 092ec1e06d240..b904b0d2599b1 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -3,7 +3,8 @@ use std::collections::VecDeque; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir; -use rustc_span::Span; +use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span}; +use tracing::{debug, debug_span, instrument}; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; use crate::coverage::spans::from_mir::{ @@ -24,7 +25,7 @@ pub(super) fn extract_refined_covspans( // First, perform the passes that need macro information. covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb)); - remove_unwanted_macro_spans(&mut covspans); + remove_unwanted_expansion_spans(&mut covspans); split_visible_macro_spans(&mut covspans); // We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`. @@ -75,18 +76,24 @@ pub(super) fn extract_refined_covspans( /// invocation, which is unhelpful. Keeping only the first such span seems to /// give better mappings, so remove the others. /// +/// Similarly, `await` expands to a branch on the discriminant of `Poll`, which +/// leads to incorrect coverage if the `Future` is immediately ready (#98712). +/// /// (The input spans should be sorted in BCB dominator order, so that the /// retained "first" span is likely to dominate the others.) -fn remove_unwanted_macro_spans(covspans: &mut Vec) { - let mut seen_macro_spans = FxHashSet::default(); +fn remove_unwanted_expansion_spans(covspans: &mut Vec) { + let mut deduplicated_spans = FxHashSet::default(); + covspans.retain(|covspan| { - // Ignore (retain) non-macro-expansion spans. - if covspan.visible_macro.is_none() { - return true; + match covspan.expn_kind { + // Retain only the first await-related or macro-expanded covspan with this span. + Some(ExpnKind::Desugaring(kind)) if kind == DesugaringKind::Await => { + deduplicated_spans.insert(covspan.span) + } + Some(ExpnKind::Macro(MacroKind::Bang, _)) => deduplicated_spans.insert(covspan.span), + // Ignore (retain) other spans. + _ => true, } - - // Retain only the first macro-expanded covspan with this span. - seen_macro_spans.insert(covspan.span) }); } @@ -98,7 +105,9 @@ fn split_visible_macro_spans(covspans: &mut Vec) { let mut extra_spans = vec![]; covspans.retain(|covspan| { - let Some(visible_macro) = covspan.visible_macro else { return true }; + let Some(ExpnKind::Macro(MacroKind::Bang, visible_macro)) = covspan.expn_kind else { + return true; + }; let split_len = visible_macro.as_str().len() as u32 + 1; let (before, after) = covspan.span.split_at(split_len); @@ -110,8 +119,8 @@ fn split_visible_macro_spans(covspans: &mut Vec) { return true; } - extra_spans.push(SpanFromMir::new(before, covspan.visible_macro, covspan.bcb)); - extra_spans.push(SpanFromMir::new(after, covspan.visible_macro, covspan.bcb)); + extra_spans.push(SpanFromMir::new(before, covspan.expn_kind.clone(), covspan.bcb)); + extra_spans.push(SpanFromMir::new(after, covspan.expn_kind.clone(), covspan.bcb)); false // Discard the original covspan that we just split. }); diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 32bd25bf4b966..7f5765c9462de 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -3,13 +3,13 @@ use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::{ self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind, }; -use rustc_span::{Span, Symbol}; +use rustc_span::{ExpnKind, Span}; use crate::coverage::graph::{ BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, }; use crate::coverage::spans::Covspan; -use crate::coverage::unexpand::unexpand_into_body_span_with_visible_macro; +use crate::coverage::unexpand::unexpand_into_body_span_with_expn_kind; use crate::coverage::ExtractedHirInfo; pub(crate) struct ExtractedCovspans { @@ -60,7 +60,7 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( let data = &mir_body[bb]; let unexpand = move |expn_span| { - unexpand_into_body_span_with_visible_macro(expn_span, body_span) + unexpand_into_body_span_with_expn_kind(expn_span, body_span) // Discard any spans that fill the entire body, because they tend // to represent compiler-inserted code, e.g. implicitly returning `()`. .filter(|(span, _)| !span.source_equal(body_span)) @@ -68,9 +68,9 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( let mut extract_statement_span = |statement| { let expn_span = filtered_statement_span(statement)?; - let (span, visible_macro) = unexpand(expn_span)?; + let (span, expn_kind) = unexpand(expn_span)?; - initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb)); + initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb)); Some(()) }; for statement in data.statements.iter() { @@ -79,9 +79,9 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( let mut extract_terminator_span = |terminator| { let expn_span = filtered_terminator_span(terminator)?; - let (span, visible_macro) = unexpand(expn_span)?; + let (span, expn_kind) = unexpand(expn_span)?; - initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb)); + initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb)); Some(()) }; extract_terminator_span(data.terminator()); @@ -214,7 +214,7 @@ pub(crate) struct SpanFromMir { /// With the exception of `fn_sig_span`, this should always be contained /// within `body_span`. pub(crate) span: Span, - pub(crate) visible_macro: Option, + pub(crate) expn_kind: Option, pub(crate) bcb: BasicCoverageBlock, } @@ -223,12 +223,12 @@ impl SpanFromMir { Self::new(fn_sig_span, None, START_BCB) } - pub(crate) fn new(span: Span, visible_macro: Option, bcb: BasicCoverageBlock) -> Self { - Self { span, visible_macro, bcb } + pub(crate) fn new(span: Span, expn_kind: Option, bcb: BasicCoverageBlock) -> Self { + Self { span, expn_kind, bcb } } pub(crate) fn into_covspan(self) -> Covspan { - let Self { span, visible_macro: _, bcb } = self; + let Self { span, expn_kind: _, bcb } = self; Covspan { span, bcb } } } diff --git a/compiler/rustc_mir_transform/src/coverage/unexpand.rs b/compiler/rustc_mir_transform/src/coverage/unexpand.rs index 8cde291b9073e..cb8615447362b 100644 --- a/compiler/rustc_mir_transform/src/coverage/unexpand.rs +++ b/compiler/rustc_mir_transform/src/coverage/unexpand.rs @@ -1,4 +1,4 @@ -use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; +use rustc_span::{ExpnKind, Span}; /// Walks through the expansion ancestors of `original_span` to find a span that /// is contained in `body_span` and has the same [syntax context] as `body_span`. @@ -13,20 +13,15 @@ pub(crate) fn unexpand_into_body_span(original_span: Span, body_span: Span) -> O /// /// If the returned span represents a bang-macro invocation (e.g. `foo!(..)`), /// the returned symbol will be the name of that macro (e.g. `foo`). -pub(crate) fn unexpand_into_body_span_with_visible_macro( +pub(crate) fn unexpand_into_body_span_with_expn_kind( original_span: Span, body_span: Span, -) -> Option<(Span, Option)> { +) -> Option<(Span, Option)> { let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?; - let visible_macro = prev - .map(|prev| match prev.ctxt().outer_expn_data().kind { - ExpnKind::Macro(MacroKind::Bang, name) => Some(name), - _ => None, - }) - .flatten(); + let expn_kind = prev.map(|prev| prev.ctxt().outer_expn_data().kind); - Some((span, visible_macro)) + Some((span, expn_kind)) } /// Walks through the expansion ancestors of `original_span` to find a span that diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 50aaed090f6b8..ce109ef7674f0 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -10,7 +10,7 @@ use rustc_span::sym; use crate::{inline, pass_manager as pm}; -pub fn provide(providers: &mut Providers) { +pub(super) fn provide(providers: &mut Providers) { providers.cross_crate_inlinable = cross_crate_inlinable; } diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index ff9fc776e541f..bd58b1b668997 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -6,12 +6,11 @@ use rustc_middle::mir::{ BasicBlock, BasicBlockData, Body, Statement, StatementKind, TerminatorKind, }; use rustc_middle::ty::TyCtxt; +use tracing::instrument; -use crate::MirPass; +pub(super) struct CtfeLimit; -pub struct CtfeLimit; - -impl<'tcx> MirPass<'tcx> for CtfeLimit { +impl<'tcx> crate::MirPass<'tcx> for CtfeLimit { #[instrument(skip(self, _tcx, body))] fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let doms = body.basic_blocks.dominators(); diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 03b1d426df4ad..7ac019ce81216 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -19,15 +19,16 @@ use rustc_mir_dataflow::value_analysis::{ use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor}; use rustc_span::DUMMY_SP; use rustc_target::abi::{Abi, FieldIdx, Size, VariantIdx, FIRST_VARIANT}; +use tracing::{debug, debug_span, instrument}; // These constants are somewhat random guesses and have not been optimized. // If `tcx.sess.mir_opt_level() >= 4`, we ignore the limits (this can become very expensive). const BLOCK_LIMIT: usize = 100; const PLACE_LIMIT: usize = 100; -pub struct DataflowConstProp; +pub(super) struct DataflowConstProp; -impl<'tcx> MirPass<'tcx> for DataflowConstProp { +impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() >= 3 } @@ -331,7 +332,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { } impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map<'tcx>) -> Self { + fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map<'tcx>) -> Self { let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); Self { map, @@ -646,7 +647,8 @@ fn try_write_constant<'tcx>( ty::FnDef(..) => {} // Those are scalars, must be handled above. - ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => throw_machine_stop_str!("primitive type with provenance"), + ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => + throw_machine_stop_str!("primitive type with provenance"), ty::Tuple(elem_tys) => { for (i, elem) in elem_tys.iter().enumerate() { @@ -837,14 +839,14 @@ impl<'tcx> MutVisitor<'tcx> for Patch<'tcx> { } } -struct OperandCollector<'tcx, 'map, 'locals, 'a> { +struct OperandCollector<'a, 'locals, 'tcx> { state: &'a State>, visitor: &'a mut Collector<'tcx, 'locals>, - ecx: &'map mut InterpCx<'tcx, DummyMachine>, - map: &'map Map<'tcx>, + ecx: &'a mut InterpCx<'tcx, DummyMachine>, + map: &'a Map<'tcx>, } -impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> { +impl<'tcx> Visitor<'tcx> for OperandCollector<'_, '_, 'tcx> { fn visit_projection_elem( &mut self, _: PlaceRef<'tcx>, diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index f473073083af4..65c037559c595 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -28,11 +28,11 @@ use crate::util::is_within_packed; /// /// The `borrowed` set must be a `BitSet` of all the locals that are ever borrowed in this body. It /// can be generated via the [`borrowed_locals`] function. -pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { +fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let borrowed_locals = borrowed_locals(body); // If the user requests complete debuginfo, mark the locals that appear in it as live, so - // we don't remove assignements to them. + // we don't remove assignments to them. let mut always_live = debuginfo_locals(body); always_live.union(&borrowed_locals); @@ -127,12 +127,12 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } } -pub enum DeadStoreElimination { +pub(super) enum DeadStoreElimination { Initial, Final, } -impl<'tcx> MirPass<'tcx> for DeadStoreElimination { +impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination { fn name(&self) -> &'static str { match self { DeadStoreElimination::Initial => "DeadStoreElimination-initial", diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index 71af099199ea9..c645bbee08a54 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -42,9 +42,9 @@ impl<'tcx> Visitor<'tcx> for DeduceReadOnly { } PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow) => { // Whether mutating though a `&raw const` is allowed is still undecided, so we - // disable any sketchy `readonly` optimizations for now. - // But we only need to do this if the pointer would point into the argument. - // IOW: for indirect places, like `&raw (*local).field`, this surely cannot mutate `local`. + // disable any sketchy `readonly` optimizations for now. But we only need to do + // this if the pointer would point into the argument. IOW: for indirect places, + // like `&raw (*local).field`, this surely cannot mutate `local`. !place.is_indirect() } PlaceContext::NonMutatingUse(..) | PlaceContext::NonUse(..) => { @@ -150,7 +150,7 @@ fn type_will_always_be_passed_directly(ty: Ty<'_>) -> bool { /// body of the function instead of just the signature. These can be useful for optimization /// purposes on a best-effort basis. We compute them here and store them into the crate metadata so /// dependent crates can use them. -pub fn deduced_param_attrs<'tcx>( +pub(super) fn deduced_param_attrs<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, ) -> &'tcx [DeducedParamAttrs] { diff --git a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs index 4a94c3eca8658..ad204e76d0d15 100644 --- a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs +++ b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs @@ -9,12 +9,13 @@ use rustc_data_structures::fx::FxHashMap; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use tracing::debug; use super::simplify::simplify_cfg; -pub struct DeduplicateBlocks; +pub(super) struct DeduplicateBlocks; -impl<'tcx> MirPass<'tcx> for DeduplicateBlocks { +impl<'tcx> crate::MirPass<'tcx> for DeduplicateBlocks { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() >= 4 } @@ -68,8 +69,8 @@ fn find_duplicates(body: &Body<'_>) -> FxHashMap { // For example, if bb1, bb2 and bb3 are duplicates, we will first insert bb3 in same_hashes. // Then we will see that bb2 is a duplicate of bb3, // and insert bb2 with the replacement bb3 in the duplicates list. - // When we see bb1, we see that it is a duplicate of bb3, and therefore insert it in the duplicates list - // with replacement bb3. + // When we see bb1, we see that it is a duplicate of bb3, and therefore insert it in the + // duplicates list with replacement bb3. // When the duplicates are removed, we will end up with only bb3. for (bb, bbd) in body.basic_blocks.iter_enumerated().rev().filter(|(_, bbd)| !bbd.is_cleanup) { // Basic blocks can get really big, so to avoid checking for duplicates in basic blocks @@ -104,7 +105,8 @@ struct BasicBlockHashable<'tcx, 'a> { impl Hash for BasicBlockHashable<'_, '_> { fn hash(&self, state: &mut H) { hash_statements(state, self.basic_block_data.statements.iter()); - // Note that since we only hash the kind, we lose span information if we deduplicate the blocks + // Note that since we only hash the kind, we lose span information if we deduplicate the + // blocks. self.basic_block_data.terminator().kind.hash(state); } } diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs index 0e2fccc85dacd..ad7ccef4976aa 100644 --- a/compiler/rustc_mir_transform/src/deref_separator.rs +++ b/compiler/rustc_mir_transform/src/deref_separator.rs @@ -1,16 +1,15 @@ -use rustc_index::IndexVec; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::visit::NonUseContext::VarDebugInfo; use rustc_middle::mir::visit::{MutVisitor, PlaceContext}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -pub struct Derefer; +pub(super) struct Derefer; -pub struct DerefChecker<'a, 'tcx> { +struct DerefChecker<'a, 'tcx> { tcx: TyCtxt<'tcx>, patcher: MirPatch<'tcx>, - local_decls: &'a IndexVec>, + local_decls: &'a LocalDecls<'tcx>, } impl<'a, 'tcx> MutVisitor<'tcx> for DerefChecker<'a, 'tcx> { @@ -67,7 +66,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for DerefChecker<'a, 'tcx> { } } -pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { +pub(super) fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let patch = MirPatch::new(body); let mut checker = DerefChecker { tcx, patcher: patch, local_decls: &body.local_decls }; @@ -78,7 +77,7 @@ pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { checker.patcher.apply(body); } -impl<'tcx> MirPass<'tcx> for Derefer { +impl<'tcx> crate::MirPass<'tcx> for Derefer { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { deref_finder(tcx, body); } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index ed924761892c7..f123658580da4 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -38,7 +38,7 @@ //! not contain any indirection through a pointer or any indexing projections. //! //! * `p` and `q` must have the **same type**. If we replace a local with a subtype or supertype, -//! we may end up with a differnet vtable for that local. See the `subtyping-impacts-selection` +//! we may end up with a different vtable for that local. See the `subtyping-impacts-selection` //! tests for an example where that causes issues. //! //! * We need to make sure that the goal of "merging the memory" is actually structurally possible @@ -144,12 +144,11 @@ use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::impls::MaybeLiveLocals; use rustc_mir_dataflow::points::{save_as_intervals, DenseLocationMap, PointIndex}; use rustc_mir_dataflow::Analysis; +use tracing::{debug, trace}; -use crate::MirPass; +pub(super) struct DestinationPropagation; -pub struct DestinationPropagation; - -impl<'tcx> MirPass<'tcx> for DestinationPropagation { +impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { // For now, only run at MIR opt level 3. Two things need to be changed before this can be // turned on by default: @@ -164,7 +163,8 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let def_id = body.source.def_id(); - let mut allocations = Allocations::default(); + let mut candidates = Candidates::default(); + let mut write_info = WriteInfo::default(); trace!(func = ?tcx.def_path_str(def_id)); let borrowed = rustc_mir_dataflow::impls::borrowed_locals(body); @@ -192,12 +192,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { loop { // PERF: Can we do something smarter than recalculating the candidates and liveness // results? - let mut candidates = find_candidates( - body, - &borrowed, - &mut allocations.candidates, - &mut allocations.candidates_reverse, - ); + candidates.reset_and_find(body, &borrowed); trace!(?candidates); dest_prop_mir_dump(tcx, body, &points, &live, round_count); @@ -205,7 +200,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { &mut candidates, &points, &live, - &mut allocations.write_info, + &mut write_info, body, ); @@ -247,27 +242,15 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { } round_count += 1; - apply_merges(body, tcx, &merges, &merged_locals); + apply_merges(body, tcx, merges, merged_locals); } trace!(round_count); } } -/// Container for the various allocations that we need. -/// -/// We store these here and hand out `&mut` access to them, instead of dropping and recreating them -/// frequently. Everything with a `&'alloc` lifetime points into here. -#[derive(Default)] -struct Allocations { - candidates: FxIndexMap>, - candidates_reverse: FxIndexMap>, - write_info: WriteInfo, - // PERF: Do this for `MaybeLiveLocals` allocations too. -} - -#[derive(Debug)] -struct Candidates<'alloc> { +#[derive(Debug, Default)] +struct Candidates { /// The set of candidates we are considering in this optimization. /// /// We will always merge the key into at most one of its values. @@ -282,11 +265,12 @@ struct Candidates<'alloc> { /// /// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to /// remove that assignment. - c: &'alloc mut FxIndexMap>, + c: FxIndexMap>, + /// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`, /// then this contains `b => a`. // PERF: Possibly these should be `SmallVec`s? - reverse: &'alloc mut FxIndexMap>, + reverse: FxIndexMap>, } ////////////////////////////////////////////////////////// @@ -297,20 +281,20 @@ struct Candidates<'alloc> { fn apply_merges<'tcx>( body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>, - merges: &FxIndexMap, - merged_locals: &BitSet, + merges: FxIndexMap, + merged_locals: BitSet, ) { let mut merger = Merger { tcx, merges, merged_locals }; merger.visit_body_preserves_cfg(body); } -struct Merger<'a, 'tcx> { +struct Merger<'tcx> { tcx: TyCtxt<'tcx>, - merges: &'a FxIndexMap, - merged_locals: &'a BitSet, + merges: FxIndexMap, + merged_locals: BitSet, } -impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> { +impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -359,19 +343,40 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> { // // This section enforces bullet point 2 -struct FilterInformation<'a, 'body, 'alloc, 'tcx> { - body: &'body Body<'tcx>, +struct FilterInformation<'a, 'tcx> { + body: &'a Body<'tcx>, points: &'a DenseLocationMap, live: &'a SparseIntervalMatrix, - candidates: &'a mut Candidates<'alloc>, - write_info: &'alloc mut WriteInfo, + candidates: &'a mut Candidates, + write_info: &'a mut WriteInfo, at: Location, } // We first implement some utility functions which we will expose removing candidates according to // different needs. Throughout the liveness filtering, the `candidates` are only ever accessed // through these methods, and not directly. -impl<'alloc> Candidates<'alloc> { +impl Candidates { + /// Collects the candidates for merging. + /// + /// This is responsible for enforcing the first and third bullet point. + fn reset_and_find<'tcx>(&mut self, body: &Body<'tcx>, borrowed: &BitSet) { + self.c.clear(); + self.reverse.clear(); + let mut visitor = FindAssignments { body, candidates: &mut self.c, borrowed }; + visitor.visit_body(body); + // Deduplicate candidates. + for (_, cands) in self.c.iter_mut() { + cands.sort(); + cands.dedup(); + } + // Generate the reverse map. + for (src, cands) in self.c.iter() { + for dest in cands.iter().copied() { + self.reverse.entry(dest).or_default().push(*src); + } + } + } + /// Just `Vec::retain`, but the condition is inverted and we add debugging output fn vec_filter_candidates( src: Local, @@ -446,7 +451,7 @@ enum CandidateFilter { Remove, } -impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> { +impl<'a, 'tcx> FilterInformation<'a, 'tcx> { /// Filters the set of candidates to remove those that conflict. /// /// The steps we take are exactly those that are outlined at the top of the file. For each @@ -464,12 +469,12 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> { /// before the statement/terminator will correctly report locals that are read in the /// statement/terminator to be live. We are additionally conservative by treating all written to /// locals as also being read from. - fn filter_liveness<'b>( - candidates: &mut Candidates<'alloc>, + fn filter_liveness( + candidates: &mut Candidates, points: &DenseLocationMap, live: &SparseIntervalMatrix, - write_info_alloc: &'alloc mut WriteInfo, - body: &'b Body<'tcx>, + write_info: &mut WriteInfo, + body: &Body<'tcx>, ) { let mut this = FilterInformation { body, @@ -478,7 +483,7 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> { candidates, // We don't actually store anything at this scope, we just keep things here to be able // to reuse the allocation. - write_info: write_info_alloc, + write_info, // Doesn't matter what we put here, will be overwritten before being used at: Location::START, }; @@ -735,40 +740,13 @@ fn places_to_candidate_pair<'tcx>( Some((a, b)) } -/// Collects the candidates for merging -/// -/// This is responsible for enforcing the first and third bullet point. -fn find_candidates<'alloc, 'tcx>( - body: &Body<'tcx>, - borrowed: &BitSet, - candidates: &'alloc mut FxIndexMap>, - candidates_reverse: &'alloc mut FxIndexMap>, -) -> Candidates<'alloc> { - candidates.clear(); - candidates_reverse.clear(); - let mut visitor = FindAssignments { body, candidates, borrowed }; - visitor.visit_body(body); - // Deduplicate candidates - for (_, cands) in candidates.iter_mut() { - cands.sort(); - cands.dedup(); - } - // Generate the reverse map - for (src, cands) in candidates.iter() { - for dest in cands.iter().copied() { - candidates_reverse.entry(dest).or_default().push(*src); - } - } - Candidates { c: candidates, reverse: candidates_reverse } -} - -struct FindAssignments<'a, 'alloc, 'tcx> { +struct FindAssignments<'a, 'tcx> { body: &'a Body<'tcx>, - candidates: &'alloc mut FxIndexMap>, + candidates: &'a mut FxIndexMap>, borrowed: &'a BitSet, } -impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> { +impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { if let StatementKind::Assign(box ( lhs, @@ -820,9 +798,9 @@ fn is_local_required(local: Local, body: &Body<'_>) -> bool { ///////////////////////////////////////////////////////// // MIR Dump -fn dest_prop_mir_dump<'body, 'tcx>( +fn dest_prop_mir_dump<'tcx>( tcx: TyCtxt<'tcx>, - body: &'body Body<'tcx>, + body: &Body<'tcx>, points: &DenseLocationMap, live: &SparseIntervalMatrix, round: usize, diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index 29db45f94506f..1640d34d6c8ee 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs @@ -7,11 +7,9 @@ use rustc_middle::mir::{write_mir_pretty, Body}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{OutFileName, OutputType}; -use crate::MirPass; +pub(super) struct Marker(pub &'static str); -pub struct Marker(pub &'static str); - -impl<'tcx> MirPass<'tcx> for Marker { +impl<'tcx> crate::MirPass<'tcx> for Marker { fn name(&self) -> &'static str { self.0 } diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 49e41c35f1f24..704ed508b22a8 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -3,6 +3,7 @@ use std::fmt::Debug; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::ty::{Ty, TyCtxt}; +use tracing::trace; use super::simplify::simplify_cfg; @@ -89,9 +90,9 @@ use super::simplify::simplify_cfg; /// | ... | /// ================= /// ``` -pub struct EarlyOtherwiseBranch; +pub(super) struct EarlyOtherwiseBranch; -impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { +impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() >= 2 } @@ -260,8 +261,8 @@ fn evaluate_candidate<'tcx>( // }; // ``` // - // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an - // invalid value, which is UB. + // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant + // of an invalid value, which is UB. // In order to fix this, **we would either need to show that the discriminant computation of // `place` is computed in all branches**. // FIXME(#95162) For the moment, we adopt a conservative approach and @@ -309,42 +310,28 @@ fn verify_candidate_branch<'tcx>( ) -> bool { // In order for the optimization to be correct, the branch must... // ...have exactly one statement - let [statement] = branch.statements.as_slice() else { - return false; - }; - // ...assign the discriminant of `place` in that statement - let StatementKind::Assign(boxed) = &statement.kind else { return false }; - let (discr_place, Rvalue::Discriminant(from_place)) = &**boxed else { return false }; - if *from_place != place { - return false; - } - // ...make that assignment to a local - if discr_place.projection.len() != 0 { - return false; - } - // ...terminate on a `SwitchInt` that invalidates that local - let TerminatorKind::SwitchInt { discr: switch_op, targets, .. } = &branch.terminator().kind - else { - return false; - }; - if *switch_op != Operand::Move(*discr_place) { - return false; - } - // ...fall through to `destination` if the switch misses - if destination != targets.otherwise() { - return false; - } - // ...have a branch for value `value` - let mut iter = targets.iter(); - let Some((target_value, _)) = iter.next() else { - return false; - }; - if target_value != value { - return false; - } - // ...and have no more branches - if let Some(_) = iter.next() { - return false; + if let [statement] = branch.statements.as_slice() + // ...assign the discriminant of `place` in that statement + && let StatementKind::Assign(boxed) = &statement.kind + && let (discr_place, Rvalue::Discriminant(from_place)) = &**boxed + && *from_place == place + // ...make that assignment to a local + && discr_place.projection.is_empty() + // ...terminate on a `SwitchInt` that invalidates that local + && let TerminatorKind::SwitchInt { discr: switch_op, targets, .. } = + &branch.terminator().kind + && *switch_op == Operand::Move(*discr_place) + // ...fall through to `destination` if the switch misses + && destination == targets.otherwise() + // ...have a branch for value `value` + && let mut iter = targets.iter() + && let Some((target_value, _)) = iter.next() + && target_value == value + // ...and have no more branches + && iter.next().is_none() + { + true + } else { + false } - true } diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index e5778f8a05d2e..367a8c0759315 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::{Ty, TyCtxt}; use rustc_target::abi::FieldIdx; /// Constructs the types used when accessing a Box's pointer -pub fn build_ptr_tys<'tcx>( +fn build_ptr_tys<'tcx>( tcx: TyCtxt<'tcx>, pointee: Ty<'tcx>, unique_did: DefId, @@ -26,7 +26,7 @@ pub fn build_ptr_tys<'tcx>( } /// Constructs the projection needed to access a Box's pointer -pub fn build_projection<'tcx>( +pub(super) fn build_projection<'tcx>( unique_ty: Ty<'tcx>, nonnull_ty: Ty<'tcx>, ptr_ty: Ty<'tcx>, @@ -62,11 +62,13 @@ impl<'tcx, 'a> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'tcx, 'a> { let base_ty = self.local_decls[place.local].ty; // Derefer ensures that derefs are always the first projection - if place.projection.first() == Some(&PlaceElem::Deref) && base_ty.is_box() { + if let Some(PlaceElem::Deref) = place.projection.first() + && let Some(boxed_ty) = base_ty.boxed_ty() + { let source_info = self.local_decls[place.local].source_info; let (unique_ty, nonnull_ty, ptr_ty) = - build_ptr_tys(tcx, base_ty.boxed_ty(), self.unique_did, self.nonnull_did); + build_ptr_tys(tcx, boxed_ty, self.unique_did, self.nonnull_did); let ptr_local = self.patch.new_temp(ptr_ty, source_info.span); @@ -86,66 +88,65 @@ impl<'tcx, 'a> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'tcx, 'a> { } } -pub struct ElaborateBoxDerefs; +pub(super) struct ElaborateBoxDerefs; -impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs { +impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if let Some(def_id) = tcx.lang_items().owned_box() { - let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[FieldIdx::ZERO].did; + // If box is not present, this pass doesn't need to do anything. + let Some(def_id) = tcx.lang_items().owned_box() else { return }; - let Some(nonnull_def) = tcx.type_of(unique_did).instantiate_identity().ty_adt_def() - else { - span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique") - }; + let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[FieldIdx::ZERO].did; - let nonnull_did = nonnull_def.non_enum_variant().fields[FieldIdx::ZERO].did; + let Some(nonnull_def) = tcx.type_of(unique_did).instantiate_identity().ty_adt_def() else { + span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique") + }; - let patch = MirPatch::new(body); + let nonnull_did = nonnull_def.non_enum_variant().fields[FieldIdx::ZERO].did; - let local_decls = &mut body.local_decls; + let patch = MirPatch::new(body); - let mut visitor = - ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch }; + let local_decls = &mut body.local_decls; - for (block, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() { - visitor.visit_basic_block_data(block, data); - } + let mut visitor = + ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch }; - visitor.patch.apply(body); + for (block, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() { + visitor.visit_basic_block_data(block, data); + } - for debug_info in body.var_debug_info.iter_mut() { - if let VarDebugInfoContents::Place(place) = &mut debug_info.value { - let mut new_projections: Option> = None; + visitor.patch.apply(body); - for (base, elem) in place.iter_projections() { - let base_ty = base.ty(&body.local_decls, tcx).ty; + for debug_info in body.var_debug_info.iter_mut() { + if let VarDebugInfoContents::Place(place) = &mut debug_info.value { + let mut new_projections: Option> = None; - if elem == PlaceElem::Deref && base_ty.is_box() { - // Clone the projections before us, since now we need to mutate them. - let new_projections = - new_projections.get_or_insert_with(|| base.projection.to_vec()); + for (base, elem) in place.iter_projections() { + let base_ty = base.ty(&body.local_decls, tcx).ty; - let (unique_ty, nonnull_ty, ptr_ty) = - build_ptr_tys(tcx, base_ty.boxed_ty(), unique_did, nonnull_did); + if let PlaceElem::Deref = elem + && let Some(boxed_ty) = base_ty.boxed_ty() + { + // Clone the projections before us, since now we need to mutate them. + let new_projections = + new_projections.get_or_insert_with(|| base.projection.to_vec()); - new_projections.extend_from_slice(&build_projection( - unique_ty, nonnull_ty, ptr_ty, - )); - new_projections.push(PlaceElem::Deref); - } else if let Some(new_projections) = new_projections.as_mut() { - // Keep building up our projections list once we've started it. - new_projections.push(elem); - } - } + let (unique_ty, nonnull_ty, ptr_ty) = + build_ptr_tys(tcx, boxed_ty, unique_did, nonnull_did); - // Store the mutated projections if we actually changed something. - if let Some(new_projections) = new_projections { - place.projection = tcx.mk_place_elems(&new_projections); + new_projections + .extend_from_slice(&build_projection(unique_ty, nonnull_ty, ptr_ty)); + new_projections.push(PlaceElem::Deref); + } else if let Some(new_projections) = new_projections.as_mut() { + // Keep building up our projections list once we've started it. + new_projections.push(elem); } } + + // Store the mutated projections if we actually changed something. + if let Some(new_projections) = new_projections { + place.projection = tcx.mk_place_elems(&new_projections); + } } - } else { - // box is not present, this pass doesn't need to do anything } } } diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 5a22ef7790349..42d13fa3613ab 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -15,12 +15,13 @@ use rustc_mir_dataflow::{ }; use rustc_span::Span; use rustc_target::abi::{FieldIdx, VariantIdx}; +use tracing::{debug, instrument}; use crate::deref_separator::deref_finder; /// During MIR building, Drop terminators are inserted in every place where a drop may occur. -/// However, in this phase, the presence of these terminators does not guarantee that a destructor will run, -/// as the target of the drop may be uninitialized. +/// However, in this phase, the presence of these terminators does not guarantee that a destructor +/// will run, as the target of the drop may be uninitialized. /// In general, the compiler cannot determine at compile time whether a destructor will run or not. /// /// At a high level, this pass refines Drop to only run the destructor if the @@ -29,10 +30,10 @@ use crate::deref_separator::deref_finder; /// Once this is complete, Drop terminators in the MIR correspond to a call to the "drop glue" or /// "drop shim" for the type of the dropped place. /// -/// This pass relies on dropped places having an associated move path, which is then used to determine -/// the initialization status of the place and its descendants. -/// It's worth noting that a MIR containing a Drop without an associated move path is probably ill formed, -/// as it would allow running a destructor on a place behind a reference: +/// This pass relies on dropped places having an associated move path, which is then used to +/// determine the initialization status of the place and its descendants. +/// It's worth noting that a MIR containing a Drop without an associated move path is probably ill +/// formed, as it would allow running a destructor on a place behind a reference: /// /// ```text /// fn drop_term(t: &mut T) { @@ -46,9 +47,9 @@ use crate::deref_separator::deref_finder; /// } /// } /// ``` -pub struct ElaborateDrops; +pub(super) struct ElaborateDrops; -impl<'tcx> MirPass<'tcx> for ElaborateDrops { +impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { #[instrument(level = "trace", skip(self, tcx, body))] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!("elaborate_drops({:?} @ {:?})", body.source, body.span); @@ -97,9 +98,9 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { /// Records unwind edges which are known to be unreachable, because they are in `drop` terminators /// that can't drop anything. #[instrument(level = "trace", skip(body, flow_inits), ret)] -fn compute_dead_unwinds<'mir, 'tcx>( - body: &'mir Body<'tcx>, - flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>, +fn compute_dead_unwinds<'a, 'tcx>( + body: &'a Body<'tcx>, + flow_inits: &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, ) -> BitSet { // We only need to do this pass once, because unwind edges can only // reach cleanup blocks, which can't have unwind edges themselves. @@ -120,12 +121,12 @@ fn compute_dead_unwinds<'mir, 'tcx>( dead_unwinds } -struct InitializationData<'a, 'mir, 'tcx> { - inits: ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'a, 'mir, 'tcx>>, - uninits: ResultsCursor<'mir, 'tcx, MaybeUninitializedPlaces<'a, 'mir, 'tcx>>, +struct InitializationData<'a, 'tcx> { + inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, + uninits: ResultsCursor<'a, 'tcx, MaybeUninitializedPlaces<'a, 'tcx>>, } -impl InitializationData<'_, '_, '_> { +impl InitializationData<'_, '_> { fn seek_before(&mut self, loc: Location) { self.inits.seek_before_primary_effect(loc); self.uninits.seek_before_primary_effect(loc); @@ -136,45 +137,35 @@ impl InitializationData<'_, '_, '_> { } } -struct Elaborator<'a, 'b, 'mir, 'tcx> { - ctxt: &'a mut ElaborateDropsCtxt<'b, 'mir, 'tcx>, -} - -impl fmt::Debug for Elaborator<'_, '_, '_, '_> { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) - } -} - -impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, '_, 'tcx> { +impl<'a, 'tcx> DropElaborator<'a, 'tcx> for ElaborateDropsCtxt<'a, 'tcx> { type Path = MovePathIndex; fn patch(&mut self) -> &mut MirPatch<'tcx> { - &mut self.ctxt.patch + &mut self.patch } fn body(&self) -> &'a Body<'tcx> { - self.ctxt.body + self.body } fn tcx(&self) -> TyCtxt<'tcx> { - self.ctxt.tcx + self.tcx } fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.ctxt.param_env() + self.param_env() } #[instrument(level = "debug", skip(self), ret)] fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle { let ((maybe_live, maybe_dead), multipart) = match mode { - DropFlagMode::Shallow => (self.ctxt.init_data.maybe_live_dead(path), false), + DropFlagMode::Shallow => (self.init_data.maybe_live_dead(path), false), DropFlagMode::Deep => { let mut some_live = false; let mut some_dead = false; let mut children_count = 0; - on_all_children_bits(self.ctxt.move_data(), path, |child| { - let (live, dead) = self.ctxt.init_data.maybe_live_dead(child); + on_all_children_bits(self.move_data(), path, |child| { + let (live, dead) = self.init_data.maybe_live_dead(child); debug!("elaborate_drop: state({:?}) = {:?}", child, (live, dead)); some_live |= live; some_dead |= dead; @@ -194,25 +185,25 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, '_, 'tcx> { fn clear_drop_flag(&mut self, loc: Location, path: Self::Path, mode: DropFlagMode) { match mode { DropFlagMode::Shallow => { - self.ctxt.set_drop_flag(loc, path, DropFlagState::Absent); + self.set_drop_flag(loc, path, DropFlagState::Absent); } DropFlagMode::Deep => { - on_all_children_bits(self.ctxt.move_data(), path, |child| { - self.ctxt.set_drop_flag(loc, child, DropFlagState::Absent) + on_all_children_bits(self.move_data(), path, |child| { + self.set_drop_flag(loc, child, DropFlagState::Absent) }); } } } fn field_subpath(&self, path: Self::Path, field: FieldIdx) -> Option { - rustc_mir_dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { + rustc_mir_dataflow::move_path_children_matching(self.move_data(), path, |e| match e { ProjectionElem::Field(idx, _) => idx == field, _ => false, }) } fn array_subpath(&self, path: Self::Path, index: u64, size: u64) -> Option { - rustc_mir_dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { + rustc_mir_dataflow::move_path_children_matching(self.move_data(), path, |e| match e { ProjectionElem::ConstantIndex { offset, min_length, from_end } => { debug_assert!(size == min_length, "min_length should be exact for arrays"); assert!(!from_end, "from_end should not be used for array element ConstantIndex"); @@ -223,34 +214,40 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, '_, 'tcx> { } fn deref_subpath(&self, path: Self::Path) -> Option { - rustc_mir_dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| { + rustc_mir_dataflow::move_path_children_matching(self.move_data(), path, |e| { e == ProjectionElem::Deref }) } fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option { - rustc_mir_dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { + rustc_mir_dataflow::move_path_children_matching(self.move_data(), path, |e| match e { ProjectionElem::Downcast(_, idx) => idx == variant, _ => false, }) } fn get_drop_flag(&mut self, path: Self::Path) -> Option> { - self.ctxt.drop_flag(path).map(Operand::Copy) + self.drop_flag(path).map(Operand::Copy) } } -struct ElaborateDropsCtxt<'a, 'mir, 'tcx> { +struct ElaborateDropsCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, - body: &'mir Body<'tcx>, + body: &'a Body<'tcx>, env: &'a MoveDataParamEnv<'tcx>, - init_data: InitializationData<'a, 'mir, 'tcx>, + init_data: InitializationData<'a, 'tcx>, drop_flags: IndexVec>, patch: MirPatch<'tcx>, } -impl<'b, 'mir, 'tcx> ElaborateDropsCtxt<'b, 'mir, 'tcx> { - fn move_data(&self) -> &'b MoveData<'tcx> { +impl fmt::Debug for ElaborateDropsCtxt<'_, '_> { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + Ok(()) + } +} + +impl<'a, 'tcx> ElaborateDropsCtxt<'a, 'tcx> { + fn move_data(&self) -> &'a MoveData<'tcx> { &self.env.move_data } @@ -369,15 +366,7 @@ impl<'b, 'mir, 'tcx> ElaborateDropsCtxt<'b, 'mir, 'tcx> { } }; self.init_data.seek_before(self.body.terminator_loc(bb)); - elaborate_drop( - &mut Elaborator { ctxt: self }, - terminator.source_info, - place, - path, - target, - unwind, - bb, - ) + elaborate_drop(self, terminator.source_info, place, path, target, unwind, bb) } LookupResult::Parent(None) => {} LookupResult::Parent(Some(_)) => { @@ -388,8 +377,8 @@ impl<'b, 'mir, 'tcx> ElaborateDropsCtxt<'b, 'mir, 'tcx> { ); } // A drop and replace behind a pointer/array/whatever. - // The borrow checker requires that these locations are initialized before the assignment, - // so we just leave an unconditional drop. + // The borrow checker requires that these locations are initialized before the + // assignment, so we just leave an unconditional drop. assert!(!data.is_cleanup); } } diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 2703dc57cdad9..9a93c3a72b1c9 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -64,7 +64,7 @@ impl<'a, P: std::fmt::Debug> LintDiagnostic<'a, ()> for AssertLint

{ } impl AssertLintKind { - pub fn lint(&self) -> &'static Lint { + pub(crate) fn lint(&self) -> &'static Lint { match self { AssertLintKind::ArithmeticOverflow => lint::builtin::ARITHMETIC_OVERFLOW, AssertLintKind::UnconditionalPanic => lint::builtin::UNCONDITIONAL_PANIC, diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index 9a2cc057232f3..8490b7e235818 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -6,6 +6,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::FFI_UNWIND_CALLS; use rustc_target::spec::abi::Abi; use rustc_target::spec::PanicStrategy; +use tracing::debug; use crate::errors; @@ -59,8 +60,9 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { let fn_def_id = match ty.kind() { ty::FnPtr(..) => None, &ty::FnDef(def_id, _) => { - // Rust calls cannot themselves create foreign unwinds (even if they use a non-Rust ABI). - // So the leak of the foreign unwind into Rust can only be elsewhere, not here. + // Rust calls cannot themselves create foreign unwinds (even if they use a non-Rust + // ABI). So the leak of the foreign unwind into Rust can only be elsewhere, not + // here. if !tcx.is_foreign_item(def_id) { continue; } diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index b7873e73c18c7..eb09a7d3b2114 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -9,11 +9,11 @@ use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi; -use crate::{errors, MirLint}; +use crate::errors; -pub struct FunctionItemReferences; +pub(super) struct FunctionItemReferences; -impl<'tcx> MirLint<'tcx> for FunctionItemReferences { +impl<'tcx> crate::MirLint<'tcx> for FunctionItemReferences { fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let mut checker = FunctionItemRefChecker { tcx, body }; checker.visit_body(body); @@ -92,8 +92,8 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { { let mut span = self.nth_arg_span(args, arg_num); if span.from_expansion() { - // The operand's ctxt wouldn't display the lint since it's inside a macro so - // we have to use the callsite's ctxt. + // The operand's ctxt wouldn't display the lint since it's + // inside a macro so we have to use the callsite's ctxt. let callsite_ctxt = span.source_callsite().ctxt(); span = span.with_ctxt(callsite_ctxt); } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 90e3ba26a438e..598bf61483d2f 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -105,12 +105,13 @@ use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; use rustc_target::abi::{self, Abi, FieldIdx, Size, VariantIdx, FIRST_VARIANT}; use smallvec::SmallVec; +use tracing::{debug, instrument, trace}; use crate::ssa::{AssignedValue, SsaLocals}; -pub struct GVN; +pub(super) struct GVN; -impl<'tcx> MirPass<'tcx> for GVN { +impl<'tcx> crate::MirPass<'tcx> for GVN { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() >= 2 } @@ -118,53 +119,50 @@ impl<'tcx> MirPass<'tcx> for GVN { #[instrument(level = "trace", skip(self, tcx, body))] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!(def_id = ?body.source.def_id()); - propagate_ssa(tcx, body); - } -} -fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); - let ssa = SsaLocals::new(tcx, body, param_env); - // Clone dominators as we need them while mutating the body. - let dominators = body.basic_blocks.dominators().clone(); - - let mut state = VnState::new(tcx, body, param_env, &ssa, &dominators, &body.local_decls); - ssa.for_each_assignment_mut( - body.basic_blocks.as_mut_preserves_cfg(), - |local, value, location| { - let value = match value { - // We do not know anything of this assigned value. - AssignedValue::Arg | AssignedValue::Terminator => None, - // Try to get some insight. - AssignedValue::Rvalue(rvalue) => { - let value = state.simplify_rvalue(rvalue, location); - // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark `local` as - // reusable if we have an exact type match. - if state.local_decls[local].ty != rvalue.ty(state.local_decls, tcx) { - return; + let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); + let ssa = SsaLocals::new(tcx, body, param_env); + // Clone dominators because we need them while mutating the body. + let dominators = body.basic_blocks.dominators().clone(); + + let mut state = VnState::new(tcx, body, param_env, &ssa, dominators, &body.local_decls); + ssa.for_each_assignment_mut( + body.basic_blocks.as_mut_preserves_cfg(), + |local, value, location| { + let value = match value { + // We do not know anything of this assigned value. + AssignedValue::Arg | AssignedValue::Terminator => None, + // Try to get some insight. + AssignedValue::Rvalue(rvalue) => { + let value = state.simplify_rvalue(rvalue, location); + // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark + // `local` as reusable if we have an exact type match. + if state.local_decls[local].ty != rvalue.ty(state.local_decls, tcx) { + return; + } + value } - value - } - }; - // `next_opaque` is `Some`, so `new_opaque` must return `Some`. - let value = value.or_else(|| state.new_opaque()).unwrap(); - state.assign(local, value); - }, - ); - - // Stop creating opaques during replacement as it is useless. - state.next_opaque = None; - - let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec(); - for bb in reverse_postorder { - let data = &mut body.basic_blocks.as_mut_preserves_cfg()[bb]; - state.visit_basic_block_data(bb, data); - } + }; + // `next_opaque` is `Some`, so `new_opaque` must return `Some`. + let value = value.or_else(|| state.new_opaque()).unwrap(); + state.assign(local, value); + }, + ); + + // Stop creating opaques during replacement as it is useless. + state.next_opaque = None; - // For each local that is reused (`y` above), we remove its storage statements do avoid any - // difficulty. Those locals are SSA, so should be easy to optimize by LLVM without storage - // statements. - StorageRemover { tcx, reused_locals: state.reused_locals }.visit_body_preserves_cfg(body); + let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec(); + for bb in reverse_postorder { + let data = &mut body.basic_blocks.as_mut_preserves_cfg()[bb]; + state.visit_basic_block_data(bb, data); + } + + // For each local that is reused (`y` above), we remove its storage statements do avoid any + // difficulty. Those locals are SSA, so should be easy to optimize by LLVM without storage + // statements. + StorageRemover { tcx, reused_locals: state.reused_locals }.visit_body_preserves_cfg(body); + } } newtype_index! { @@ -260,7 +258,7 @@ struct VnState<'body, 'tcx> { /// Cache the value of the `unsized_locals` features, to avoid fetching it repeatedly in a loop. feature_unsized_locals: bool, ssa: &'body SsaLocals, - dominators: &'body Dominators, + dominators: Dominators, reused_locals: BitSet, } @@ -270,7 +268,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { body: &Body<'tcx>, param_env: ty::ParamEnv<'tcx>, ssa: &'body SsaLocals, - dominators: &'body Dominators, + dominators: Dominators, local_decls: &'body LocalDecls<'tcx>, ) -> Self { // Compute a rough estimate of the number of values in the body from the number of @@ -479,7 +477,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let pointer = self.evaluated[local].as_ref()?; let mut mplace = self.ecx.deref_pointer(pointer).ok()?; for proj in place.projection.iter().skip(1) { - // We have no call stack to associate a local with a value, so we cannot interpret indexing. + // We have no call stack to associate a local with a value, so we cannot + // interpret indexing. if matches!(proj, ProjectionElem::Index(_)) { return None; } @@ -1381,7 +1380,8 @@ fn op_to_prop_const<'tcx>( return Some(ConstValue::ZeroSized); } - // Do not synthetize too large constants. Codegen will just memcpy them, which we'd like to avoid. + // Do not synthetize too large constants. Codegen will just memcpy them, which we'd like to + // avoid. if !matches!(op.layout.abi, Abi::Scalar(..) | Abi::ScalarPair(..)) { return None; } @@ -1490,7 +1490,7 @@ impl<'tcx> VnState<'_, 'tcx> { let other = self.rev_locals.get(index)?; other .iter() - .find(|&&other| self.ssa.assignment_dominates(self.dominators, other, loc)) + .find(|&&other| self.ssa.assignment_dominates(&self.dominators, other, loc)) .copied() } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 2d6950be55d85..870cb180ce1be 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -20,6 +20,7 @@ use rustc_span::source_map::Spanned; use rustc_span::sym; use rustc_target::abi::FieldIdx; use rustc_target::spec::abi::Abi; +use tracing::{debug, instrument, trace, trace_span}; use crate::cost_checker::CostChecker; use crate::deref_separator::deref_finder; @@ -31,9 +32,11 @@ pub(crate) mod cycle; const TOP_DOWN_DEPTH_LIMIT: usize = 5; +// Made public so that `mir_drops_elaborated_and_const_checked` can be overridden +// by custom rustc drivers, running all the steps by themselves. See #114628. pub struct Inline; -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] struct CallSite<'tcx> { callee: Instance<'tcx>, fn_sig: ty::PolyFnSig<'tcx>, @@ -41,7 +44,7 @@ struct CallSite<'tcx> { source_info: SourceInfo, } -impl<'tcx> MirPass<'tcx> for Inline { +impl<'tcx> crate::MirPass<'tcx> for Inline { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { // FIXME(#127234): Coverage instrumentation currently doesn't handle inlined // MIR correctly when Modified Condition/Decision Coverage is enabled. @@ -155,7 +158,6 @@ impl<'tcx> Inliner<'tcx> { match self.try_inlining(caller_body, &callsite) { Err(reason) => { debug!("not-inlined {} [{}]", callsite.callee, reason); - continue; } Ok(new_blocks) => { debug!("inlined {}", callsite.callee); @@ -566,7 +568,8 @@ impl<'tcx> Inliner<'tcx> { // if the no-attribute function ends up with the same instruction set anyway. return Err("Cannot move inline-asm across instruction sets"); } else if let TerminatorKind::TailCall { .. } = term.kind { - // FIXME(explicit_tail_calls): figure out how exactly functions containing tail calls can be inlined (and if they even should) + // FIXME(explicit_tail_calls): figure out how exactly functions containing tail + // calls can be inlined (and if they even should) return Err("can't inline functions with tail calls"); } else { work_list.extend(term.successors()) @@ -637,7 +640,7 @@ impl<'tcx> Inliner<'tcx> { ); let dest_ty = dest.ty(caller_body, self.tcx); let temp = - Place::from(self.new_call_temp(caller_body, &callsite, dest_ty, return_block)); + Place::from(self.new_call_temp(caller_body, callsite, dest_ty, return_block)); caller_body[callsite.block].statements.push(Statement { source_info: callsite.source_info, kind: StatementKind::Assign(Box::new((temp, dest))), @@ -656,7 +659,7 @@ impl<'tcx> Inliner<'tcx> { true, self.new_call_temp( caller_body, - &callsite, + callsite, destination.ty(caller_body, self.tcx).ty, return_block, ), @@ -664,7 +667,7 @@ impl<'tcx> Inliner<'tcx> { }; // Copy the arguments if needed. - let args = self.make_call_args(args, &callsite, caller_body, &callee_body, return_block); + let args = self.make_call_args(args, callsite, caller_body, &callee_body, return_block); let mut integrator = Integrator { args: &args, diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index c65cc993b19fd..9828e90de883e 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -5,6 +5,7 @@ use rustc_middle::mir::TerminatorKind; use rustc_middle::ty::{self, GenericArgsRef, InstanceKind, TyCtxt, TypeVisitableExt}; use rustc_session::Limit; use rustc_span::sym; +use tracing::{instrument, trace}; // FIXME: check whether it is cheaper to precompute the entire call graph instead of invoking // this query ridiculously often. diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 3ec553d0ba0c5..0b344f29b078b 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -13,24 +13,18 @@ use rustc_target::spec::abi::Abi; use crate::simplify::simplify_duplicate_switch_targets; use crate::take_array; -pub enum InstSimplify { +pub(super) enum InstSimplify { BeforeInline, AfterSimplifyCfg, } -impl InstSimplify { - pub fn name(&self) -> &'static str { +impl<'tcx> crate::MirPass<'tcx> for InstSimplify { + fn name(&self) -> &'static str { match self { InstSimplify::BeforeInline => "InstSimplify-before-inline", InstSimplify::AfterSimplifyCfg => "InstSimplify-after-simplifycfg", } } -} - -impl<'tcx> MirPass<'tcx> for InstSimplify { - fn name(&self) -> &'static str { - self.name() - } fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() > 0 diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 8997b1d57cd91..d48df59ada860 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -51,16 +51,17 @@ use rustc_mir_dataflow::lattice::HasBottom; use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, State, TrackElem}; use rustc_span::DUMMY_SP; use rustc_target::abi::{TagEncoding, Variants}; +use tracing::{debug, instrument, trace}; use crate::cost_checker::CostChecker; -pub struct JumpThreading; +pub(super) struct JumpThreading; const MAX_BACKTRACK: usize = 5; const MAX_COST: usize = 100; const MAX_PLACES: usize = 100; -impl<'tcx> MirPass<'tcx> for JumpThreading { +impl<'tcx> crate::MirPass<'tcx> for JumpThreading { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() >= 2 } @@ -77,18 +78,16 @@ impl<'tcx> MirPass<'tcx> for JumpThreading { } let param_env = tcx.param_env_reveal_all_normalized(def_id); - let map = Map::new(tcx, body, Some(MAX_PLACES)); - let loop_headers = loop_headers(body); - let arena = DroplessArena::default(); + let arena = &DroplessArena::default(); let mut finder = TOFinder { tcx, param_env, ecx: InterpCx::new(tcx, DUMMY_SP, param_env, DummyMachine), body, - arena: &arena, - map: &map, - loop_headers: &loop_headers, + arena, + map: Map::new(tcx, body, Some(MAX_PLACES)), + loop_headers: loop_headers(body), opportunities: Vec::new(), }; @@ -104,7 +103,7 @@ impl<'tcx> MirPass<'tcx> for JumpThreading { // Verify that we do not thread through a loop header. for to in opportunities.iter() { - assert!(to.chain.iter().all(|&block| !loop_headers.contains(block))); + assert!(to.chain.iter().all(|&block| !finder.loop_headers.contains(block))); } OpportunitySet::new(body, opportunities).apply(body); } @@ -123,8 +122,8 @@ struct TOFinder<'tcx, 'a> { param_env: ty::ParamEnv<'tcx>, ecx: InterpCx<'tcx, DummyMachine>, body: &'a Body<'tcx>, - map: &'a Map<'tcx>, - loop_headers: &'a BitSet, + map: Map<'tcx>, + loop_headers: BitSet, /// We use an arena to avoid cloning the slices when cloning `state`. arena: &'a DroplessArena, opportunities: Vec, @@ -222,7 +221,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { })) }; let conds = ConditionSet(conds); - state.insert_value_idx(discr, conds, self.map); + state.insert_value_idx(discr, conds, &self.map); self.find_opportunity(bb, state, cost, 0); } @@ -263,7 +262,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { // _1 = 5 // Whatever happens here, it won't change the result of a `SwitchInt`. // _1 = 6 if let Some((lhs, tail)) = self.mutated_statement(stmt) { - state.flood_with_tail_elem(lhs.as_ref(), tail, self.map, ConditionSet::BOTTOM); + state.flood_with_tail_elem(lhs.as_ref(), tail, &self.map, ConditionSet::BOTTOM); } } @@ -369,7 +368,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target }) }; - if let Some(conditions) = state.try_get_idx(lhs, self.map) + if let Some(conditions) = state.try_get_idx(lhs, &self.map) && let Immediate::Scalar(Scalar::Int(int)) = *rhs { conditions.iter_matches(int).for_each(register_opportunity); @@ -405,7 +404,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { } }, &mut |place, op| { - if let Some(conditions) = state.try_get_idx(place, self.map) + if let Some(conditions) = state.try_get_idx(place, &self.map) && let Ok(imm) = self.ecx.read_immediate_raw(op) && let Some(imm) = imm.right() && let Immediate::Scalar(Scalar::Int(int)) = *imm @@ -440,7 +439,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { // Transfer the conditions on the copied rhs. Operand::Move(rhs) | Operand::Copy(rhs) => { let Some(rhs) = self.map.find(rhs.as_ref()) else { return }; - state.insert_place_idx(rhs, lhs, self.map); + state.insert_place_idx(rhs, lhs, &self.map); } } } @@ -460,7 +459,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { Rvalue::CopyForDeref(rhs) => self.process_operand(bb, lhs, &Operand::Copy(*rhs), state), Rvalue::Discriminant(rhs) => { let Some(rhs) = self.map.find_discr(rhs.as_ref()) else { return }; - state.insert_place_idx(rhs, lhs, self.map); + state.insert_place_idx(rhs, lhs, &self.map); } // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. Rvalue::Aggregate(box ref kind, ref operands) => { @@ -491,10 +490,10 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { } // Transfer the conditions on the copy rhs, after inversing polarity. Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => { - let Some(conditions) = state.try_get_idx(lhs, self.map) else { return }; + let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return }; let Some(place) = self.map.find(place.as_ref()) else { return }; let conds = conditions.map(self.arena, Condition::inv); - state.insert_value_idx(place, conds, self.map); + state.insert_value_idx(place, conds, &self.map); } // We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`. // Create a condition on `rhs ?= B`. @@ -503,7 +502,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { box (Operand::Move(place) | Operand::Copy(place), Operand::Constant(value)) | box (Operand::Constant(value), Operand::Move(place) | Operand::Copy(place)), ) => { - let Some(conditions) = state.try_get_idx(lhs, self.map) else { return }; + let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return }; let Some(place) = self.map.find(place.as_ref()) else { return }; let equals = match op { BinOp::Eq => ScalarInt::TRUE, @@ -527,7 +526,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { polarity: if c.matches(equals) { Polarity::Eq } else { Polarity::Ne }, ..c }); - state.insert_value_idx(place, conds, self.map); + state.insert_value_idx(place, conds, &self.map); } _ => {} @@ -582,7 +581,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume( Operand::Copy(place) | Operand::Move(place), )) => { - let Some(conditions) = state.try_get(place.as_ref(), self.map) else { return }; + let Some(conditions) = state.try_get(place.as_ref(), &self.map) else { return }; conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity); } StatementKind::Assign(box (lhs_place, rhs)) => { @@ -630,7 +629,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { // We can recurse through this terminator. let mut state = state(); if let Some(place_to_flood) = place_to_flood { - state.flood_with(place_to_flood.as_ref(), self.map, ConditionSet::BOTTOM); + state.flood_with(place_to_flood.as_ref(), &self.map, ConditionSet::BOTTOM); } self.find_opportunity(bb, state, cost.clone(), depth + 1); } @@ -649,7 +648,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { let Some(discr) = discr.place() else { return }; let discr_ty = discr.ty(self.body, self.tcx).ty; let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return }; - let Some(conditions) = state.try_get(discr.as_ref(), self.map) else { return }; + let Some(conditions) = state.try_get(discr.as_ref(), &self.map) else { return }; if let Some((value, _)) = targets.iter().find(|&(_, target)| target == target_bb) { let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return }; diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 3427b93c1f6fe..46b17c3c7e04d 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -1,8 +1,6 @@ -//! A lint that checks for known panics like -//! overflows, division by zero, -//! out-of-bound access etc. -//! Uses const propagation to determine the -//! values of operands during checks. +//! A lint that checks for known panics like overflows, division by zero, +//! out-of-bound access etc. Uses const propagation to determine the values of +//! operands during checks. use std::fmt::Debug; @@ -22,13 +20,13 @@ use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayo use rustc_middle::ty::{self, ConstInt, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::Span; use rustc_target::abi::{Abi, FieldIdx, HasDataLayout, Size, TargetDataLayout, VariantIdx}; +use tracing::{debug, instrument, trace}; use crate::errors::{AssertLint, AssertLintKind}; -use crate::MirLint; -pub struct KnownPanicsLint; +pub(super) struct KnownPanicsLint; -impl<'tcx> MirLint<'tcx> for KnownPanicsLint { +impl<'tcx> crate::MirLint<'tcx> for KnownPanicsLint { fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { if body.tainted_by_errors.is_some() { return; @@ -562,7 +560,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let val = self.use_ecx(|this| this.ecx.binary_op(bin_op, &left, &right))?; if matches!(val.layout.abi, Abi::ScalarPair(..)) { - // FIXME `Value` should properly support pairs in `Immediate`... but currently it does not. + // FIXME `Value` should properly support pairs in `Immediate`... but currently + // it does not. let (val, overflow) = val.to_pair(&self.ecx); Value::Aggregate { variant: VariantIdx::ZERO, @@ -852,7 +851,7 @@ const MAX_ALLOC_LIMIT: u64 = 1024; /// The mode that `ConstProp` is allowed to run in for a given `Local`. #[derive(Clone, Copy, Debug, PartialEq)] -pub enum ConstPropMode { +enum ConstPropMode { /// The `Local` can be propagated into and reads of this `Local` can also be propagated. FullConstProp, /// The `Local` can only be propagated into and from its own block. @@ -864,7 +863,7 @@ pub enum ConstPropMode { /// A visitor that determines locals in a MIR body /// that can be const propagated -pub struct CanConstProp { +struct CanConstProp { can_const_prop: IndexVec, // False at the beginning. Once set, no more assignments are allowed to that local. found_assignment: BitSet, @@ -872,7 +871,7 @@ pub struct CanConstProp { impl CanConstProp { /// Returns true if `local` can be propagated - pub fn check<'tcx>( + fn check<'tcx>( tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, body: &Body<'tcx>, @@ -913,7 +912,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp { fn visit_place(&mut self, place: &Place<'tcx>, mut context: PlaceContext, loc: Location) { use rustc_middle::mir::visit::PlaceContext::*; - // Dereferencing just read the addess of `place.local`. + // Dereferencing just read the address of `place.local`. if place.projection.first() == Some(&PlaceElem::Deref) { context = NonMutatingUse(NonMutatingUseContext::Copy); } diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index cbc3169f2f10a..3e263aa406774 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -16,28 +16,190 @@ use rustc_target::abi::{HasDataLayout, Size, TagEncoding, Variants}; /// Large([u32; 1024]), /// } /// ``` -/// Instead of emitting moves of the large variant, -/// Perform a memcpy instead. +/// Instead of emitting moves of the large variant, perform a memcpy instead. /// Based off of [this HackMD](https://hackmd.io/@ft4bxUsFT5CEUBmRKYHr7w/rJM8BBPzD). /// /// In summary, what this does is at runtime determine which enum variant is active, /// and instead of copying all the bytes of the largest possible variant, /// copy only the bytes for the currently active variant. -pub struct EnumSizeOpt { +pub(super) struct EnumSizeOpt { pub(crate) discrepancy: u64, } -impl<'tcx> MirPass<'tcx> for EnumSizeOpt { +impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { fn is_enabled(&self, sess: &Session) -> bool { // There are some differences in behavior on wasm and ARM that are not properly // understood, so we conservatively treat this optimization as unsound: // https://github.com/rust-lang/rust/pull/85158#issuecomment-1101836457 sess.opts.unstable_opts.unsound_mir_opts || sess.mir_opt_level() >= 3 } + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // NOTE: This pass may produce different MIR based on the alignment of the target // platform, but it will still be valid. - self.optim(tcx, body); + + let mut alloc_cache = FxHashMap::default(); + let body_did = body.source.def_id(); + let param_env = tcx.param_env_reveal_all_normalized(body_did); + + let blocks = body.basic_blocks.as_mut(); + let local_decls = &mut body.local_decls; + + for bb in blocks { + bb.expand_statements(|st| { + let StatementKind::Assign(box ( + lhs, + Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)), + )) = &st.kind + else { + return None; + }; + + let ty = lhs.ty(local_decls, tcx).ty; + + let (adt_def, num_variants, alloc_id) = + self.candidate(tcx, param_env, ty, &mut alloc_cache)?; + + let source_info = st.source_info; + let span = source_info.span; + + let tmp_ty = Ty::new_array(tcx, tcx.types.usize, num_variants as u64); + let size_array_local = local_decls.push(LocalDecl::new(tmp_ty, span)); + let store_live = + Statement { source_info, kind: StatementKind::StorageLive(size_array_local) }; + + let place = Place::from(size_array_local); + let constant_vals = ConstOperand { + span, + user_ty: None, + const_: Const::Val( + ConstValue::Indirect { alloc_id, offset: Size::ZERO }, + tmp_ty, + ), + }; + let rval = Rvalue::Use(Operand::Constant(Box::new(constant_vals))); + let const_assign = + Statement { source_info, kind: StatementKind::Assign(Box::new((place, rval))) }; + + let discr_place = Place::from( + local_decls.push(LocalDecl::new(adt_def.repr().discr_type().to_ty(tcx), span)), + ); + let store_discr = Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + discr_place, + Rvalue::Discriminant(*rhs), + ))), + }; + + let discr_cast_place = + Place::from(local_decls.push(LocalDecl::new(tcx.types.usize, span))); + let cast_discr = Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + discr_cast_place, + Rvalue::Cast( + CastKind::IntToInt, + Operand::Copy(discr_place), + tcx.types.usize, + ), + ))), + }; + + let size_place = + Place::from(local_decls.push(LocalDecl::new(tcx.types.usize, span))); + let store_size = Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + size_place, + Rvalue::Use(Operand::Copy(Place { + local: size_array_local, + projection: tcx + .mk_place_elems(&[PlaceElem::Index(discr_cast_place.local)]), + })), + ))), + }; + + let dst = + Place::from(local_decls.push(LocalDecl::new(Ty::new_mut_ptr(tcx, ty), span))); + let dst_ptr = Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + dst, + Rvalue::RawPtr(Mutability::Mut, *lhs), + ))), + }; + + let dst_cast_ty = Ty::new_mut_ptr(tcx, tcx.types.u8); + let dst_cast_place = + Place::from(local_decls.push(LocalDecl::new(dst_cast_ty, span))); + let dst_cast = Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + dst_cast_place, + Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(dst), dst_cast_ty), + ))), + }; + + let src = + Place::from(local_decls.push(LocalDecl::new(Ty::new_imm_ptr(tcx, ty), span))); + let src_ptr = Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + src, + Rvalue::RawPtr(Mutability::Not, *rhs), + ))), + }; + + let src_cast_ty = Ty::new_imm_ptr(tcx, tcx.types.u8); + let src_cast_place = + Place::from(local_decls.push(LocalDecl::new(src_cast_ty, span))); + let src_cast = Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + src_cast_place, + Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(src), src_cast_ty), + ))), + }; + + let deinit_old = + Statement { source_info, kind: StatementKind::Deinit(Box::new(dst)) }; + + let copy_bytes = Statement { + source_info, + kind: StatementKind::Intrinsic(Box::new( + NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + src: Operand::Copy(src_cast_place), + dst: Operand::Copy(dst_cast_place), + count: Operand::Copy(size_place), + }), + )), + }; + + let store_dead = + Statement { source_info, kind: StatementKind::StorageDead(size_array_local) }; + + let iter = [ + store_live, + const_assign, + store_discr, + cast_discr, + store_size, + dst_ptr, + dst_cast, + src_ptr, + src_cast, + deinit_old, + copy_bytes, + store_dead, + ] + .into_iter(); + + st.make_nop(); + + Some(iter) + }); + } } } @@ -82,6 +244,8 @@ impl EnumSizeOpt { let ptr_sized_int = data_layout.ptr_sized_integer(); let target_bytes = ptr_sized_int.size().bytes() as usize; let mut data = vec![0; target_bytes * num_discrs]; + + // We use a macro because `$bytes` can be u32 or u64. macro_rules! encode_store { ($curr_idx: expr, $endian: expr, $bytes: expr) => { let bytes = match $endian { @@ -116,184 +280,4 @@ impl EnumSizeOpt { let alloc = tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(alloc)); Some((*adt_def, num_discrs, *alloc_cache.entry(ty).or_insert(alloc))) } - fn optim<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let mut alloc_cache = FxHashMap::default(); - let body_did = body.source.def_id(); - let param_env = tcx.param_env_reveal_all_normalized(body_did); - - let blocks = body.basic_blocks.as_mut(); - let local_decls = &mut body.local_decls; - - for bb in blocks { - bb.expand_statements(|st| { - if let StatementKind::Assign(box ( - lhs, - Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)), - )) = &st.kind - { - let ty = lhs.ty(local_decls, tcx).ty; - - let source_info = st.source_info; - let span = source_info.span; - - let (adt_def, num_variants, alloc_id) = - self.candidate(tcx, param_env, ty, &mut alloc_cache)?; - - let tmp_ty = Ty::new_array(tcx, tcx.types.usize, num_variants as u64); - - let size_array_local = local_decls.push(LocalDecl::new(tmp_ty, span)); - let store_live = Statement { - source_info, - kind: StatementKind::StorageLive(size_array_local), - }; - - let place = Place::from(size_array_local); - let constant_vals = ConstOperand { - span, - user_ty: None, - const_: Const::Val( - ConstValue::Indirect { alloc_id, offset: Size::ZERO }, - tmp_ty, - ), - }; - let rval = Rvalue::Use(Operand::Constant(Box::new(constant_vals))); - - let const_assign = Statement { - source_info, - kind: StatementKind::Assign(Box::new((place, rval))), - }; - - let discr_place = Place::from( - local_decls - .push(LocalDecl::new(adt_def.repr().discr_type().to_ty(tcx), span)), - ); - - let store_discr = Statement { - source_info, - kind: StatementKind::Assign(Box::new(( - discr_place, - Rvalue::Discriminant(*rhs), - ))), - }; - - let discr_cast_place = - Place::from(local_decls.push(LocalDecl::new(tcx.types.usize, span))); - - let cast_discr = Statement { - source_info, - kind: StatementKind::Assign(Box::new(( - discr_cast_place, - Rvalue::Cast( - CastKind::IntToInt, - Operand::Copy(discr_place), - tcx.types.usize, - ), - ))), - }; - - let size_place = - Place::from(local_decls.push(LocalDecl::new(tcx.types.usize, span))); - - let store_size = Statement { - source_info, - kind: StatementKind::Assign(Box::new(( - size_place, - Rvalue::Use(Operand::Copy(Place { - local: size_array_local, - projection: tcx - .mk_place_elems(&[PlaceElem::Index(discr_cast_place.local)]), - })), - ))), - }; - - let dst = Place::from( - local_decls.push(LocalDecl::new(Ty::new_mut_ptr(tcx, ty), span)), - ); - - let dst_ptr = Statement { - source_info, - kind: StatementKind::Assign(Box::new(( - dst, - Rvalue::RawPtr(Mutability::Mut, *lhs), - ))), - }; - - let dst_cast_ty = Ty::new_mut_ptr(tcx, tcx.types.u8); - let dst_cast_place = - Place::from(local_decls.push(LocalDecl::new(dst_cast_ty, span))); - - let dst_cast = Statement { - source_info, - kind: StatementKind::Assign(Box::new(( - dst_cast_place, - Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(dst), dst_cast_ty), - ))), - }; - - let src = Place::from( - local_decls.push(LocalDecl::new(Ty::new_imm_ptr(tcx, ty), span)), - ); - - let src_ptr = Statement { - source_info, - kind: StatementKind::Assign(Box::new(( - src, - Rvalue::RawPtr(Mutability::Not, *rhs), - ))), - }; - - let src_cast_ty = Ty::new_imm_ptr(tcx, tcx.types.u8); - let src_cast_place = - Place::from(local_decls.push(LocalDecl::new(src_cast_ty, span))); - - let src_cast = Statement { - source_info, - kind: StatementKind::Assign(Box::new(( - src_cast_place, - Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(src), src_cast_ty), - ))), - }; - - let deinit_old = - Statement { source_info, kind: StatementKind::Deinit(Box::new(dst)) }; - - let copy_bytes = Statement { - source_info, - kind: StatementKind::Intrinsic(Box::new( - NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { - src: Operand::Copy(src_cast_place), - dst: Operand::Copy(dst_cast_place), - count: Operand::Copy(size_place), - }), - )), - }; - - let store_dead = Statement { - source_info, - kind: StatementKind::StorageDead(size_array_local), - }; - let iter = [ - store_live, - const_assign, - store_discr, - cast_discr, - store_size, - dst_ptr, - dst_cast, - src_ptr, - src_cast, - deinit_old, - copy_bytes, - store_dead, - ] - .into_iter(); - - st.make_nop(); - Some(iter) - } else { - None - } - }); - } - } } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 88094b44edf21..84d07d3833071 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -3,23 +3,20 @@ #![feature(box_patterns)] #![feature(const_type_name)] #![feature(cow_is_borrowed)] -#![feature(decl_macro)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(let_chains)] #![feature(map_try_insert)] #![feature(never_type)] -#![feature(option_get_or_insert_default)] #![feature(round_char_boundary)] #![feature(try_blocks)] #![feature(yeet_expr)] +#![warn(unreachable_pub)] // tidy-alphabetical-end -#[macro_use] -extern crate tracing; - use hir::ConstContext; use required_consts::RequiredConstsVisitor; +use rustc_const_eval::check_consts::{self, ConstCx}; use rustc_const_eval::util; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::steal::Steal; @@ -30,7 +27,7 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_index::IndexVec; use rustc_middle::mir::{ AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, LocalDecl, - MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo, + MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo, Statement, StatementKind, TerminatorKind, START_BLOCK, }; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; @@ -39,11 +36,12 @@ use rustc_middle::{bug, query, span_bug}; use rustc_span::source_map::Spanned; use rustc_span::{sym, DUMMY_SP}; use rustc_trait_selection::traits; +use tracing::{debug, trace}; #[macro_use] mod pass_manager; -use pass_manager::{self as pm, Lint, MirLint, WithMinOptLevel}; +use pass_manager::{self as pm, Lint, MirLint, MirPass, WithMinOptLevel}; mod abort_unwinding_calls; mod add_call_guards; @@ -75,6 +73,8 @@ mod errors; mod ffi_unwind_calls; mod function_item_references; mod gvn; +// Made public so that `mir_drops_elaborated_and_const_checked` can be overridden +// by custom rustc drivers, running all the steps by themselves. See #114628. pub mod inline; mod instsimplify; mod jump_threading; @@ -87,6 +87,7 @@ mod match_branches; mod mentioned_items; mod multiple_return_terminators; mod nrvo; +mod post_drop_elaboration; mod prettify; mod promote_consts; mod ref_prop; @@ -98,6 +99,7 @@ mod remove_unneeded_drops; mod remove_zsts; mod required_consts; mod reveal_all; +mod sanity_check; mod shim; mod ssa; // This pass is public to allow external drivers to perform MIR cleanup @@ -110,9 +112,6 @@ mod unreachable_enum_branching; mod unreachable_prop; mod validate; -use rustc_const_eval::check_consts::{self, ConstCx}; -use rustc_mir_dataflow::rustc_peek; - rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { @@ -130,7 +129,7 @@ pub fn provide(providers: &mut Providers) { mir_coroutine_witnesses: coroutine::mir_coroutine_witnesses, optimized_mir, is_mir_available, - is_ctfe_mir_available: |tcx, did| is_mir_available(tcx, did), + is_ctfe_mir_available: is_mir_available, mir_callgraph_reachable: inline::cycle::mir_callgraph_reachable, mir_inliner_callees: inline::cycle::mir_inliner_callees, promoted_mir, @@ -170,8 +169,9 @@ fn remap_mir_for_const_eval_select<'tcx>( let (method, place): (fn(Place<'tcx>) -> Operand<'tcx>, Place<'tcx>) = match tupled_args.node { Operand::Constant(_) => { - // there is no good way of extracting a tuple arg from a constant (const generic stuff) - // so we just create a temporary and deconstruct that. + // There is no good way of extracting a tuple arg from a constant + // (const generic stuff) so we just create a temporary and deconstruct + // that. let local = body.local_decls.push(LocalDecl::new(ty, fn_span)); bb.statements.push(Statement { source_info: SourceInfo::outermost(fn_span), @@ -223,17 +223,15 @@ fn is_mir_available(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { /// Finds the full set of `DefId`s within the current crate that have /// MIR associated with them. fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { - let mut set = FxIndexSet::default(); - // All body-owners have MIR associated with them. - set.extend(tcx.hir().body_owners()); + let set: FxIndexSet<_> = tcx.hir().body_owners().collect(); // Additionally, tuple struct/variant constructors have MIR, but // they don't have a BodyId, so we need to build them separately. - struct GatherCtors<'a> { - set: &'a mut FxIndexSet, + struct GatherCtors { + set: FxIndexSet, } - impl<'tcx> Visitor<'tcx> for GatherCtors<'_> { + impl<'tcx> Visitor<'tcx> for GatherCtors { fn visit_variant_data(&mut self, v: &'tcx hir::VariantData<'tcx>) { if let hir::VariantData::Tuple(_, _, def_id) = *v { self.set.insert(def_id); @@ -241,9 +239,11 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { intravisit::walk_struct_def(self, v) } } - tcx.hir().visit_all_item_likes_in_crate(&mut GatherCtors { set: &mut set }); - set + let mut gather_ctors = GatherCtors { set }; + tcx.hir().visit_all_item_likes_in_crate(&mut gather_ctors); + + gather_ctors.set } fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { @@ -251,8 +251,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { // No need to const-check a non-const `fn`. match const_kind { - Some(ConstContext::Const { .. } | ConstContext::Static(_)) - | Some(ConstContext::ConstFn) => {} + Some(ConstContext::Const { .. } | ConstContext::Static(_) | ConstContext::ConstFn) => {} None => span_bug!( tcx.def_span(def), "`mir_const_qualif` should only be called on const fns and const items" @@ -296,7 +295,7 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { &Lint(function_item_references::FunctionItemReferences), // What we need to do constant evaluation. &simplify::SimplifyCfg::Initial, - &rustc_peek::SanityCheck, // Just a lint + &Lint(sanity_check::SanityCheck), ], None, ); @@ -467,8 +466,8 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & tcx.alloc_steal_mir(body) } -// Made public such that `mir_drops_elaborated_and_const_checked` can be overridden -// by custom rustc drivers, running all the steps by themselves. +// Made public so that `mir_drops_elaborated_and_const_checked` can be overridden +// by custom rustc drivers, running all the steps by themselves. See #114628. pub fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { assert!(body.phase == MirPhase::Analysis(AnalysisPhase::Initial)); let did = body.source.def_id(); @@ -482,10 +481,13 @@ pub fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' pm::run_passes( tcx, body, - &[&remove_uninit_drops::RemoveUninitDrops, &simplify::SimplifyCfg::RemoveFalseEdges], + &[ + &remove_uninit_drops::RemoveUninitDrops, + &simplify::SimplifyCfg::RemoveFalseEdges, + &Lint(post_drop_elaboration::CheckLiveDrops), + ], None, ); - check_consts::post_drop_elaboration::check_live_drops(tcx, body); // FIXME: make this a MIR lint } debug!("runtime_mir_lowering({:?})", did); @@ -514,10 +516,12 @@ fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { /// Returns the sequence of passes that lowers analysis to runtime MIR. fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let passes: &[&dyn MirPass<'tcx>] = &[ - // These next passes must be executed together + // These next passes must be executed together. &add_call_guards::CriticalCallEdges, - &reveal_all::RevealAll, // has to be done before drop elaboration, since we need to drop opaque types, too. - &add_subtyping_projections::Subtyper, // calling this after reveal_all ensures that we don't deal with opaque types + // Must be done before drop elaboration because we need to drop opaque types, too. + &reveal_all::RevealAll, + // Calling this after reveal_all ensures that we don't deal with opaque types. + &add_subtyping_projections::Subtyper, &elaborate_drops::ElaborateDrops, // This will remove extraneous landing pads which are no longer // necessary as well as forcing any call in a non-unwinding @@ -526,8 +530,8 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // AddMovesForPackedDrops needs to run after drop // elaboration. &add_moves_for_packed_drops::AddMovesForPackedDrops, - // `AddRetag` needs to run after `ElaborateDrops` but before `ElaborateBoxDerefs`. Otherwise it should run fairly late, - // but before optimizations begin. + // `AddRetag` needs to run after `ElaborateDrops` but before `ElaborateBoxDerefs`. + // Otherwise it should run fairly late, but before optimizations begin. &add_retag::AddRetag, &elaborate_box_derefs::ElaborateBoxDerefs, &coroutine::StateTransform, @@ -568,13 +572,15 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // Before inlining: trim down MIR with passes to reduce inlining work. // Has to be done before inlining, otherwise actual call will be almost always inlined. - // Also simple, so can just do first + // Also simple, so can just do first. &lower_slice_len::LowerSliceLenCalls, - // Perform instsimplify before inline to eliminate some trivial calls (like clone shims). + // Perform instsimplify before inline to eliminate some trivial calls (like clone + // shims). &instsimplify::InstSimplify::BeforeInline, // Perform inlining, which may add a lot of code. &inline::Inline, - // Code from other crates may have storage markers, so this needs to happen after inlining. + // Code from other crates may have storage markers, so this needs to happen after + // inlining. &remove_storage_markers::RemoveStorageMarkers, // Inlining and instantiation may introduce ZST and useless drops. &remove_zsts::RemoveZsts, @@ -591,7 +597,8 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &match_branches::MatchBranchSimplification, // inst combine is after MatchBranchSimplification to clean up Ne(_1, false) &multiple_return_terminators::MultipleReturnTerminators, - // After simplifycfg, it allows us to discover new opportunities for peephole optimizations. + // After simplifycfg, it allows us to discover new opportunities for peephole + // optimizations. &instsimplify::InstSimplify::AfterSimplifyCfg, &simplify::SimplifyLocals::BeforeConstProp, &dead_store_elimination::DeadStoreElimination::Initial, diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index 746068064b8fb..23733994a8b4a 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -13,7 +13,7 @@ use rustc_mir_dataflow::impls::{MaybeStorageDead, MaybeStorageLive}; use rustc_mir_dataflow::storage::always_storage_live_locals; use rustc_mir_dataflow::{Analysis, ResultsCursor}; -pub fn lint_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, when: String) { +pub(super) fn lint_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, when: String) { let always_live_locals = &always_storage_live_locals(body); let maybe_storage_live = MaybeStorageLive::new(Cow::Borrowed(always_live_locals)) diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index a9bdff95fe5ac..6d635606687a9 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -7,9 +7,9 @@ use rustc_span::symbol::sym; use crate::take_array; -pub struct LowerIntrinsics; +pub(super) struct LowerIntrinsics; -impl<'tcx> MirPass<'tcx> for LowerIntrinsics { +impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let local_decls = &body.local_decls; for block in body.basic_blocks.as_mut() { @@ -35,20 +35,19 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { terminator.kind = TerminatorKind::Goto { target }; } sym::forget => { - if let Some(target) = *target { - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( - *destination, - Rvalue::Use(Operand::Constant(Box::new(ConstOperand { - span: terminator.source_info.span, - user_ty: None, - const_: Const::zero_sized(tcx.types.unit), - }))), - ))), - }); - terminator.kind = TerminatorKind::Goto { target }; - } + let target = target.unwrap(); + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::Use(Operand::Constant(Box::new(ConstOperand { + span: terminator.source_info.span, + user_ty: None, + const_: Const::zero_sized(tcx.types.unit), + }))), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; } sym::copy_nonoverlapping => { let target = target.unwrap(); @@ -121,43 +120,41 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { terminator.kind = TerminatorKind::Goto { target }; } sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { - if let Some(target) = *target { - let Ok([lhs, rhs]) = take_array(args) else { - bug!("Wrong arguments for {} intrinsic", intrinsic.name); - }; - let bin_op = match intrinsic.name { - sym::add_with_overflow => BinOp::AddWithOverflow, - sym::sub_with_overflow => BinOp::SubWithOverflow, - sym::mul_with_overflow => BinOp::MulWithOverflow, - _ => bug!("unexpected intrinsic"), - }; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( - *destination, - Rvalue::BinaryOp(bin_op, Box::new((lhs.node, rhs.node))), - ))), - }); - terminator.kind = TerminatorKind::Goto { target }; - } + let target = target.unwrap(); + let Ok([lhs, rhs]) = take_array(args) else { + bug!("Wrong arguments for {} intrinsic", intrinsic.name); + }; + let bin_op = match intrinsic.name { + sym::add_with_overflow => BinOp::AddWithOverflow, + sym::sub_with_overflow => BinOp::SubWithOverflow, + sym::mul_with_overflow => BinOp::MulWithOverflow, + _ => bug!("unexpected intrinsic"), + }; + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::BinaryOp(bin_op, Box::new((lhs.node, rhs.node))), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; } sym::size_of | sym::min_align_of => { - if let Some(target) = *target { - let tp_ty = generic_args.type_at(0); - let null_op = match intrinsic.name { - sym::size_of => NullOp::SizeOf, - sym::min_align_of => NullOp::AlignOf, - _ => bug!("unexpected intrinsic"), - }; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( - *destination, - Rvalue::NullaryOp(null_op, tp_ty), - ))), - }); - terminator.kind = TerminatorKind::Goto { target }; - } + let target = target.unwrap(); + let tp_ty = generic_args.type_at(0); + let null_op = match intrinsic.name { + sym::size_of => NullOp::SizeOf, + sym::min_align_of => NullOp::AlignOf, + _ => bug!("unexpected intrinsic"), + }; + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::NullaryOp(null_op, tp_ty), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; } sym::read_via_copy => { let Ok([arg]) = take_array(args) else { @@ -219,17 +216,23 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { terminator.kind = TerminatorKind::Goto { target }; } sym::discriminant_value => { - if let (Some(target), Some(arg)) = (*target, args[0].node.place()) { - let arg = tcx.mk_place_deref(arg); - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( - *destination, - Rvalue::Discriminant(arg), - ))), - }); - terminator.kind = TerminatorKind::Goto { target }; - } + let target = target.unwrap(); + let Ok([arg]) = take_array(args) else { + span_bug!( + terminator.source_info.span, + "Wrong arguments for discriminant_value intrinsic" + ); + }; + let arg = arg.node.place().unwrap(); + let arg = tcx.mk_place_deref(arg); + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::Discriminant(arg), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; } sym::offset => { let target = target.unwrap(); @@ -267,7 +270,6 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { Rvalue::Cast(CastKind::Transmute, arg.node, dst_ty), ))), }); - if let Some(target) = *target { terminator.kind = TerminatorKind::Goto { target }; } else { @@ -299,7 +301,6 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { Rvalue::Aggregate(Box::new(kind), fields.into()), ))), }); - terminator.kind = TerminatorKind::Goto { target }; } sym::ptr_metadata => { diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 77a7f4f47dd4e..420661f29c8cf 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -5,30 +5,26 @@ use rustc_hir::def_id::DefId; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -pub struct LowerSliceLenCalls; +pub(super) struct LowerSliceLenCalls; -impl<'tcx> MirPass<'tcx> for LowerSliceLenCalls { +impl<'tcx> crate::MirPass<'tcx> for LowerSliceLenCalls { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() > 0 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - lower_slice_len_calls(tcx, body) - } -} - -pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let language_items = tcx.lang_items(); - let Some(slice_len_fn_item_def_id) = language_items.slice_len_fn() else { - // there is no lang item to compare to :) - return; - }; + let language_items = tcx.lang_items(); + let Some(slice_len_fn_item_def_id) = language_items.slice_len_fn() else { + // there is no lang item to compare to :) + return; + }; - // The one successor remains unchanged, so no need to invalidate - let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); - for block in basic_blocks { - // lower `<[_]>::len` calls - lower_slice_len_call(block, slice_len_fn_item_def_id); + // The one successor remains unchanged, so no need to invalidate + let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); + for block in basic_blocks { + // lower `<[_]>::len` calls + lower_slice_len_call(block, slice_len_fn_item_def_id); + } } } diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 47758b56f8c90..0f981425cfd0d 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -10,9 +10,9 @@ use rustc_type_ir::TyKind::*; use super::simplify::simplify_cfg; -pub struct MatchBranchSimplification; +pub(super) struct MatchBranchSimplification; -impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { +impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() >= 1 } @@ -57,8 +57,9 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { } trait SimplifyMatch<'tcx> { - /// Simplifies a match statement, returning true if the simplification succeeds, false otherwise. - /// Generic code is written here, and we generally don't need a custom implementation. + /// Simplifies a match statement, returning true if the simplification succeeds, false + /// otherwise. Generic code is written here, and we generally don't need a custom + /// implementation. fn simplify( &mut self, tcx: TyCtxt<'tcx>, @@ -240,7 +241,8 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToIf { // 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. + // Different value between blocks. Make value conditional on switch + // condition. let size = tcx.layout_of(param_env.and(discr_ty)).unwrap().size; let const_cmp = Operand::const_from_scalar( tcx, @@ -289,7 +291,7 @@ fn can_cast( #[derive(Default)] struct SimplifyToExp { - transfrom_kinds: Vec, + transform_kinds: Vec, } #[derive(Clone, Copy)] @@ -302,17 +304,17 @@ enum ExpectedTransformKind<'tcx, 'a> { Cast { place: &'a Place<'tcx>, ty: Ty<'tcx> }, } -enum TransfromKind { +enum TransformKind { Same, Cast, } -impl From> for TransfromKind { +impl From> for TransformKind { fn from(compare_type: ExpectedTransformKind<'_, '_>) -> Self { match compare_type { - ExpectedTransformKind::Same(_) => TransfromKind::Same, - ExpectedTransformKind::SameByEq { .. } => TransfromKind::Same, - ExpectedTransformKind::Cast { .. } => TransfromKind::Cast, + ExpectedTransformKind::Same(_) => TransformKind::Same, + ExpectedTransformKind::SameByEq { .. } => TransformKind::Same, + ExpectedTransformKind::Cast { .. } => TransformKind::Cast, } } } @@ -394,14 +396,16 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { return None; } - // We first compare the two branches, and then the other branches need to fulfill the same conditions. + // 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. + // 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)))), @@ -475,7 +479,7 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { } } } - self.transfrom_kinds = expected_transform_kinds.into_iter().map(|c| c.into()).collect(); + self.transform_kinds = expected_transform_kinds.into_iter().map(|c| c.into()).collect(); Some(()) } @@ -493,13 +497,13 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { let (_, first) = targets.iter().next().unwrap(); let first = &bbs[first]; - for (t, s) in iter::zip(&self.transfrom_kinds, &first.statements) { + for (t, s) in iter::zip(&self.transform_kinds, &first.statements) { match (t, &s.kind) { - (TransfromKind::Same, _) => { + (TransformKind::Same, _) => { patch.add_statement(parent_end, s.kind.clone()); } ( - TransfromKind::Cast, + TransformKind::Cast, StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), ) => { let operand = Operand::Copy(Place::from(discr_local)); diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index 32c8064ebca50..f24de609e6b29 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -1,19 +1,19 @@ use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{self, Location, MentionedItem, MirPass}; +use rustc_middle::mir::{self, Location, MentionedItem}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::Session; use rustc_span::source_map::Spanned; -pub struct MentionedItems; +pub(super) struct MentionedItems; struct MentionedItemsVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, - mentioned_items: &'a mut Vec>>, + mentioned_items: Vec>>, } -impl<'tcx> MirPass<'tcx> for MentionedItems { +impl<'tcx> crate::MirPass<'tcx> for MentionedItems { fn is_enabled(&self, _sess: &Session) -> bool { // If this pass is skipped the collector assume that nothing got mentioned! We could // potentially skip it in opt-level 0 if we are sure that opt-level will never *remove* uses @@ -23,9 +23,9 @@ impl<'tcx> MirPass<'tcx> for MentionedItems { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { - let mut mentioned_items = Vec::new(); - MentionedItemsVisitor { tcx, body, mentioned_items: &mut mentioned_items }.visit_body(body); - body.set_mentioned_items(mentioned_items); + let mut visitor = MentionedItemsVisitor { tcx, body, mentioned_items: Vec::new() }; + visitor.visit_body(body); + body.set_mentioned_items(visitor.mentioned_items); } } @@ -82,7 +82,9 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { source_ty.builtin_deref(true).map(|t| t.kind()), target_ty.builtin_deref(true).map(|t| t.kind()), ) { - (Some(ty::Array(..)), Some(ty::Str | ty::Slice(..))) => false, // &str/&[T] unsizing + // &str/&[T] unsizing + (Some(ty::Array(..)), Some(ty::Str | ty::Slice(..))) => false, + _ => true, }; if may_involve_vtable { diff --git a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs index 1e87a0e01d9be..b6d6ef5de1da9 100644 --- a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs +++ b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs @@ -7,9 +7,9 @@ use rustc_middle::ty::TyCtxt; use crate::simplify; -pub struct MultipleReturnTerminators; +pub(super) struct MultipleReturnTerminators; -impl<'tcx> MirPass<'tcx> for MultipleReturnTerminators { +impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() >= 4 } diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs index 885dbd5f33934..98fa149e2bc71 100644 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ b/compiler/rustc_mir_transform/src/nrvo.rs @@ -6,8 +6,7 @@ use rustc_middle::bug; use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, BasicBlock, Local, Location}; use rustc_middle::ty::TyCtxt; - -use crate::MirPass; +use tracing::{debug, trace}; /// This pass looks for MIR that always copies the same local into the return place and eliminates /// the copy by renaming all uses of that local to `_0`. @@ -31,9 +30,9 @@ use crate::MirPass; /// /// [#47954]: https://github.com/rust-lang/rust/pull/47954 /// [#71003]: https://github.com/rust-lang/rust/pull/71003 -pub struct RenameReturnPlace; +pub(super) struct RenameReturnPlace; -impl<'tcx> MirPass<'tcx> for RenameReturnPlace { +impl<'tcx> crate::MirPass<'tcx> for RenameReturnPlace { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { // unsound: #111005 sess.mir_opt_level() > 0 && sess.opts.unstable_opts.unsound_mir_opts diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index d092477e340a4..60ece5e7db97d 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -1,18 +1,99 @@ +use std::cell::RefCell; +use std::collections::hash_map::Entry; + +use rustc_data_structures::fx::FxHashMap; use rustc_middle::mir::{self, Body, MirPhase, RuntimePhase}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; +use tracing::trace; use crate::lint::lint_body; -use crate::{validate, MirPass}; +use crate::validate; + +thread_local! { + static PASS_NAMES: RefCell> = { + RefCell::new(FxHashMap::default()) + }; +} + +/// Converts a MIR pass name into a snake case form to match the profiling naming style. +fn to_profiler_name(type_name: &'static str) -> &'static str { + PASS_NAMES.with(|names| match names.borrow_mut().entry(type_name) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => { + let snake_case: String = type_name + .chars() + .flat_map(|c| { + if c.is_ascii_uppercase() { + vec!['_', c.to_ascii_lowercase()] + } else if c == '-' { + vec!['_'] + } else { + vec![c] + } + }) + .collect(); + let result = &*String::leak(format!("mir_pass{}", snake_case)); + e.insert(result); + result + } + }) +} + +// const wrapper for `if let Some((_, tail)) = name.rsplit_once(':') { tail } else { name }` +const fn c_name(name: &'static str) -> &'static str { + // FIXME Simplify the implementation once more `str` methods get const-stable. + // and inline into call site + let bytes = name.as_bytes(); + let mut i = bytes.len(); + while i > 0 && bytes[i - 1] != b':' { + i = i - 1; + } + let (_, bytes) = bytes.split_at(i); + match std::str::from_utf8(bytes) { + Ok(name) => name, + Err(_) => name, + } +} -/// Just like `MirPass`, except it cannot mutate `Body`. -pub trait MirLint<'tcx> { +/// A streamlined trait that you can implement to create a pass; the +/// pass will be named after the type, and it will consist of a main +/// loop that goes over each available MIR and applies `run_pass`. +pub(super) trait MirPass<'tcx> { + fn name(&self) -> &'static str { + // FIXME Simplify the implementation once more `str` methods get const-stable. + // See copypaste in `MirLint` + const { + let name = std::any::type_name::(); + c_name(name) + } + } + + fn profiler_name(&self) -> &'static str { + to_profiler_name(self.name()) + } + + /// Returns `true` if this pass is enabled with the current combination of compiler flags. + fn is_enabled(&self, _sess: &Session) -> bool { + true + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>); + + fn is_mir_dump_enabled(&self) -> bool { + true + } +} + +/// Just like `MirPass`, except it cannot mutate `Body`, and MIR dumping is +/// disabled (via the `Lint` adapter). +pub(super) trait MirLint<'tcx> { fn name(&self) -> &'static str { // FIXME Simplify the implementation once more `str` methods get const-stable. // See copypaste in `MirPass` const { let name = std::any::type_name::(); - rustc_middle::util::common::c_name(name) + c_name(name) } } @@ -25,7 +106,7 @@ pub trait MirLint<'tcx> { /// An adapter for `MirLint`s that implements `MirPass`. #[derive(Debug, Clone)] -pub struct Lint(pub T); +pub(super) struct Lint(pub T); impl<'tcx, T> MirPass<'tcx> for Lint where @@ -48,7 +129,7 @@ where } } -pub struct WithMinOptLevel(pub u32, pub T); +pub(super) struct WithMinOptLevel(pub u32, pub T); impl<'tcx, T> MirPass<'tcx> for WithMinOptLevel where @@ -69,7 +150,7 @@ where /// Run the sequence of passes without validating the MIR after each pass. The MIR is still /// validated at the end. -pub fn run_passes_no_validate<'tcx>( +pub(super) fn run_passes_no_validate<'tcx>( tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>], @@ -79,7 +160,7 @@ pub fn run_passes_no_validate<'tcx>( } /// The optional `phase_change` is applied after executing all the passes, if present -pub fn run_passes<'tcx>( +pub(super) fn run_passes<'tcx>( tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>], @@ -88,7 +169,7 @@ pub fn run_passes<'tcx>( run_passes_inner(tcx, body, passes, phase_change, true); } -pub fn should_run_pass<'tcx, P>(tcx: TyCtxt<'tcx>, pass: &P) -> bool +pub(super) fn should_run_pass<'tcx, P>(tcx: TyCtxt<'tcx>, pass: &P) -> bool where P: MirPass<'tcx> + ?Sized, { @@ -184,16 +265,11 @@ fn run_passes_inner<'tcx>( } } -pub fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) { +pub(super) fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) { validate::Validator { when, mir_phase: body.phase }.run_pass(tcx, body); } -pub fn dump_mir_for_pass<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - pass_name: &str, - is_after: bool, -) { +fn dump_mir_for_pass<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, pass_name: &str, is_after: bool) { mir::dump_mir( tcx, true, @@ -204,7 +280,7 @@ pub fn dump_mir_for_pass<'tcx>( ); } -pub fn dump_mir_for_phase_change<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { +pub(super) fn dump_mir_for_phase_change<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { assert_eq!(body.pass_count, 0); mir::dump_mir(tcx, true, body.phase.name(), &"after", body, |_, _| Ok(())) } diff --git a/compiler/rustc_mir_transform/src/post_drop_elaboration.rs b/compiler/rustc_mir_transform/src/post_drop_elaboration.rs new file mode 100644 index 0000000000000..75721d4607626 --- /dev/null +++ b/compiler/rustc_mir_transform/src/post_drop_elaboration.rs @@ -0,0 +1,13 @@ +use rustc_const_eval::check_consts; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; + +use crate::MirLint; + +pub(super) struct CheckLiveDrops; + +impl<'tcx> MirLint<'tcx> for CheckLiveDrops { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + check_consts::post_drop_elaboration::check_live_drops(tcx, body); + } +} diff --git a/compiler/rustc_mir_transform/src/prettify.rs b/compiler/rustc_mir_transform/src/prettify.rs index 14dd0c6f61e76..937c207776b35 100644 --- a/compiler/rustc_mir_transform/src/prettify.rs +++ b/compiler/rustc_mir_transform/src/prettify.rs @@ -15,9 +15,9 @@ use rustc_session::Session; /// /// Thus after this pass, all the successors of a block are later than it in the /// `IndexVec`, unless that successor is a back-edge (such as from a loop). -pub struct ReorderBasicBlocks; +pub(super) struct ReorderBasicBlocks; -impl<'tcx> MirPass<'tcx> for ReorderBasicBlocks { +impl<'tcx> crate::MirPass<'tcx> for ReorderBasicBlocks { fn is_enabled(&self, _session: &Session) -> bool { false } @@ -43,9 +43,9 @@ impl<'tcx> MirPass<'tcx> for ReorderBasicBlocks { /// assigned or referenced will have a smaller number. /// /// (Does not reorder arguments nor the [`RETURN_PLACE`].) -pub struct ReorderLocals; +pub(super) struct ReorderLocals; -impl<'tcx> MirPass<'tcx> for ReorderLocals { +impl<'tcx> crate::MirPass<'tcx> for ReorderLocals { fn is_enabled(&self, _session: &Session) -> bool { false } @@ -63,7 +63,7 @@ impl<'tcx> MirPass<'tcx> for ReorderLocals { finder.visit_basic_block_data(bb, bbd); } - // track everything in case there are some locals that we never saw, + // Track everything in case there are some locals that we never saw, // such as in non-block things like debug info or in non-uses. for local in body.local_decls.indices() { finder.track(local); @@ -87,7 +87,7 @@ impl<'tcx> MirPass<'tcx> for ReorderLocals { fn permute(data: &mut IndexVec, map: &IndexSlice) { // FIXME: It would be nice to have a less-awkward way to apply permutations, - // but I don't know one that exists. `sort_by_cached_key` has logic for it + // but I don't know one that exists. `sort_by_cached_key` has logic for it // internally, but not in a way that we're allowed to use here. let mut enumerated: Vec<_> = std::mem::take(data).into_iter_enumerated().collect(); enumerated.sort_by_key(|p| map[p.0]); @@ -135,8 +135,8 @@ impl<'tcx> Visitor<'tcx> for LocalFinder { } struct LocalUpdater<'tcx> { - pub map: IndexVec, - pub tcx: TyCtxt<'tcx>, + map: IndexVec, + tcx: TyCtxt<'tcx>, } impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 6e84914ef972c..59df99f858dcf 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -1,16 +1,14 @@ //! A pass that promotes borrows of constant rvalues. //! -//! The rvalues considered constant are trees of temps, -//! each with exactly one initialization, and holding -//! a constant value with no interior mutability. -//! They are placed into a new MIR constant body in -//! `promoted` and the borrow rvalue is replaced with -//! a `Literal::Promoted` using the index into `promoted` -//! of that constant MIR. +//! The rvalues considered constant are trees of temps, each with exactly one +//! initialization, and holding a constant value with no interior mutability. +//! They are placed into a new MIR constant body in `promoted` and the borrow +//! rvalue is replaced with a `Literal::Promoted` using the index into +//! `promoted` of that constant MIR. //! -//! This pass assumes that every use is dominated by an -//! initialization and can otherwise silence errors, if -//! move analysis runs after promotion on broken MIR. +//! This pass assumes that every use is dominated by an initialization and can +//! otherwise silence errors, if move analysis runs after promotion on broken +//! MIR. use std::assert_matches::assert_matches; use std::cell::Cell; @@ -27,6 +25,7 @@ use rustc_middle::ty::{self, GenericArgs, List, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::source_map::Spanned; use rustc_span::Span; +use tracing::{debug, instrument}; /// A `MirPass` for promotion. /// @@ -36,11 +35,12 @@ use rustc_span::Span; /// After this pass is run, `promoted_fragments` will hold the MIR body corresponding to each /// newly created `Constant`. #[derive(Default)] -pub struct PromoteTemps<'tcx> { +pub(super) struct PromoteTemps<'tcx> { + // Must use `Cell` because `run_pass` takes `&self`, not `&mut self`. pub promoted_fragments: Cell>>, } -impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { +impl<'tcx> crate::MirPass<'tcx> for PromoteTemps<'tcx> { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // There's not really any point in promoting errorful MIR. // @@ -385,7 +385,8 @@ impl<'tcx> Validator<'_, 'tcx> { fn validate_ref(&mut self, kind: BorrowKind, place: &Place<'tcx>) -> Result<(), Unpromotable> { match kind { // Reject these borrow types just to be safe. - // FIXME(RalfJung): could we allow them? Should we? No point in it until we have a usecase. + // FIXME(RalfJung): could we allow them? Should we? No point in it until we have a + // usecase. BorrowKind::Fake(_) | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => { return Err(Unpromotable); } @@ -467,7 +468,8 @@ impl<'tcx> Validator<'_, 'tcx> { let lhs_ty = lhs.ty(self.body, self.tcx); if let ty::RawPtr(_, _) | ty::FnPtr(..) = lhs_ty.kind() { - // Raw and fn pointer operations are not allowed inside consts and thus not promotable. + // Raw and fn pointer operations are not allowed inside consts and thus not + // promotable. assert_matches!( op, BinOp::Eq @@ -497,7 +499,8 @@ impl<'tcx> Validator<'_, 'tcx> { Some(x) if x != 0 => {} // okay _ => return Err(Unpromotable), // value not known or 0 -- not okay } - // Furthermore, for signed divison, we also have to exclude `int::MIN / -1`. + // Furthermore, for signed division, we also have to exclude `int::MIN / + // -1`. if lhs_ty.is_signed() { match rhs_val.map(|x| x.to_int(sz)) { Some(-1) | None => { @@ -511,8 +514,11 @@ impl<'tcx> Validator<'_, 'tcx> { }; let lhs_min = sz.signed_int_min(); match lhs_val.map(|x| x.to_int(sz)) { - Some(x) if x != lhs_min => {} // okay - _ => return Err(Unpromotable), // value not known or int::MIN -- not okay + // okay + Some(x) if x != lhs_min => {} + + // value not known or int::MIN -- not okay + _ => return Err(Unpromotable), } } _ => {} @@ -814,8 +820,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { TerminatorKind::Call { mut func, mut args, call_source: desugar, fn_span, .. } => { - // This promoted involves a function call, so it may fail to evaluate. - // Let's make sure it is added to `required_consts` so that failure cannot get lost. + // This promoted involves a function call, so it may fail to evaluate. Let's + // make sure it is added to `required_consts` so that failure cannot get lost. self.add_to_required = true; self.visit_operand(&mut func, loc); diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index 973a191d786e1..4c3a99b79d4f2 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::impls::MaybeStorageDead; use rustc_mir_dataflow::storage::always_storage_live_locals; use rustc_mir_dataflow::Analysis; +use tracing::{debug, instrument}; use crate::ssa::{SsaLocals, StorageLiveLocals}; @@ -69,9 +70,9 @@ use crate::ssa::{SsaLocals, StorageLiveLocals}; /// /// For immutable borrows, we do not need to preserve such uniqueness property, /// so we perform all the possible instantiations without removing the `_1 = &_2` statement. -pub struct ReferencePropagation; +pub(super) struct ReferencePropagation; -impl<'tcx> MirPass<'tcx> for ReferencePropagation { +impl<'tcx> crate::MirPass<'tcx> for ReferencePropagation { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() >= 2 } @@ -252,11 +253,8 @@ fn compute_replacement<'tcx>( debug!(?targets); - let mut finder = ReplacementFinder { - targets: &mut targets, - can_perform_opt, - allowed_replacements: FxHashSet::default(), - }; + let mut finder = + ReplacementFinder { targets, can_perform_opt, allowed_replacements: FxHashSet::default() }; let reachable_blocks = traversal::reachable_as_bitset(body); for (bb, bbdata) in body.basic_blocks.iter_enumerated() { // Only visit reachable blocks as we rely on dataflow. @@ -268,19 +266,19 @@ fn compute_replacement<'tcx>( let allowed_replacements = finder.allowed_replacements; return Replacer { tcx, - targets, + targets: finder.targets, storage_to_remove, allowed_replacements, any_replacement: false, }; - struct ReplacementFinder<'a, 'tcx, F> { - targets: &'a mut IndexVec>, + struct ReplacementFinder<'tcx, F> { + targets: IndexVec>, can_perform_opt: F, allowed_replacements: FxHashSet<(Local, Location)>, } - impl<'tcx, F> Visitor<'tcx> for ReplacementFinder<'_, 'tcx, F> + impl<'tcx, F> Visitor<'tcx> for ReplacementFinder<'tcx, F> where F: FnMut(Place<'tcx>, Location) -> bool, { @@ -344,7 +342,7 @@ fn fully_replacable_locals(ssa: &SsaLocals) -> BitSet { replacable } -/// Utility to help performing subtitution of `*pattern` by `target`. +/// Utility to help performing substitution of `*pattern` by `target`. struct Replacer<'tcx> { tcx: TyCtxt<'tcx>, targets: IndexVec>, diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 1df5737e85974..55394e93a5cb7 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -3,13 +3,14 @@ use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_target::spec::PanicStrategy; +use tracing::debug; /// A pass that removes noop landing pads and replaces jumps to them with /// `UnwindAction::Continue`. This is important because otherwise LLVM generates /// terrible code for these. -pub struct RemoveNoopLandingPads; +pub(super) struct RemoveNoopLandingPads; -impl<'tcx> MirPass<'tcx> for RemoveNoopLandingPads { +impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.panic_strategy() != PanicStrategy::Abort } @@ -17,7 +18,61 @@ impl<'tcx> MirPass<'tcx> for RemoveNoopLandingPads { fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let def_id = body.source.def_id(); debug!(?def_id); - self.remove_nop_landing_pads(body) + + // Skip the pass if there are no blocks with a resume terminator. + let has_resume = body + .basic_blocks + .iter_enumerated() + .any(|(_bb, block)| matches!(block.terminator().kind, TerminatorKind::UnwindResume)); + if !has_resume { + debug!("remove_noop_landing_pads: no resume block in MIR"); + return; + } + + // make sure there's a resume block without any statements + let resume_block = { + let mut patch = MirPatch::new(body); + let resume_block = patch.resume_block(); + patch.apply(body); + resume_block + }; + debug!("remove_noop_landing_pads: resume block is {:?}", resume_block); + + let mut jumps_folded = 0; + let mut landing_pads_removed = 0; + let mut nop_landing_pads = BitSet::new_empty(body.basic_blocks.len()); + + // This is a post-order traversal, so that if A post-dominates B + // then A will be visited before B. + let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect(); + for bb in postorder { + debug!(" processing {:?}", bb); + if let Some(unwind) = body[bb].terminator_mut().unwind_mut() { + if let UnwindAction::Cleanup(unwind_bb) = *unwind { + if nop_landing_pads.contains(unwind_bb) { + debug!(" removing noop landing pad"); + landing_pads_removed += 1; + *unwind = UnwindAction::Continue; + } + } + } + + for target in body[bb].terminator_mut().successors_mut() { + if *target != resume_block && nop_landing_pads.contains(*target) { + debug!(" folding noop jump to {:?} to resume block", target); + *target = resume_block; + jumps_folded += 1; + } + } + + let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); + if is_nop_landing_pad { + nop_landing_pads.insert(bb); + } + debug!(" is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad); + } + + debug!("removed {:?} jumps and {:?} landing pads", jumps_folded, landing_pads_removed); } } @@ -81,61 +136,4 @@ impl RemoveNoopLandingPads { | TerminatorKind::InlineAsm { .. } => false, } } - - fn remove_nop_landing_pads(&self, body: &mut Body<'_>) { - // Skip the pass if there are no blocks with a resume terminator. - let has_resume = body - .basic_blocks - .iter_enumerated() - .any(|(_bb, block)| matches!(block.terminator().kind, TerminatorKind::UnwindResume)); - if !has_resume { - debug!("remove_noop_landing_pads: no resume block in MIR"); - return; - } - - // make sure there's a resume block without any statements - let resume_block = { - let mut patch = MirPatch::new(body); - let resume_block = patch.resume_block(); - patch.apply(body); - resume_block - }; - debug!("remove_noop_landing_pads: resume block is {:?}", resume_block); - - let mut jumps_folded = 0; - let mut landing_pads_removed = 0; - let mut nop_landing_pads = BitSet::new_empty(body.basic_blocks.len()); - - // This is a post-order traversal, so that if A post-dominates B - // then A will be visited before B. - let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect(); - for bb in postorder { - debug!(" processing {:?}", bb); - if let Some(unwind) = body[bb].terminator_mut().unwind_mut() { - if let UnwindAction::Cleanup(unwind_bb) = *unwind { - if nop_landing_pads.contains(unwind_bb) { - debug!(" removing noop landing pad"); - landing_pads_removed += 1; - *unwind = UnwindAction::Continue; - } - } - } - - for target in body[bb].terminator_mut().successors_mut() { - if *target != resume_block && nop_landing_pads.contains(*target) { - debug!(" folding noop jump to {:?} to resume block", target); - *target = resume_block; - jumps_folded += 1; - } - } - - let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); - if is_nop_landing_pad { - nop_landing_pads.insert(bb); - } - debug!(" is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad); - } - - debug!("removed {:?} jumps and {:?} landing pads", jumps_folded, landing_pads_removed); - } } diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs index 78335b3b5e063..71399eb72f004 100644 --- a/compiler/rustc_mir_transform/src/remove_place_mention.rs +++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs @@ -2,10 +2,11 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use tracing::trace; -pub struct RemovePlaceMention; +pub(super) struct RemovePlaceMention; -impl<'tcx> MirPass<'tcx> for RemovePlaceMention { +impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { !sess.opts.unstable_opts.mir_keep_place_mention } diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs index f68e592db154f..3ecb4a8994f5f 100644 --- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs +++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs @@ -2,10 +2,11 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use tracing::trace; -pub struct RemoveStorageMarkers; +pub(super) struct RemoveStorageMarkers; -impl<'tcx> MirPass<'tcx> for RemoveStorageMarkers { +impl<'tcx> crate::MirPass<'tcx> for RemoveStorageMarkers { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() > 0 && !sess.emit_lifetime_markers() } diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index fae1cb5f7d8a5..d80a4edecdf37 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -6,8 +6,6 @@ use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; use rustc_mir_dataflow::{move_path_children_matching, Analysis, MaybeReachable}; use rustc_target::abi::FieldIdx; -use crate::MirPass; - /// Removes `Drop` terminators whose target is known to be uninitialized at /// that point. /// @@ -16,9 +14,9 @@ use crate::MirPass; /// like [#90770]. /// /// [#90770]: https://github.com/rust-lang/rust/issues/90770 -pub struct RemoveUninitDrops; +pub(super) struct RemoveUninitDrops; -impl<'tcx> MirPass<'tcx> for RemoveUninitDrops { +impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let param_env = tcx.param_env(body.source.def_id()); let move_data = @@ -108,8 +106,9 @@ fn is_needs_drop_and_init<'tcx>( // If its projection *is* present in `MoveData`, then the field may have been moved // from separate from its parent. Recurse. adt.variants().iter_enumerated().any(|(vid, variant)| { - // Enums have multiple variants, which are discriminated with a `Downcast` projection. - // Structs have a single variant, and don't use a `Downcast` projection. + // Enums have multiple variants, which are discriminated with a `Downcast` + // projection. Structs have a single variant, and don't use a `Downcast` + // projection. let mpi = if adt.is_enum() { let downcast = move_path_children_matching(move_data, mpi, |x| x.is_downcast_to(vid)); diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index 2778d91e17b99..28925ba1beb13 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -6,12 +6,13 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use tracing::{debug, trace}; use super::simplify::simplify_cfg; -pub struct RemoveUnneededDrops; +pub(super) struct RemoveUnneededDrops; -impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops { +impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("Running RemoveUnneededDrops on {:?}", body.source); diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 9a94cae338288..f13bb1c5993d5 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -4,9 +4,9 @@ use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; -pub struct RemoveZsts; +pub(super) struct RemoveZsts; -impl<'tcx> MirPass<'tcx> for RemoveZsts { +impl<'tcx> crate::MirPass<'tcx> for RemoveZsts { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() > 0 } diff --git a/compiler/rustc_mir_transform/src/required_consts.rs b/compiler/rustc_mir_transform/src/required_consts.rs index 50637e2ac03bb..99d1cd6f63ecf 100644 --- a/compiler/rustc_mir_transform/src/required_consts.rs +++ b/compiler/rustc_mir_transform/src/required_consts.rs @@ -1,26 +1,21 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{traversal, Body, ConstOperand, Location}; -pub struct RequiredConstsVisitor<'a, 'tcx> { - required_consts: &'a mut Vec>, +pub(super) struct RequiredConstsVisitor<'tcx> { + required_consts: Vec>, } -impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> { - fn new(required_consts: &'a mut Vec>) -> Self { - RequiredConstsVisitor { required_consts } - } - - pub fn compute_required_consts(body: &mut Body<'tcx>) { - let mut required_consts = Vec::new(); - let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts); +impl<'tcx> RequiredConstsVisitor<'tcx> { + pub(super) fn compute_required_consts(body: &mut Body<'tcx>) { + let mut visitor = RequiredConstsVisitor { required_consts: Vec::new() }; for (bb, bb_data) in traversal::reverse_postorder(&body) { - required_consts_visitor.visit_basic_block_data(bb, bb_data); + visitor.visit_basic_block_data(bb, bb_data); } - body.set_required_consts(required_consts); + body.set_required_consts(visitor.required_consts); } } -impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> { +impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'tcx> { fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _: Location) { if constant.const_.is_required_const() { self.required_consts.push(*constant); diff --git a/compiler/rustc_mir_transform/src/reveal_all.rs b/compiler/rustc_mir_transform/src/reveal_all.rs index 5eaa024f84689..f3b2f78b31c3a 100644 --- a/compiler/rustc_mir_transform/src/reveal_all.rs +++ b/compiler/rustc_mir_transform/src/reveal_all.rs @@ -4,9 +4,9 @@ use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; -pub struct RevealAll; +pub(super) struct RevealAll; -impl<'tcx> MirPass<'tcx> for RevealAll { +impl<'tcx> crate::MirPass<'tcx> for RevealAll { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); RevealAllVisitor { tcx, param_env }.visit_body_preserves_cfg(body); @@ -35,9 +35,9 @@ impl<'tcx> MutVisitor<'tcx> for RevealAllVisitor<'tcx> { if place.projection.iter().all(|elem| !matches!(elem, ProjectionElem::OpaqueCast(_))) { return; } - // `OpaqueCast` projections are only needed if there are opaque types on which projections are performed. - // After the `RevealAll` pass, all opaque types are replaced with their hidden types, so we don't need these - // projections anymore. + // `OpaqueCast` projections are only needed if there are opaque types on which projections + // are performed. After the `RevealAll` pass, all opaque types are replaced with their + // hidden types, so we don't need these projections anymore. place.projection = self.tcx.mk_place_elems( &place .projection diff --git a/compiler/rustc_mir_transform/src/sanity_check.rs b/compiler/rustc_mir_transform/src/sanity_check.rs new file mode 100644 index 0000000000000..c9445d1816295 --- /dev/null +++ b/compiler/rustc_mir_transform/src/sanity_check.rs @@ -0,0 +1,11 @@ +use rustc_middle::mir::Body; +use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::rustc_peek::sanity_check; + +pub(super) struct SanityCheck; + +impl<'tcx> crate::MirLint<'tcx> for SanityCheck { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + sanity_check(tcx, body); + } +} diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 86eada0183f87..f1bd803d83530 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -17,6 +17,7 @@ use rustc_span::source_map::Spanned; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; use rustc_target::spec::abi::Abi; +use tracing::{debug, instrument}; use crate::{ abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, @@ -25,7 +26,7 @@ use crate::{ mod async_destructor_ctor; -pub fn provide(providers: &mut Providers) { +pub(super) fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; } @@ -330,7 +331,7 @@ fn new_body<'tcx>( body } -pub struct DropShimElaborator<'a, 'tcx> { +pub(super) struct DropShimElaborator<'a, 'tcx> { pub body: &'a Body<'tcx>, pub patch: MirPatch<'tcx>, pub tcx: TyCtxt<'tcx>, @@ -403,8 +404,7 @@ fn build_thread_local_shim<'tcx>( let span = tcx.def_span(def_id); let source_info = SourceInfo::outermost(span); - let mut blocks = IndexVec::with_capacity(1); - blocks.push(BasicBlockData { + let blocks = IndexVec::from_raw(vec![BasicBlockData { statements: vec![Statement { source_info, kind: StatementKind::Assign(Box::new(( @@ -414,7 +414,7 @@ fn build_thread_local_shim<'tcx>( }], terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), is_cleanup: false, - }); + }]); new_body( MirSource::from_instance(instance), @@ -912,7 +912,7 @@ fn build_call_shim<'tcx>( body } -pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { +pub(super) fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { debug_assert!(tcx.is_constructor(ctor_id)); let param_env = tcx.param_env_reveal_all_normalized(ctor_id); @@ -1002,7 +1002,8 @@ fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'t let locals = local_decls_for_sig(&sig, span); let source_info = SourceInfo::outermost(span); - // FIXME: use `expose_provenance` once we figure out whether function pointers have meaningful provenance. + // FIXME: use `expose_provenance` once we figure out whether function pointers have meaningful + // provenance. let rvalue = Rvalue::Cast( CastKind::FnPtrToPtr, Operand::Move(Place::from(Local::new(1))), diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index 9c3f903e0eab7..c88953a1d1a20 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -19,10 +19,11 @@ use rustc_span::source_map::respan; use rustc_span::{Span, Symbol}; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_target::spec::PanicStrategy; +use tracing::debug; use super::{local_decls_for_sig, new_body}; -pub fn build_async_destructor_ctor_shim<'tcx>( +pub(super) fn build_async_destructor_ctor_shim<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>, diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 4fe8cf6213f80..7ed43547e1127 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -33,8 +33,9 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_span::DUMMY_SP; use smallvec::SmallVec; +use tracing::{debug, trace}; -pub enum SimplifyCfg { +pub(super) enum SimplifyCfg { Initial, PromoteConsts, RemoveFalseEdges, @@ -49,7 +50,7 @@ pub enum SimplifyCfg { } impl SimplifyCfg { - pub fn name(&self) -> &'static str { + fn name(&self) -> &'static str { match self { SimplifyCfg::Initial => "SimplifyCfg-initial", SimplifyCfg::PromoteConsts => "SimplifyCfg-promote-consts", @@ -65,7 +66,7 @@ impl SimplifyCfg { } } -pub(crate) fn simplify_cfg(body: &mut Body<'_>) { +pub(super) fn simplify_cfg(body: &mut Body<'_>) { CfgSimplifier::new(body).simplify(); remove_dead_blocks(body); @@ -73,7 +74,7 @@ pub(crate) fn simplify_cfg(body: &mut Body<'_>) { body.basic_blocks_mut().raw.shrink_to_fit(); } -impl<'tcx> MirPass<'tcx> for SimplifyCfg { +impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg { fn name(&self) -> &'static str { self.name() } @@ -84,13 +85,13 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg { } } -pub struct CfgSimplifier<'a, 'tcx> { +struct CfgSimplifier<'a, 'tcx> { basic_blocks: &'a mut IndexSlice>, pred_count: IndexVec, } impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { - pub fn new(body: &'a mut Body<'tcx>) -> Self { + fn new(body: &'a mut Body<'tcx>) -> Self { let mut pred_count = IndexVec::from_elem(0u32, &body.basic_blocks); // we can't use mir.predecessors() here because that counts @@ -110,7 +111,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { CfgSimplifier { basic_blocks, pred_count } } - pub fn simplify(mut self) { + fn simplify(mut self) { self.strip_nops(); // Vec of the blocks that should be merged. We store the indices here, instead of the @@ -279,7 +280,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { } } -pub fn simplify_duplicate_switch_targets(terminator: &mut Terminator<'_>) { +pub(super) fn simplify_duplicate_switch_targets(terminator: &mut Terminator<'_>) { if let TerminatorKind::SwitchInt { targets, .. } = &mut terminator.kind { let otherwise = targets.otherwise(); if targets.iter().any(|t| t.1 == otherwise) { @@ -291,7 +292,7 @@ pub fn simplify_duplicate_switch_targets(terminator: &mut Terminator<'_>) { } } -pub(crate) fn remove_dead_blocks(body: &mut Body<'_>) { +pub(super) fn remove_dead_blocks(body: &mut Body<'_>) { let should_deduplicate_unreachable = |bbdata: &BasicBlockData<'_>| { // CfgSimplifier::simplify leaves behind some unreachable basic blocks without a // terminator. Those blocks will be deleted by remove_dead_blocks, but we run just @@ -359,13 +360,13 @@ pub(crate) fn remove_dead_blocks(body: &mut Body<'_>) { } } -pub enum SimplifyLocals { +pub(super) enum SimplifyLocals { BeforeConstProp, AfterGVN, Final, } -impl<'tcx> MirPass<'tcx> for SimplifyLocals { +impl<'tcx> crate::MirPass<'tcx> for SimplifyLocals { fn name(&self) -> &'static str { match &self { SimplifyLocals::BeforeConstProp => "SimplifyLocals-before-const-prop", @@ -380,23 +381,33 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("running SimplifyLocals on {:?}", body.source); - simplify_locals(body, tcx); - } -} -pub fn remove_unused_definitions<'tcx>(body: &mut Body<'tcx>) { - // First, we're going to get a count of *actual* uses for every `Local`. - let mut used_locals = UsedLocals::new(body); + // First, we're going to get a count of *actual* uses for every `Local`. + let mut used_locals = UsedLocals::new(body); - // Next, we're going to remove any `Local` with zero actual uses. When we remove those - // `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals` - // count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from - // `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a - // fixedpoint where there are no more unused locals. - remove_unused_definitions_helper(&mut used_locals, body); + // Next, we're going to remove any `Local` with zero actual uses. When we remove those + // `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals` + // count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from + // `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a + // fixedpoint where there are no more unused locals. + remove_unused_definitions_helper(&mut used_locals, body); + + // Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the + // `Local`s. + let map = make_local_map(&mut body.local_decls, &used_locals); + + // Only bother running the `LocalUpdater` if we actually found locals to remove. + if map.iter().any(Option::is_none) { + // Update references to all vars and tmps now + let mut updater = LocalUpdater { map, tcx }; + updater.visit_body_preserves_cfg(body); + + body.local_decls.shrink_to_fit(); + } + } } -pub fn simplify_locals<'tcx>(body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>) { +pub(super) fn remove_unused_definitions<'tcx>(body: &mut Body<'tcx>) { // First, we're going to get a count of *actual* uses for every `Local`. let mut used_locals = UsedLocals::new(body); @@ -406,18 +417,6 @@ pub fn simplify_locals<'tcx>(body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>) { // `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a // fixedpoint where there are no more unused locals. remove_unused_definitions_helper(&mut used_locals, body); - - // Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s. - let map = make_local_map(&mut body.local_decls, &used_locals); - - // Only bother running the `LocalUpdater` if we actually found locals to remove. - if map.iter().any(Option::is_none) { - // Update references to all vars and tmps now - let mut updater = LocalUpdater { map, tcx }; - updater.visit_body_preserves_cfg(body); - - body.local_decls.shrink_to_fit(); - } } /// Construct the mapping while swapping out unused stuff out from the `vec`. diff --git a/compiler/rustc_mir_transform/src/simplify_branches.rs b/compiler/rustc_mir_transform/src/simplify_branches.rs index c746041ebd8ac..e83b4727c4856 100644 --- a/compiler/rustc_mir_transform/src/simplify_branches.rs +++ b/compiler/rustc_mir_transform/src/simplify_branches.rs @@ -1,12 +1,14 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use tracing::trace; -pub enum SimplifyConstCondition { +pub(super) enum SimplifyConstCondition { AfterConstProp, Final, } + /// A pass that replaces a branch with a goto when its condition is known. -impl<'tcx> MirPass<'tcx> for SimplifyConstCondition { +impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition { fn name(&self) -> &'static str { match self { SimplifyConstCondition::AfterConstProp => "SimplifyConstCondition-after-const-prop", diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs index 59f67d8e73f45..e8d8335b136b5 100644 --- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs +++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs @@ -7,8 +7,7 @@ use rustc_middle::mir::{ TerminatorKind, }; use rustc_middle::ty::{Ty, TyCtxt}; - -use super::MirPass; +use tracing::trace; /// Pass to convert `if` conditions on integrals into switches on the integral. /// For an example, it turns something like @@ -24,9 +23,9 @@ use super::MirPass; /// ```ignore (MIR) /// switchInt(_4) -> [43i32: bb3, otherwise: bb2]; /// ``` -pub struct SimplifyComparisonIntegral; +pub(super) struct SimplifyComparisonIntegral; -impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral { +impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() > 0 } @@ -74,12 +73,13 @@ impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral { _ => unreachable!(), } - // delete comparison statement if it the value being switched on was moved, which means it can not be user later on + // delete comparison statement if it the value being switched on was moved, which means + // it can not be user later on if opt.can_remove_bin_op_stmt { bb.statements[opt.bin_op_stmt_idx].make_nop(); } else { - // if the integer being compared to a const integral is being moved into the comparison, - // e.g `_2 = Eq(move _3, const 'x');` + // if the integer being compared to a const integral is being moved into the + // comparison, e.g `_2 = Eq(move _3, const 'x');` // we want to avoid making a double move later on in the switchInt on _3. // So to avoid `switchInt(move _3) -> ['x': bb2, otherwise: bb1];`, // we convert the move in the comparison statement to a copy. @@ -103,12 +103,15 @@ impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral { // remove StorageDead (if it exists) being used in the assign of the comparison for (stmt_idx, stmt) in bb.statements.iter().enumerate() { - if !matches!(stmt.kind, StatementKind::StorageDead(local) if local == opt.to_switch_on.local) - { + if !matches!( + stmt.kind, + StatementKind::StorageDead(local) if local == opt.to_switch_on.local + ) { continue; } storage_deads_to_remove.push((stmt_idx, opt.bb_idx)); - // if we have StorageDeads to remove then make sure to insert them at the top of each target + // if we have StorageDeads to remove then make sure to insert them at the top of + // each target for bb_idx in new_targets.all_targets() { storage_deads_to_insert.push(( *bb_idx, @@ -208,7 +211,8 @@ fn find_branch_value_info<'tcx>( (Constant(branch_value), Copy(to_switch_on) | Move(to_switch_on)) | (Copy(to_switch_on) | Move(to_switch_on), Constant(branch_value)) => { let branch_value_ty = branch_value.const_.ty(); - // we only want to apply this optimization if we are matching on integrals (and chars), as it is not possible to switch on floats + // we only want to apply this optimization if we are matching on integrals (and chars), + // as it is not possible to switch on floats if !branch_value_ty.is_integral() && !branch_value_ty.is_char() { return None; }; @@ -223,7 +227,8 @@ fn find_branch_value_info<'tcx>( struct OptimizationInfo<'tcx> { /// Basic block to apply the optimization bb_idx: BasicBlock, - /// Statement index of Eq/Ne assignment that can be removed. None if the assignment can not be removed - i.e the statement is used later on + /// Statement index of Eq/Ne assignment that can be removed. None if the assignment can not be + /// removed - i.e the statement is used later on bin_op_stmt_idx: usize, /// Can remove Eq/Ne assignment can_remove_bin_op_stmt: bool, diff --git a/compiler/rustc_mir_transform/src/single_use_consts.rs b/compiler/rustc_mir_transform/src/single_use_consts.rs index 35cb6872fe9a2..26059268c37d4 100644 --- a/compiler/rustc_mir_transform/src/single_use_consts.rs +++ b/compiler/rustc_mir_transform/src/single_use_consts.rs @@ -19,9 +19,9 @@ use rustc_middle::ty::TyCtxt; /// /// It also removes *never*-used constants, since it had all the information /// needed to do that too, including updating the debug info. -pub struct SingleUseConsts; +pub(super) struct SingleUseConsts; -impl<'tcx> MirPass<'tcx> for SingleUseConsts { +impl<'tcx> crate::MirPass<'tcx> for SingleUseConsts { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() > 0 } diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index c2108795372f6..e6dd2cfd862d8 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -9,10 +9,11 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::value_analysis::{excluded_locals, iter_fields}; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; +use tracing::{debug, instrument}; -pub struct ScalarReplacementOfAggregates; +pub(super) struct ScalarReplacementOfAggregates; -impl<'tcx> MirPass<'tcx> for ScalarReplacementOfAggregates { +impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() >= 2 } diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index 76591f526250c..cf8622cadd1ff 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -14,8 +14,9 @@ use rustc_middle::middle::resolve_bound_vars::Set1; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::{ParamEnv, TyCtxt}; +use tracing::{debug, instrument, trace}; -pub struct SsaLocals { +pub(super) struct SsaLocals { /// Assignments to each local. This defines whether the local is SSA. assignments: IndexVec>, /// We visit the body in reverse postorder, to ensure each local is assigned before it is used. @@ -31,14 +32,18 @@ pub struct SsaLocals { borrowed_locals: BitSet, } -pub enum AssignedValue<'a, 'tcx> { +pub(super) enum AssignedValue<'a, 'tcx> { Arg, Rvalue(&'a mut Rvalue<'tcx>), Terminator, } impl SsaLocals { - pub fn new<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, param_env: ParamEnv<'tcx>) -> SsaLocals { + pub(super) fn new<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + param_env: ParamEnv<'tcx>, + ) -> SsaLocals { let assignment_order = Vec::with_capacity(body.local_decls.len()); let assignments = IndexVec::from_elem(Set1::Empty, &body.local_decls); @@ -100,25 +105,25 @@ impl SsaLocals { ssa } - pub fn num_locals(&self) -> usize { + pub(super) fn num_locals(&self) -> usize { self.assignments.len() } - pub fn locals(&self) -> impl Iterator { + pub(super) fn locals(&self) -> impl Iterator { self.assignments.indices() } - pub fn is_ssa(&self, local: Local) -> bool { + pub(super) fn is_ssa(&self, local: Local) -> bool { matches!(self.assignments[local], Set1::One(_)) } /// Return the number of uses if a local that are not "Deref". - pub fn num_direct_uses(&self, local: Local) -> u32 { + pub(super) fn num_direct_uses(&self, local: Local) -> u32 { self.direct_uses[local] } #[inline] - pub fn assignment_dominates( + pub(super) fn assignment_dominates( &self, dominators: &Dominators, local: Local, @@ -130,7 +135,7 @@ impl SsaLocals { } } - pub fn assignments<'a, 'tcx>( + pub(super) fn assignments<'a, 'tcx>( &'a self, body: &'a Body<'tcx>, ) -> impl Iterator, Location)> + 'a { @@ -147,7 +152,7 @@ impl SsaLocals { }) } - pub fn for_each_assignment_mut<'tcx>( + pub(super) fn for_each_assignment_mut<'tcx>( &self, basic_blocks: &mut IndexSlice>, mut f: impl FnMut(Local, AssignedValue<'_, 'tcx>, Location), @@ -193,17 +198,17 @@ impl SsaLocals { /// _d => _a // transitively through _c /// /// Exception: we do not see through the return place, as it cannot be instantiated. - pub fn copy_classes(&self) -> &IndexSlice { + pub(super) fn copy_classes(&self) -> &IndexSlice { &self.copy_classes } /// Set of SSA locals that are immutably borrowed. - pub fn borrowed_locals(&self) -> &BitSet { + pub(super) fn borrowed_locals(&self) -> &BitSet { &self.borrowed_locals } /// Make a property uniform on a copy equivalence class by removing elements. - pub fn meet_copy_equivalence(&self, property: &mut BitSet) { + pub(super) fn meet_copy_equivalence(&self, property: &mut BitSet) { // Consolidate to have a local iff all its copies are. // // `copy_classes` defines equivalence classes between locals. The `local`s that recursively diff --git a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs index 81baf58a5e0a0..5612e779d6bca 100644 --- a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs +++ b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs @@ -10,10 +10,9 @@ use rustc_middle::mir::{ use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_target::abi::{Abi, Variants}; +use tracing::trace; -use crate::MirPass; - -pub struct UnreachableEnumBranching; +pub(super) struct UnreachableEnumBranching; fn get_discriminant_local(terminator: &TerminatorKind<'_>) -> Option { if let TerminatorKind::SwitchInt { discr: Operand::Move(p), .. } = terminator { @@ -73,7 +72,7 @@ fn variant_discriminants<'tcx>( } } -impl<'tcx> MirPass<'tcx> for UnreachableEnumBranching { +impl<'tcx> crate::MirPass<'tcx> for UnreachableEnumBranching { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() > 0 } @@ -157,9 +156,9 @@ impl<'tcx> MirPass<'tcx> for UnreachableEnumBranching { }; true } - // If and only if there is a variant that does not have a branch set, - // change the current of otherwise as the variant branch and set otherwise to unreachable. - // It transforms following code + // If and only if there is a variant that does not have a branch set, change the + // current of otherwise as the variant branch and set otherwise to unreachable. It + // transforms following code // ```rust // match c { // Ordering::Less => 1, diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index a6c3c3b189ed0..f3dafd13824f3 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -10,9 +10,9 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; use rustc_target::abi::Size; -pub struct UnreachablePropagation; +pub(super) struct UnreachablePropagation; -impl MirPass<'_> for UnreachablePropagation { +impl crate::MirPass<'_> for UnreachablePropagation { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { // Enable only under -Zmir-opt-level=2 as this can make programs less debuggable. sess.mir_opt_level() >= 2 @@ -26,7 +26,8 @@ impl MirPass<'_> for UnreachablePropagation { let terminator = bb_data.terminator(); let is_unreachable = match &terminator.kind { TerminatorKind::Unreachable => true, - // This will unconditionally run into an unreachable and is therefore unreachable as well. + // This will unconditionally run into an unreachable and is therefore unreachable + // as well. TerminatorKind::Goto { target } if unreachable_blocks.contains(target) => { patch.patch_terminator(bb, TerminatorKind::Unreachable); true @@ -85,8 +86,9 @@ fn remove_successors_from_switch<'tcx>( // } // } // - // This generates a `switchInt() -> [0: 0, 1: 1, otherwise: unreachable]`, which allows us or LLVM to - // turn it into just `x` later. Without the unreachable, such a transformation would be illegal. + // This generates a `switchInt() -> [0: 0, 1: 1, otherwise: unreachable]`, which allows us or + // LLVM to turn it into just `x` later. Without the unreachable, such a transformation would be + // illegal. // // In order to preserve this information, we record reachable and unreachable targets as // `Assume` statements in MIR. diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 99e06f59dd04d..3b84755ddedde 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1,6 +1,7 @@ //! Validates the MIR to ensure that invariants are upheld. use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir as hir; use rustc_hir::LangItem; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; @@ -25,7 +26,7 @@ enum EdgeKind { Normal, } -pub struct Validator { +pub(super) struct Validator { /// Describes at which point in the pipeline this validation is happening. pub when: String, /// The phase for which we are upholding the dialect. If the given phase forbids a specific @@ -36,7 +37,7 @@ pub struct Validator { pub mir_phase: MirPhase, } -impl<'tcx> MirPass<'tcx> for Validator { +impl<'tcx> crate::MirPass<'tcx> for Validator { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // FIXME(JakobDegen): These bodies never instantiated in codegend anyway, so it's not // terribly important that they pass the validator. However, I think other passes might @@ -387,10 +388,11 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } self.check_unwind_edge(location, unwind); - // The code generation assumes that there are no critical call edges. The assumption - // is used to simplify inserting code that should be executed along the return edge - // from the call. FIXME(tmiasko): Since this is a strictly code generation concern, - // the code generation should be responsible for handling it. + // The code generation assumes that there are no critical call edges. The + // assumption is used to simplify inserting code that should be executed along + // the return edge from the call. FIXME(tmiasko): Since this is a strictly code + // generation concern, the code generation should be responsible for handling + // it. if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized) && self.is_critical_call_edge(target, unwind) { @@ -403,8 +405,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { ); } - // The call destination place and Operand::Move place used as an argument might be - // passed by a reference to the callee. Consequently they cannot be packed. + // The call destination place and Operand::Move place used as an argument might + // be passed by a reference to the callee. Consequently they cannot be packed. if is_within_packed(self.tcx, &self.body.local_decls, destination).is_some() { // This is bad! The callee will expect the memory to be aligned. self.fail( @@ -530,7 +532,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { /// /// `caller_body` is used to detect cycles in MIR inlining and MIR validation before /// `optimized_mir` is available. -pub fn validate_types<'tcx>( +pub(super) fn validate_types<'tcx>( tcx: TyCtxt<'tcx>, mir_phase: MirPhase, param_env: ty::ParamEnv<'tcx>, @@ -714,7 +716,17 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // since we may be in the process of computing this MIR in the // first place. let layout = if def_id == self.caller_body.source.def_id() { - // FIXME: This is not right for async closures. + self.caller_body.coroutine_layout_raw() + } else if let Some(hir::CoroutineKind::Desugared( + _, + hir::CoroutineSource::Closure, + )) = self.tcx.coroutine_kind(def_id) + && let ty::ClosureKind::FnOnce = + args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap() + && self.caller_body.source.def_id() + == self.tcx.coroutine_by_move_body_def_id(def_id) + { + // Same if this is the by-move body of a coroutine-closure. self.caller_body.coroutine_layout_raw() } else { self.tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()) @@ -942,9 +954,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } AggregateKind::RawPtr(pointee_ty, mutability) => { if !matches!(self.mir_phase, MirPhase::Runtime(_)) { - // It would probably be fine to support this in earlier phases, - // but at the time of writing it's only ever introduced from intrinsic lowering, - // so earlier things just `bug!` on it. + // It would probably be fine to support this in earlier phases, but at the + // time of writing it's only ever introduced from intrinsic lowering, so + // earlier things just `bug!` on it. self.fail(location, "RawPtr should be in runtime MIR only"); } @@ -1098,10 +1110,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } UnOp::PtrMetadata => { if !matches!(self.mir_phase, MirPhase::Runtime(_)) { - // It would probably be fine to support this in earlier phases, - // but at the time of writing it's only ever introduced from intrinsic lowering - // or other runtime-phase optimization passes, - // so earlier things can just `bug!` on it. + // It would probably be fine to support this in earlier phases, but at + // the time of writing it's only ever introduced from intrinsic + // lowering or other runtime-phase optimization passes, so earlier + // things can just `bug!` on it. self.fail(location, "PtrMetadata should be in runtime MIR only"); } @@ -1495,7 +1507,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } if let TerminatorKind::TailCall { .. } = terminator.kind { - // FIXME(explicit_tail_calls): implement tail-call specific checks here (such as signature matching, forbidding closures, etc) + // FIXME(explicit_tail_calls): implement tail-call specific checks here (such + // as signature matching, forbidding closures, etc) } } TerminatorKind::Assert { cond, .. } => { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 9c820b888d910..093697a290c00 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -397,7 +397,7 @@ fn collect_items_rec<'tcx>( MonoItem::Static(def_id) => { recursion_depth_reset = None; - // Statics always get evaluted (which is possible because they can't be generic), so for + // Statics always get evaluated (which is possible because they can't be generic), so for // `MentionedItems` collection there's nothing to do here. if mode == CollectionMode::UsedItems { let instance = Instance::mono(tcx, def_id); @@ -1041,8 +1041,11 @@ fn find_vtable_types_for_unsizing<'tcx>( match (source_ty.kind(), target_ty.kind()) { (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _)) | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => ptr_vtable(a, b), - (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { - ptr_vtable(source_ty.boxed_ty(), target_ty.boxed_ty()) + (_, _) + if let Some(source_boxed) = source_ty.boxed_ty() + && let Some(target_boxed) = target_ty.boxed_ty() => + { + ptr_vtable(source_boxed, target_boxed) } // T as dyn* Trait diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index b22e8e30465ea..91101ddd59022 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -1,5 +1,7 @@ // tidy-alphabetical-start #![feature(array_windows)] +#![feature(if_let_guard)] +#![feature(let_chains)] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index ea244b5107ad6..ca140500e2cd3 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -5,6 +5,7 @@ //! So if you got to this crate from the old solver, it's totally normal. // tidy-alphabetical-start +#![cfg_attr(not(bootstrap), allow(rustc::usage_of_type_ir_inherent))] #![warn(unreachable_pub)] // tidy-alphabetical-end 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 bb05eb4c25697..6c9a6011144cd 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -304,6 +304,11 @@ where let mut candidates = vec![]; + if self.solver_mode() == SolverMode::Coherence { + if let Ok(candidate) = self.consider_coherence_unknowable_candidate(goal) { + return vec![candidate]; + } + } self.assemble_impl_candidates(goal, &mut candidates); self.assemble_builtin_impl_candidates(goal, &mut candidates); @@ -314,11 +319,8 @@ where self.assemble_param_env_candidates(goal, &mut candidates); - match self.solver_mode() { - SolverMode::Normal => self.discard_impls_shadowed_by_env(goal, &mut candidates), - SolverMode::Coherence => { - self.assemble_coherence_unknowable_candidates(goal, &mut candidates) - } + if self.solver_mode() == SolverMode::Normal { + self.discard_impls_shadowed_by_env(goal, &mut candidates); } candidates @@ -682,38 +684,34 @@ where /// also consider impls which may get added in a downstream or sibling crate /// or which an upstream impl may add in a minor release. /// - /// To do so we add an ambiguous candidate in case such an unknown impl could - /// apply to the current goal. + /// To do so we return a single ambiguous candidate in case such an unknown + /// impl could apply to the current goal. #[instrument(level = "trace", skip_all)] - fn assemble_coherence_unknowable_candidates>( + fn consider_coherence_unknowable_candidate>( &mut self, goal: Goal, - candidates: &mut Vec>, - ) { - let cx = self.cx(); - - candidates.extend(self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter( - |ecx| { - let trait_ref = goal.predicate.trait_ref(cx); - if ecx.trait_ref_is_knowable(goal.param_env, trait_ref)? { - Err(NoSolution) - } else { - // While the trait bound itself may be unknowable, we may be able to - // prove that a super trait is not implemented. For this, we recursively - // prove the super trait bounds of the current goal. - // - // We skip the goal itself as that one would cycle. - let predicate: I::Predicate = trait_ref.upcast(cx); - ecx.add_goals( - GoalSource::Misc, - elaborate::elaborate(cx, [predicate]) - .skip(1) - .map(|predicate| goal.with(cx, predicate)), - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } - }, - )) + ) -> Result, NoSolution> { + self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter(|ecx| { + let cx = ecx.cx(); + let trait_ref = goal.predicate.trait_ref(cx); + if ecx.trait_ref_is_knowable(goal.param_env, trait_ref)? { + Err(NoSolution) + } else { + // While the trait bound itself may be unknowable, we may be able to + // prove that a super trait is not implemented. For this, we recursively + // prove the super trait bounds of the current goal. + // + // We skip the goal itself as that one would cycle. + let predicate: I::Predicate = trait_ref.upcast(cx); + ecx.add_goals( + GoalSource::Misc, + elaborate::elaborate(cx, [predicate]) + .skip(1) + .map(|predicate| goal.with(cx, predicate)), + ); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + }) } /// If there's a where-bound for the current goal, do not use any impl candidates diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 2e521ddcec322..1c00f5f8b4173 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -238,7 +238,7 @@ where (normalization_nested_goals.clone(), certainty) } - /// This returns the canoncial variable values to instantiate the bound variables of + /// This returns the canonical variable values to instantiate the bound variables of /// the canonical response. This depends on the `original_values` for the /// bound variables. fn compute_query_response_instantiation_values>( diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index c65c5851e9b4f..536b502136ad3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -164,7 +164,7 @@ where // - `Bound` cannot exist as we don't have a binder around the self Type // - `Expr` is part of `feature(generic_const_exprs)` and is not implemented yet ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Expr(_) => { - panic!("unexpect const kind: {:?}", ct) + panic!("unexpected const kind: {:?}", ct) } } } 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 c7a9846cd97f0..17b6ec7e2bb2c 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 @@ -3,7 +3,7 @@ mod inherent; mod opaque_types; mod weak_types; -use rustc_type_ir::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::{self as ty, Interner, NormalizesTo, Upcast as _}; @@ -106,6 +106,12 @@ where if let Some(projection_pred) = assumption.as_projection_clause() { if projection_pred.projection_def_id() == goal.predicate.def_id() { let cx = ecx.cx(); + if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + goal.predicate.alias.args, + projection_pred.skip_binder().projection_term.args, + ) { + return Err(NoSolution); + } ecx.probe_trait_candidate(source).enter(|ecx| { let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); @@ -144,7 +150,7 @@ where let goal_trait_ref = goal.predicate.alias.trait_ref(cx); let impl_trait_ref = cx.impl_trait_ref(impl_def_id); - if !DeepRejectCtxt::new(ecx.cx(), TreatParams::ForLookup).args_may_unify( + if !DeepRejectCtxt::relate_rigid_infer(ecx.cx()).args_may_unify( goal.predicate.alias.trait_ref(cx).args, impl_trait_ref.skip_binder().args, ) { 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 1314b7eb6ffc6..683d8dab3b2a8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -2,7 +2,7 @@ use rustc_ast_ir::Movability; use rustc_type_ir::data_structures::IndexSet; -use rustc_type_ir::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::visit::TypeVisitableExt as _; @@ -47,7 +47,7 @@ where let cx = ecx.cx(); let impl_trait_ref = cx.impl_trait_ref(impl_def_id); - if !DeepRejectCtxt::new(ecx.cx(), TreatParams::ForLookup) + if !DeepRejectCtxt::relate_rigid_infer(ecx.cx()) .args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) { return Err(NoSolution); @@ -124,6 +124,13 @@ where if trait_clause.def_id() == goal.predicate.def_id() && trait_clause.polarity() == goal.predicate.polarity { + if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + goal.predicate.trait_ref.args, + trait_clause.skip_binder().trait_ref.args, + ) { + return Err(NoSolution); + } + ecx.probe_trait_candidate(source).enter(|ecx| { let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); ecx.eq( @@ -628,7 +635,7 @@ where } // FIXME: This actually should destructure the `Result` we get from transmutability and - // register candiates. We probably need to register >1 since we may have an OR of ANDs. + // register candidates. We probably need to register >1 since we may have an OR of ANDs. ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { let certainty = ecx.is_transmutable( goal.param_env, diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index b33896154f239..c59ae48a07d03 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -12,6 +12,7 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } +rustc_index = { path = "../rustc_index" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 8e8d91ce4d038..ec9a676ea3135 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -381,6 +381,7 @@ parse_invalid_char_in_escape_msg = invalid character in {$is_hex -> *[false] unicode } escape + parse_invalid_comparison_operator = invalid comparison operator `{$invalid}` .use_instead = `{$invalid}` is not a valid comparison operator, use `{$correct}` .spaceship_operator_invalid = `<=>` is not a valid comparison operator, use `std::cmp::Ordering` @@ -581,6 +582,11 @@ parse_missing_trait_in_trait_impl = missing trait in a trait impl .suggestion_add_trait = add a trait here .suggestion_remove_for = for an inherent impl, drop this `for` +parse_misspelled_kw = {$is_incorrect_case -> + [true] write keyword `{$similar_kw}` in lowercase + *[false] there is a keyword `{$similar_kw}` with a similar name +} + parse_modifier_lifetime = `{$modifier}` may only modify trait bounds, not lifetime bounds .suggestion = remove the `{$modifier}` diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index f30939093c2ec..b7232ff21ca03 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -13,7 +13,6 @@ use rustc_session::lint::builtin::{ }; use rustc_session::lint::BuiltinLintDiag; use rustc_session::parse::ParseSess; -use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Pos, Span}; use tracing::debug; @@ -188,9 +187,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { preceded_by_whitespace = true; continue; } - rustc_lexer::TokenKind::Ident => { - self.ident(start) - } + rustc_lexer::TokenKind::Ident => self.ident(start), rustc_lexer::TokenKind::RawIdent => { let sym = nfc_normalize(self.str_from(start + BytePos(2))); let span = self.mk_sp(start, self.pos); @@ -205,20 +202,31 @@ impl<'psess, 'src> StringReader<'psess, 'src> { self.report_unknown_prefix(start); self.ident(start) } - rustc_lexer::TokenKind::InvalidIdent - | rustc_lexer::TokenKind::InvalidPrefix + rustc_lexer::TokenKind::UnknownPrefixLifetime => { + self.report_unknown_prefix(start); + // Include the leading `'` in the real identifier, for macro + // expansion purposes. See #12512 for the gory details of why + // this is necessary. + let lifetime_name = self.str_from(start); + self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1))); + let ident = Symbol::intern(lifetime_name); + token::Lifetime(ident, IdentIsRaw::No) + } + rustc_lexer::TokenKind::InvalidIdent | rustc_lexer::TokenKind::InvalidPrefix // Do not recover an identifier with emoji if the codepoint is a confusable // with a recoverable substitution token, like `➖`. - if !UNICODE_ARRAY - .iter() - .any(|&(c, _, _)| { - let sym = self.str_from(start); - sym.chars().count() == 1 && c == sym.chars().next().unwrap() - }) => + if !UNICODE_ARRAY.iter().any(|&(c, _, _)| { + let sym = self.str_from(start); + sym.chars().count() == 1 && c == sym.chars().next().unwrap() + }) => { let sym = nfc_normalize(self.str_from(start)); let span = self.mk_sp(start, self.pos); - self.psess.bad_unicode_identifiers.borrow_mut().entry(sym).or_default() + self.psess + .bad_unicode_identifiers + .borrow_mut() + .entry(sym) + .or_default() .push(span); token::Ident(sym, IdentIsRaw::No) } @@ -249,9 +257,9 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let suffix = if suffix_start < self.pos { let string = self.str_from(suffix_start); if string == "_" { - self - .dcx() - .emit_err(errors::UnderscoreLiteralSuffix { span: self.mk_sp(suffix_start, self.pos) }); + self.dcx().emit_err(errors::UnderscoreLiteralSuffix { + span: self.mk_sp(suffix_start, self.pos), + }); None } else { Some(Symbol::intern(string)) @@ -269,12 +277,47 @@ impl<'psess, 'src> StringReader<'psess, 'src> { self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1))); if starts_with_number { let span = self.mk_sp(start, self.pos); - self.dcx().struct_err("lifetimes cannot start with a number") + self.dcx() + .struct_err("lifetimes cannot start with a number") .with_span(span) .stash(span, StashKey::LifetimeIsChar); } let ident = Symbol::intern(lifetime_name); - token::Lifetime(ident) + token::Lifetime(ident, IdentIsRaw::No) + } + rustc_lexer::TokenKind::RawLifetime => { + self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1))); + + let ident_start = start + BytePos(3); + let prefix_span = self.mk_sp(start, ident_start); + + if prefix_span.at_least_rust_2021() { + let lifetime_name_without_tick = self.str_from(ident_start); + // Put the `'` back onto the lifetime name. + let mut lifetime_name = String::with_capacity(lifetime_name_without_tick.len() + 1); + lifetime_name.push('\''); + lifetime_name += lifetime_name_without_tick; + let sym = Symbol::intern(&lifetime_name); + + token::Lifetime(sym, IdentIsRaw::Yes) + } else { + // Otherwise, this should be parsed like `'r`. Warn about it though. + self.psess.buffer_lint( + RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, + prefix_span, + ast::CRATE_NODE_ID, + BuiltinLintDiag::RawPrefix(prefix_span), + ); + + // Reset the state so we just lex the `'r`. + let lt_start = start + BytePos(2); + self.pos = lt_start; + self.cursor = Cursor::new(&str_before[2 as usize..]); + + let lifetime_name = self.str_from(start); + let ident = Symbol::intern(lifetime_name); + token::Lifetime(ident, IdentIsRaw::No) + } } rustc_lexer::TokenKind::Semi => token::Semi, rustc_lexer::TokenKind::Comma => token::Comma, @@ -331,16 +374,19 @@ impl<'psess, 'src> StringReader<'psess, 'src> { // first remove compound tokens like `<<` from `rustc_lexer`, and then add // fancier error recovery to it, as there will be less overall work to do this // way. - let (token, sugg) = unicode_chars::check_for_substitution(self, start, c, repeats+1); + let (token, sugg) = + unicode_chars::check_for_substitution(self, start, c, repeats + 1); self.dcx().emit_err(errors::UnknownTokenStart { span: self.mk_sp(start, self.pos + Pos::from_usize(repeats * c.len_utf8())), escaped: escaped_char(c), sugg, - null: if c == '\x00' {Some(errors::UnknownTokenNull)} else {None}, + null: if c == '\x00' { Some(errors::UnknownTokenNull) } else { None }, repeat: if repeats > 0 { swallow_next_invalid = repeats; Some(errors::UnknownTokenRepeat { repeats }) - } else {None} + } else { + None + }, }); if let Some(token) = token { @@ -699,7 +745,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let expn_data = prefix_span.ctxt().outer_expn_data(); - if expn_data.edition >= Edition::Edition2021 { + if expn_data.edition.at_least_rust_2021() { // In Rust 2021, this is a hard error. let sugg = if prefix == "rb" { Some(errors::UnknownPrefixSugg::UseBr(prefix_span)) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 49df2811d525b..205cca830b2e0 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::{iter, mem}; use rustc_ast::token::{Delimiter, Token, TokenKind}; @@ -6,6 +7,7 @@ use rustc_ast::tokenstream::{ Spacing, ToAttrTokenStream, }; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, HasTokens}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::PResult; use rustc_session::parse::ParseSess; use rustc_span::{sym, Span, DUMMY_SP}; @@ -134,9 +136,8 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { node_replacements.array_windows() { assert!( - node_range.0.end <= next_node_range.0.start - || node_range.0.end >= next_node_range.0.end, - "Node ranges should be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", + node_range.0.end <= next_node_range.0.start, + "Node ranges should be disjoint: ({:?}, {:?}) ({:?}, {:?})", node_range, tokens, next_node_range, @@ -144,20 +145,8 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { ); } - // Process the replace ranges, starting from the highest start - // position and working our way back. If have tokens like: - // - // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` - // - // Then we will generate replace ranges for both - // the `#[cfg(FALSE)] field: bool` and the entire - // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` - // - // By starting processing from the replace range with the greatest - // start position, we ensure that any (outer) replace range which - // encloses another (inner) replace range will fully overwrite the - // inner range's replacement. - for (node_range, target) in node_replacements.into_iter().rev() { + // Process the replace ranges. + for (node_range, target) in node_replacements.into_iter() { assert!( !node_range.0.is_empty(), "Cannot replace an empty node range: {:?}", @@ -234,6 +223,8 @@ impl<'a> Parser<'a> { force_collect: ForceCollect, f: impl FnOnce(&mut Self, AttrVec) -> PResult<'a, (R, Trailing, UsePreAttrPos)>, ) -> PResult<'a, R> { + let possible_capture_mode = self.capture_cfg; + // We must collect if anything could observe the collected tokens, i.e. // if any of the following conditions hold. // - We are force collecting tokens (because force collection requires @@ -244,9 +235,9 @@ impl<'a> Parser<'a> { // - Our target supports custom inner attributes (custom // inner attribute invocation might require token capturing). || R::SUPPORTS_CUSTOM_INNER_ATTRS - // - We are in `capture_cfg` mode (which requires tokens if + // - We are in "possible capture mode" (which requires tokens if // the parsed node has `#[cfg]` or `#[cfg_attr]` attributes). - || self.capture_cfg; + || possible_capture_mode; if !needs_collection { return Ok(f(self, attrs.attrs)?.0); } @@ -267,18 +258,48 @@ impl<'a> Parser<'a> { res? }; - // When we're not in `capture_cfg` mode, then skip collecting and - // return early if either of the following conditions hold. // - `None`: Our target doesn't support tokens at all (e.g. `NtIdent`). + // - `Some(None)`: Our target supports tokens and has none. // - `Some(Some(_))`: Our target already has tokens set (e.g. we've - // parsed something like `#[my_attr] $item`). The actual parsing code - // takes care of prepending any attributes to the nonterminal, so we - // don't need to modify the already captured tokens. + // parsed something like `#[my_attr] $item`). + let ret_can_hold_tokens = matches!(ret.tokens_mut(), Some(None)); + + // Ignore any attributes we've previously processed. This happens when + // an inner call to `collect_tokens` returns an AST node and then an + // outer call ends up with the same AST node without any additional + // wrapping layer. + let mut seen_indices = FxHashSet::default(); + for (i, attr) in ret.attrs().iter().enumerate() { + let is_unseen = self.capture_state.seen_attrs.insert(attr.id); + if !is_unseen { + seen_indices.insert(i); + } + } + let ret_attrs: Cow<'_, [Attribute]> = + if seen_indices.is_empty() { + Cow::Borrowed(ret.attrs()) + } else { + let ret_attrs = + ret.attrs() + .iter() + .enumerate() + .filter_map(|(i, attr)| { + if seen_indices.contains(&i) { None } else { Some(attr.clone()) } + }) + .collect(); + Cow::Owned(ret_attrs) + }; + + // When we're not in "definite capture mode", then skip collecting and + // return early if `ret` doesn't support tokens or already has some. // // Note that this check is independent of `force_collect`. There's no // need to collect tokens when we don't support tokens or already have // tokens. - if !self.capture_cfg && matches!(ret.tokens_mut(), None | Some(Some(_))) { + let definite_capture_mode = self.capture_cfg + && matches!(self.capture_state.capturing, Capturing::Yes) + && has_cfg_or_cfg_attr(&ret_attrs); + if !definite_capture_mode && !ret_can_hold_tokens { return Ok(ret); } @@ -297,12 +318,12 @@ impl<'a> Parser<'a> { // outer and inner attributes. So this check is more precise than // the earlier `needs_tokens` check, and we don't need to // check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.) - || needs_tokens(ret.attrs()) - // - We are in `capture_cfg` mode and there are `#[cfg]` or - // `#[cfg_attr]` attributes. (During normal non-`capture_cfg` - // parsing, we don't need any special capturing for those - // attributes, because they're builtin.) - || (self.capture_cfg && has_cfg_or_cfg_attr(ret.attrs())); + || needs_tokens(&ret_attrs) + // - We are in "definite capture mode", which requires that there + // are `#[cfg]` or `#[cfg_attr]` attributes. (During normal + // non-`capture_cfg` parsing, we don't need any special capturing + // for those attributes, because they're builtin.) + || definite_capture_mode; if !needs_collection { return Ok(ret); } @@ -336,7 +357,7 @@ impl<'a> Parser<'a> { // `Parser::parse_inner_attributes`, and pair them in a `ParserReplacement` with `None`, // which means the relevant tokens will be removed. (More details below.) let mut inner_attr_parser_replacements = Vec::new(); - for attr in ret.attrs() { + for attr in ret_attrs.iter() { if attr.style == ast::AttrStyle::Inner { if let Some(inner_attr_parser_range) = self.capture_state.inner_attr_parser_ranges.remove(&attr.id) @@ -359,11 +380,10 @@ impl<'a> Parser<'a> { // from `ParserRange` form to `NodeRange` form. We will perform the actual // replacement only when we convert the `LazyAttrTokenStream` to an // `AttrTokenStream`. - self.capture_state.parser_replacements - [parser_replacements_start..parser_replacements_end] - .iter() - .cloned() - .chain(inner_attr_parser_replacements.iter().cloned()) + self.capture_state + .parser_replacements + .drain(parser_replacements_start..parser_replacements_end) + .chain(inner_attr_parser_replacements.into_iter()) .map(|(parser_range, data)| { (NodeRange::new(parser_range, collect_pos.start_pos), data) }) @@ -399,20 +419,12 @@ impl<'a> Parser<'a> { break_last_token: self.break_last_token, node_replacements, }); + let mut tokens_used = false; - // If we support tokens and don't already have them, store the newly captured tokens. - if let Some(target_tokens @ None) = ret.tokens_mut() { - *target_tokens = Some(tokens.clone()); - } - - // If `capture_cfg` is set and we're inside a recursive call to - // `collect_tokens`, then we need to register a replace range if we - // have `#[cfg]` or `#[cfg_attr]`. This allows us to run eager - // cfg-expansion on the captured token stream. - if self.capture_cfg - && matches!(self.capture_state.capturing, Capturing::Yes) - && has_cfg_or_cfg_attr(ret.attrs()) - { + // If in "definite capture mode" we need to register a replace range + // for the `#[cfg]` and/or `#[cfg_attr]` attrs. This allows us to run + // eager cfg-expansion on the captured token stream. + if definite_capture_mode { assert!(!self.break_last_token, "Should not have unglued last token with cfg attr"); // What is the status here when parsing the example code at the top of this method? @@ -429,7 +441,9 @@ impl<'a> Parser<'a> { // cfg-expand this AST node. let start_pos = if has_outer_attrs { attrs.start_pos.unwrap() } else { collect_pos.start_pos }; - let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens }; + let target = + AttrsTarget { attrs: ret_attrs.iter().cloned().collect(), tokens: tokens.clone() }; + tokens_used = true; self.capture_state .parser_replacements .push((ParserRange(start_pos..end_pos), Some(target))); @@ -438,7 +452,16 @@ impl<'a> Parser<'a> { // the outermost call to this method. self.capture_state.parser_replacements.clear(); self.capture_state.inner_attr_parser_ranges.clear(); + self.capture_state.seen_attrs.clear(); } + + // If we support tokens and don't already have them, store the newly captured tokens. + if let Some(target_tokens @ None) = ret.tokens_mut() { + tokens_used = true; + *target_tokens = Some(tokens); + } + + assert!(tokens_used); // check we didn't create `tokens` unnecessarily Ok(ret) } } @@ -510,9 +533,11 @@ fn make_attr_token_stream( } /// Tokens are needed if: -/// - any non-single-segment attributes (other than doc comments) are present; or -/// - any `cfg_attr` attributes are present; -/// - any single-segment, non-builtin attributes are present. +/// - any non-single-segment attributes (other than doc comments) are present, +/// e.g. `rustfmt::skip`; or +/// - any `cfg_attr` attributes are present; or +/// - any single-segment, non-builtin attributes are present, e.g. `derive`, +/// `test`, `global_allocator`. fn needs_tokens(attrs: &[ast::Attribute]) -> bool { attrs.iter().any(|attr| match attr.ident() { None => !attr.is_doc_comment(), diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index fcdc10c0837e5..f6f66821df7fb 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -19,8 +19,9 @@ use rustc_errors::{ Subdiagnostic, }; use rustc_session::errors::ExprParenthesesNeeded; +use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::symbol::{kw, sym, AllKeywords, Ident}; use rustc_span::{BytePos, Span, SpanSnippetError, Symbol, DUMMY_SP}; use thin_vec::{thin_vec, ThinVec}; use tracing::{debug, trace}; @@ -203,6 +204,37 @@ impl std::fmt::Display for UnaryFixity { } } +#[derive(Debug, rustc_macros::Subdiagnostic)] +#[suggestion( + parse_misspelled_kw, + applicability = "machine-applicable", + code = "{similar_kw}", + style = "verbose" +)] +struct MisspelledKw { + similar_kw: String, + #[primary_span] + span: Span, + is_incorrect_case: bool, +} + +/// Checks if the given `lookup` identifier is similar to any keyword symbol in `candidates`. +fn find_similar_kw(lookup: Ident, candidates: &[Symbol]) -> Option { + let lowercase = lookup.name.as_str().to_lowercase(); + let lowercase_sym = Symbol::intern(&lowercase); + if candidates.contains(&lowercase_sym) { + Some(MisspelledKw { similar_kw: lowercase, span: lookup.span, is_incorrect_case: true }) + } else if let Some(similar_sym) = find_best_match_for_name(candidates, lookup.name, None) { + Some(MisspelledKw { + similar_kw: similar_sym.to_string(), + span: lookup.span, + is_incorrect_case: false, + }) + } else { + None + } +} + struct MultiSugg { msg: String, patches: Vec<(Span, String)>, @@ -638,9 +670,9 @@ impl<'a> Parser<'a> { let concat = Symbol::intern(&format!("{prev}{cur}")); let ident = Ident::new(concat, DUMMY_SP); if ident.is_used_keyword() || ident.is_reserved() || ident.is_raw_guess() { - let span = self.prev_token.span.to(self.token.span); + let concat_span = self.prev_token.span.to(self.token.span); err.span_suggestion_verbose( - span, + concat_span, format!("consider removing the space to spell keyword `{concat}`"), concat, Applicability::MachineApplicable, @@ -741,9 +773,55 @@ impl<'a> Parser<'a> { err.span_label(sp, label_exp); err.span_label(self.token.span, "unexpected token"); } + + // Check for misspelled keywords if there are no suggestions added to the diagnostic. + if err.suggestions.as_ref().is_ok_and(|code_suggestions| code_suggestions.is_empty()) { + self.check_for_misspelled_kw(&mut err, &expected); + } Err(err) } + /// Checks if the current token or the previous token are misspelled keywords + /// and adds a helpful suggestion. + fn check_for_misspelled_kw(&self, err: &mut Diag<'_>, expected: &[TokenType]) { + let Some((curr_ident, _)) = self.token.ident() else { + return; + }; + let expected_tokens: &[TokenType] = + expected.len().checked_sub(10).map_or(&expected, |index| &expected[index..]); + let expected_keywords: Vec = expected_tokens + .iter() + .filter_map(|token| if let TokenType::Keyword(kw) = token { Some(*kw) } else { None }) + .collect(); + + // When there are a few keywords in the last ten elements of `self.expected_tokens` and the current + // token is an identifier, it's probably a misspelled keyword. + // This handles code like `async Move {}`, misspelled `if` in match guard, misspelled `else` in `if`-`else` + // and mispelled `where` in a where clause. + if !expected_keywords.is_empty() + && !curr_ident.is_used_keyword() + && let Some(misspelled_kw) = find_similar_kw(curr_ident, &expected_keywords) + { + err.subdiagnostic(misspelled_kw); + } else if let Some((prev_ident, _)) = self.prev_token.ident() + && !prev_ident.is_used_keyword() + { + // We generate a list of all keywords at runtime rather than at compile time + // so that it gets generated only when the diagnostic needs it. + // Also, it is unlikely that this list is generated multiple times because the + // parser halts after execution hits this path. + let all_keywords = AllKeywords::new().collect_used(|| prev_ident.span.edition()); + + // Otherwise, check the previous token with all the keywords as possible candidates. + // This handles code like `Struct Human;` and `While a < b {}`. + // We check the previous token only when the current token is an identifier to avoid false + // positives like suggesting keyword `for` for `extern crate foo {}`. + if let Some(misspelled_kw) = find_similar_kw(prev_ident, &all_keywords) { + err.subdiagnostic(misspelled_kw); + } + } + } + /// The user has written `#[attr] expr` which is unsupported. (#106020) pub(super) fn attr_on_non_tail_expr(&self, expr: &Expr) -> ErrorGuaranteed { // Missing semicolon typo error. @@ -846,6 +924,7 @@ impl<'a> Parser<'a> { ); } } + err.emit() } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 84684e808d940..ecc4cd96fafb0 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2050,7 +2050,7 @@ impl<'a> Parser<'a> { }; // On an error path, eagerly consider a lifetime to be an unclosed character lit, if that // makes sense. - if let Some(ident) = self.token.lifetime() + if let Some((ident, IdentIsRaw::No)) = self.token.lifetime() && could_be_unclosed_char_literal(ident) { let lt = self.expect_lifetime(); @@ -2925,9 +2925,9 @@ impl<'a> Parser<'a> { } pub(crate) fn eat_label(&mut self) -> Option