Merge pull request #4656 from rust-lang/rustup-2025-10-30
Automatic Rustup
This commit is contained in:
commit
9cb215ada4
664 changed files with 15920 additions and 17722 deletions
1
.mailmap
1
.mailmap
|
|
@ -680,6 +680,7 @@ Valerii Lashmanov <vflashm@gmail.com>
|
|||
Vitali Haravy <HumaneProgrammer@gmail.com> Vitali Haravy <humaneprogrammer@gmail.com>
|
||||
Vitaly Shukela <vi0oss@gmail.com>
|
||||
Waffle Lapkin <waffle.lapkin@gmail.com>
|
||||
Waffle Lapkin <waffle.lapkin@gmail.com> <Waffle Lapkin>
|
||||
Waffle Lapkin <waffle.lapkin@gmail.com> <waffle.lapkin@tasking.com>
|
||||
Weihang Lo <me@weihanglo.tw>
|
||||
Weihang Lo <me@weihanglo.tw> <weihanglo@users.noreply.github.com>
|
||||
|
|
|
|||
39
Cargo.lock
39
Cargo.lock
|
|
@ -75,7 +75,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"unicode-width 0.2.1",
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "annotate-snippets"
|
||||
version = "0.12.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47224528f74de27d1d06aad6a5dda4f865b6ebe2e56c538943d746a7270cb67e"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -136,7 +146,7 @@ dependencies = [
|
|||
"anstyle-lossy",
|
||||
"anstyle-parse",
|
||||
"html-escape",
|
||||
"unicode-width 0.2.1",
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -464,10 +474,11 @@ version = "0.1.0"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.16"
|
||||
version = "1.2.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||
checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
|
|
@ -677,7 +688,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
|
|||
dependencies = [
|
||||
"serde",
|
||||
"termcolor",
|
||||
"unicode-width 0.2.1",
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -808,7 +819,7 @@ dependencies = [
|
|||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"unicode-width 0.2.1",
|
||||
"unicode-width 0.2.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
|
|
@ -1485,7 +1496,7 @@ version = "0.2.24"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
|
||||
dependencies = [
|
||||
"unicode-width 0.2.1",
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1887,7 +1898,7 @@ dependencies = [
|
|||
"console",
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-width 0.2.1",
|
||||
"unicode-width 0.2.2",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
|
|
@ -3756,7 +3767,7 @@ dependencies = [
|
|||
name = "rustc_errors"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.11.5",
|
||||
"annotate-snippets 0.12.7",
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"derive_setters",
|
||||
|
|
@ -4331,7 +4342,7 @@ dependencies = [
|
|||
"thin-vec",
|
||||
"tracing",
|
||||
"unicode-normalization",
|
||||
"unicode-width 0.2.1",
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4590,7 +4601,7 @@ dependencies = [
|
|||
"sha1",
|
||||
"sha2",
|
||||
"tracing",
|
||||
"unicode-width 0.2.1",
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5936,9 +5947,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
|
|
@ -6223,7 +6234,7 @@ dependencies = [
|
|||
"bumpalo",
|
||||
"leb128fmt",
|
||||
"memchr",
|
||||
"unicode-width 0.2.1",
|
||||
"unicode-width 0.2.2",
|
||||
"wasm-encoder 0.240.0",
|
||||
]
|
||||
|
||||
|
|
|
|||
211
RELEASES.md
211
RELEASES.md
|
|
@ -1,3 +1,214 @@
|
|||
Version 1.91.0 (2025-10-30)
|
||||
==========================
|
||||
|
||||
<a id="1.91.0-Language"></a>
|
||||
|
||||
Language
|
||||
--------
|
||||
|
||||
- [Lower pattern bindings in the order they're written and base drop order on primary bindings' order](https://github.com/rust-lang/rust/pull/143764)
|
||||
- [Stabilize declaration of C-style variadic functions for `sysv64`, `win64`, `efiapi`, and `aapcs` ABIs](https://github.com/rust-lang/rust/pull/144066).
|
||||
This brings these ABIs in line with the C ABI: variadic functions can be declared in extern blocks but not defined.
|
||||
- [Add `dangling_pointers_from_locals` lint to warn against dangling pointers from local variables](https://github.com/rust-lang/rust/pull/144322)
|
||||
- [Upgrade `semicolon_in_expressions_from_macros` from warn to deny](https://github.com/rust-lang/rust/pull/144369)
|
||||
- [Stabilize LoongArch32 inline assembly](https://github.com/rust-lang/rust/pull/144402)
|
||||
- [Add warn-by-default `integer_to_ptr_transmutes` lint against integer-to-pointer transmutes](https://github.com/rust-lang/rust/pull/144531)
|
||||
- [Stabilize `sse4a` and `tbm` target features](https://github.com/rust-lang/rust/pull/144542)
|
||||
- [Add `target_env = "macabi"` and `target_env = "sim"` cfgs](https://github.com/rust-lang/rust/pull/139451) as replacements for the `target_abi` cfgs with the same values.
|
||||
|
||||
<a id="1.91.0-Compiler"></a>
|
||||
|
||||
Compiler
|
||||
--------
|
||||
|
||||
- [Don't warn on never-to-any `as` casts as unreachable](https://github.com/rust-lang/rust/pull/144804)
|
||||
|
||||
<a id="1.91.0-Platform-Support"></a>
|
||||
|
||||
Platform Support
|
||||
----------------
|
||||
|
||||
- [Promote `aarch64-pc-windows-gnullvm` and `x86_64-pc-windows-gnullvm` to Tier 2 with host tools.](https://github.com/rust-lang/rust/pull/143031)
|
||||
Note: llvm-tools and MSI installers are missing but will be added in future releases.
|
||||
- [Promote `aarch64-pc-windows-msvc` to Tier 1](https://github.com/rust-lang/rust/pull/145682)
|
||||
|
||||
Refer to Rust's [platform support page][platform-support-doc]
|
||||
for more information on Rust's tiered platform support.
|
||||
|
||||
[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html
|
||||
|
||||
<a id="1.91.0-Libraries"></a>
|
||||
|
||||
Libraries
|
||||
---------
|
||||
|
||||
- [Print thread ID in panic message](https://github.com/rust-lang/rust/pull/115746)
|
||||
- [Fix overly restrictive lifetime in `core::panic::Location::file` return type](https://github.com/rust-lang/rust/pull/132087)
|
||||
- [Guarantee parameter order for `_by()` variants of `min` / `max`/ `minmax` in `std::cmp`](https://github.com/rust-lang/rust/pull/139357)
|
||||
- [Document assumptions about `Clone` and `Eq` traits](https://github.com/rust-lang/rust/pull/144330/)
|
||||
- [`std::thread`: Return error if setting thread stack size fails](https://github.com/rust-lang/rust/pull/144210)
|
||||
This used to panic within the standard library.
|
||||
|
||||
<a id="1.91.0-Stabilized-APIs"></a>
|
||||
|
||||
Stabilized APIs
|
||||
---------------
|
||||
|
||||
- [`Path::file_prefix`](https://doc.rust-lang.org/stable/std/path/struct.Path.html#method.file_prefix)
|
||||
- [`AtomicPtr::fetch_ptr_add`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_ptr_add)
|
||||
- [`AtomicPtr::fetch_ptr_sub`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_ptr_sub)
|
||||
- [`AtomicPtr::fetch_byte_add`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_byte_add)
|
||||
- [`AtomicPtr::fetch_byte_sub`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_byte_sub)
|
||||
- [`AtomicPtr::fetch_or`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_or)
|
||||
- [`AtomicPtr::fetch_and`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_and)
|
||||
- [`AtomicPtr::fetch_xor`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_xor)
|
||||
- [`{integer}::strict_add`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_add)
|
||||
- [`{integer}::strict_sub`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_sub)
|
||||
- [`{integer}::strict_mul`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_mul)
|
||||
- [`{integer}::strict_div`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_div)
|
||||
- [`{integer}::strict_div_euclid`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_div_euclid)
|
||||
- [`{integer}::strict_rem`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_rem)
|
||||
- [`{integer}::strict_rem_euclid`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_rem_euclid)
|
||||
- [`{integer}::strict_neg`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_neg)
|
||||
- [`{integer}::strict_shl`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_shl)
|
||||
- [`{integer}::strict_shr`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_shr)
|
||||
- [`{integer}::strict_pow`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_pow)
|
||||
- [`i{N}::strict_add_unsigned`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_add_unsigned)
|
||||
- [`i{N}::strict_sub_unsigned`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_sub_unsigned)
|
||||
- [`i{N}::strict_abs`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_abs)
|
||||
- [`u{N}::strict_add_signed`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_add_signed)
|
||||
- [`u{N}::strict_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_sub_signed)
|
||||
- [`PanicHookInfo::payload_as_str`](https://doc.rust-lang.org/stable/std/panic/struct.PanicHookInfo.html#method.payload_as_str)
|
||||
- [`core::iter::chain`](https://doc.rust-lang.org/stable/core/iter/fn.chain.html)
|
||||
- [`u{N}::checked_signed_diff`](https://doc.rust-lang.org/stable/std/primitive.u16.html#method.checked_signed_diff)
|
||||
- [`core::array::repeat`](https://doc.rust-lang.org/stable/core/array/fn.repeat.html)
|
||||
- [`PathBuf::add_extension`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.add_extension)
|
||||
- [`PathBuf::with_added_extension`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.with_added_extension)
|
||||
- [`Duration::from_mins`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_mins)
|
||||
- [`Duration::from_hours`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_hours)
|
||||
- [`impl PartialEq<str> for PathBuf`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#impl-PartialEq%3Cstr%3E-for-PathBuf)
|
||||
- [`impl PartialEq<String> for PathBuf`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#impl-PartialEq%3CString%3E-for-PathBuf)
|
||||
- [`impl PartialEq<str> for Path`](https://doc.rust-lang.org/stable/std/path/struct.Path.html#impl-PartialEq%3Cstr%3E-for-Path)
|
||||
- [`impl PartialEq<String> for Path`](https://doc.rust-lang.org/stable/std/path/struct.Path.html#impl-PartialEq%3CString%3E-for-Path)
|
||||
- [`impl PartialEq<PathBuf> for String`](https://doc.rust-lang.org/stable/std/string/struct.String.html#impl-PartialEq%3CPathBuf%3E-for-String)
|
||||
- [`impl PartialEq<Path> for String`](https://doc.rust-lang.org/stable/std/string/struct.String.html#impl-PartialEq%3CPath%3E-for-String)
|
||||
- [`impl PartialEq<PathBuf> for str`](https://doc.rust-lang.org/stable/std/primitive.str.html#impl-PartialEq%3CPathBuf%3E-for-str)
|
||||
- [`impl PartialEq<Path> for str`](https://doc.rust-lang.org/stable/std/primitive.str.html#impl-PartialEq%3CPath%3E-for-str)
|
||||
- [`Ipv4Addr::from_octets`](https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.from_octets)
|
||||
- [`Ipv6Addr::from_octets`](https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.from_octets)
|
||||
- [`Ipv6Addr::from_segments`](https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.from_segments)
|
||||
- [`impl<T> Default for Pin<Box<T>> where Box<T>: Default, T: ?Sized`](https://doc.rust-lang.org/stable/std/default/trait.Default.html#impl-Default-for-Pin%3CBox%3CT%3E%3E)
|
||||
- [`impl<T> Default for Pin<Rc<T>> where Rc<T>: Default, T: ?Sized`](https://doc.rust-lang.org/stable/std/default/trait.Default.html#impl-Default-for-Pin%3CRc%3CT%3E%3E)
|
||||
- [`impl<T> Default for Pin<Arc<T>> where Arc<T>: Default, T: ?Sized`](https://doc.rust-lang.org/stable/std/default/trait.Default.html#impl-Default-for-Pin%3CArc%3CT%3E%3E)
|
||||
- [`Cell::as_array_of_cells`](https://doc.rust-lang.org/stable/std/cell/struct.Cell.html#method.as_array_of_cells)
|
||||
- [`u{N}::carrying_add`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.carrying_add)
|
||||
- [`u{N}::borrowing_sub`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.borrowing_sub)
|
||||
- [`u{N}::carrying_mul`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.carrying_mul)
|
||||
- [`u{N}::carrying_mul_add`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.carrying_mul_add)
|
||||
- [`BTreeMap::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.BTreeMap.html#method.extract_if)
|
||||
- [`BTreeSet::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.BTreeSet.html#method.extract_if)
|
||||
- [`impl Debug for windows::ffi::EncodeWide<'_>`](https://doc.rust-lang.org/stable/std/os/windows/ffi/struct.EncodeWide.html#impl-Debug-for-EncodeWide%3C'_%3E)
|
||||
- [`str::ceil_char_boundary`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.ceil_char_boundary)
|
||||
- [`str::floor_char_boundary`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.floor_char_boundary)
|
||||
- [`impl Sum for Saturating<u{N}>`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Sum-for-Saturating%3Cu32%3E)
|
||||
- [`impl Sum<&Self> for Saturating<u{N}>`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Sum%3C%26Saturating%3Cu32%3E%3E-for-Saturating%3Cu32%3E)
|
||||
- [`impl Product for Saturating<u{N}>`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Product-for-Saturating%3Cu32%3E)
|
||||
- [`impl Product<&Self> for Saturating<u{N}>`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Product%3C%26Saturating%3Cu32%3E%3E-for-Saturating%3Cu32%3E)
|
||||
|
||||
These previously stable APIs are now stable in const contexts:
|
||||
|
||||
- [`<[T; N]>::each_ref`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_ref)
|
||||
- [`<[T; N]>::each_mut`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_mut)
|
||||
- [`OsString::new`](https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html#method.new)
|
||||
- [`PathBuf::new`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.new)
|
||||
- [`TypeId::of`](https://doc.rust-lang.org/stable/std/any/struct.TypeId.html#method.of)
|
||||
- [`ptr::with_exposed_provenance`](https://doc.rust-lang.org/stable/std/ptr/fn.with_exposed_provenance.html)
|
||||
- [`ptr::with_exposed_provenance_mut`](https://doc.rust-lang.org/stable/std/ptr/fn.with_exposed_provenance_mut.html)
|
||||
|
||||
<a id="1.91.0-Cargo"></a>
|
||||
|
||||
Cargo
|
||||
-----
|
||||
|
||||
- 🎉 Stabilize `build.build-dir`.
|
||||
This config sets the directory where intermediate build artifacts are stored.
|
||||
These artifacts are produced by Cargo and rustc during the build process.
|
||||
End users usually won't need to interact with them, and the layout inside
|
||||
`build-dir` is an implementation detail that may change without notice.
|
||||
([config doc](https://doc.rust-lang.org/stable/cargo/reference/config.html#buildbuild-dir))
|
||||
([build cache doc](https://doc.rust-lang.org/stable/cargo/reference/build-cache.html))
|
||||
[#15833](https://github.com/rust-lang/cargo/pull/15833)
|
||||
[#15840](https://github.com/rust-lang/cargo/pull/15840)
|
||||
- The `--target` flag and the `build.target` configuration can now take literal
|
||||
`"host-tuple"` string, which will internally be substituted by the host
|
||||
machine's target triple.
|
||||
[#15838](https://github.com/rust-lang/cargo/pull/15838)
|
||||
[#16003](https://github.com/rust-lang/cargo/pull/16003)
|
||||
[#16032](https://github.com/rust-lang/cargo/pull/16032)
|
||||
|
||||
<a id="1.91.0-Rustdoc"></a>
|
||||
|
||||
Rustdoc
|
||||
-----
|
||||
- [In search results, rank doc aliases lower than non-alias items with the same name](https://github.com/rust-lang/rust/pull/145100)
|
||||
- [Raw pointers now work in type-based search like references](https://github.com/rust-lang/rust/pull/145731). This means you can now search for things like `*const u8 ->`, and additionally functions that take or return raw pointers will now display their signature properly in search results.
|
||||
|
||||
<a id="1.91.0-Compatibility-Notes"></a>
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
|
||||
- [Always require coroutine captures to be drop-live](https://github.com/rust-lang/rust/pull/144156)
|
||||
- [Apple: Always pass SDK root when linking with `cc`, and pass it via `SDKROOT` env var](https://github.com/rust-lang/rust/pull/131477). This should fix linking issues with `rustc` running inside Xcode. Libraries in `/usr/local/lib` may no longer be linked automatically, if you develop or use a crate that relies on this, you should explicitly set `cargo::rustc-link-search=/usr/local/lib` in a `build.rs` script.
|
||||
- [Relaxed bounds in associated type bound position like in `TraitRef<AssocTy: ?Sized>` are now correctly forbidden](https://github.com/rust-lang/rust/pull/135331)
|
||||
- [Add unstable `#[sanitize(xyz = "on|off")]` built-in attribute that shadows procedural macros with the same name](https://github.com/rust-lang/rust/pull/142681)
|
||||
- [Fix the drop checker being more permissive for bindings declared with let-else](https://github.com/rust-lang/rust/pull/143028)
|
||||
- [Be more strict when parsing attributes, erroring on many invalid attributes](https://github.com/rust-lang/rust/pull/144689)
|
||||
- [Error on invalid `#[should_panic]` attributes](https://github.com/rust-lang/rust/pull/143808)
|
||||
- [Error on invalid `#[link]` attributes](https://github.com/rust-lang/rust/pull/143193)
|
||||
- [Mark all deprecation lints in name resolution as deny-by-default and also report in dependencies](https://github.com/rust-lang/rust/pull/143929)
|
||||
- The lint `semicolon_in_expressions_from_macros`, for `macro_rules!` macros in expression position that expand to end in a semicolon (`;`), is now deny-by-default. It was already warn-by-default, and a future compatibility warning (FCW) that warned even in dependencies. This lint will become a hard error in the future.
|
||||
- [Trait impl modifiers (e.g., `unsafe`, `!`, `default`) in inherent impls are no longer syntactically valid](https://github.com/rust-lang/rust/pull/144386)
|
||||
- [Start reporting future breakage for `ill_formed_attribute_input` in dependencies](https://github.com/rust-lang/rust/pull/144544)
|
||||
- [Restrict the scope of temporaries created by the macros `pin!`, `format_args!`, `write!`, and `writeln!` in `if let` scrutinees in Rust Edition 2024.](https://github.com/rust-lang/rust/pull/145342) This applies [Rust Edition 2024's `if let` temporary scope rules](https://doc.rust-lang.org/edition-guide/rust-2024/temporary-if-let-scope.html) to these temporaries, which previously could live past the `if` expression regardless of Edition.
|
||||
- [Invalid numeric literal suffixes in tuple indexing, tuple struct indexing, and struct field name positions are now correctly rejected](https://github.com/rust-lang/rust/pull/145463)
|
||||
- [Closures marked with the keyword `static` are now syntactically invalid](https://github.com/rust-lang/rust/pull/145604)
|
||||
- [Shebangs inside `--cfg` and `--check-cfg` arguments are no longer allowed](https://github.com/rust-lang/rust/pull/146211)
|
||||
- [Add future incompatibility lint for temporary lifetime shortening in Rust 1.92](https://github.com/rust-lang/rust/pull/147056)
|
||||
|
||||
Cargo compatibility notes:
|
||||
|
||||
- `cargo publish` no longer keeps `.crate` tarballs as final build artifacts
|
||||
when `build.build-dir` is set. These tarballs were previously included due to
|
||||
an oversight and are now treated as intermediate artifacts.
|
||||
To get `.crate` tarballs as final artifacts, use `cargo package`.
|
||||
In a future version, this change will apply regardless of `build.build-dir`.
|
||||
[#15910](https://github.com/rust-lang/cargo/pull/15910)
|
||||
- Adjust Cargo messages to match rustc diagnostic style.
|
||||
This changes some of the terminal colors used by Cargo messages.
|
||||
[#15928](https://github.com/rust-lang/cargo/pull/15928)
|
||||
- Tools and projects relying on the
|
||||
[internal details of Cargo's `build-dir`](https://doc.rust-lang.org/cargo/reference/build-cache.html)
|
||||
may not work for users changing their `build-dir` layout.
|
||||
For those doing so, we'd recommend proactively testing these cases
|
||||
particularly as we are considering changing the default location of the `build-dir` in the future
|
||||
([cargo#16147](https://github.com/rust-lang/cargo/issues/16147)).
|
||||
If you can't migrate off of Cargo's internal details,
|
||||
we'd like to learn more about your use case as we prepare to change the layout of the `build-dir`
|
||||
([cargo#15010](https://github.com/rust-lang/cargo/issues/15010)).
|
||||
|
||||
<a id="1.91.0-Internal-Changes"></a>
|
||||
|
||||
Internal Changes
|
||||
----------------
|
||||
|
||||
These changes do not affect any public interfaces of Rust, but they represent
|
||||
significant improvements to the performance or internals of rustc and related
|
||||
tools.
|
||||
|
||||
- [Update to LLVM 21](https://github.com/rust-lang/rust/pull/143684)
|
||||
|
||||
|
||||
Version 1.90.0 (2025-09-18)
|
||||
===========================
|
||||
|
||||
|
|
|
|||
|
|
@ -3680,6 +3680,9 @@ pub struct TraitImplHeader {
|
|||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
|
||||
pub struct FnContract {
|
||||
/// Declarations of variables accessible both in the `requires` and
|
||||
/// `ensures` clauses.
|
||||
pub declarations: ThinVec<Stmt>,
|
||||
pub requires: Option<Box<Expr>>,
|
||||
pub ensures: Option<Box<Expr>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break }
|
||||
}
|
||||
|
||||
fn lower_stmts(
|
||||
pub(super) fn lower_stmts(
|
||||
&mut self,
|
||||
mut ast_stmts: &[Stmt],
|
||||
) -> (&'hir [hir::Stmt<'hir>], Option<&'hir hir::Expr<'hir>>) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>,
|
||||
contract: &rustc_ast::FnContract,
|
||||
) -> rustc_hir::Expr<'hir> {
|
||||
// The order in which things are lowered is important! I.e to
|
||||
// refer to variables in contract_decls from postcond/precond,
|
||||
// we must lower it first!
|
||||
let contract_decls = self.lower_stmts(&contract.declarations).0;
|
||||
|
||||
match (&contract.requires, &contract.ensures) {
|
||||
(Some(req), Some(ens)) => {
|
||||
// Lower the fn contract, which turns:
|
||||
|
|
@ -27,6 +32,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// into:
|
||||
//
|
||||
// let __postcond = if contract_checks {
|
||||
// CONTRACT_DECLARATIONS;
|
||||
// contract_check_requires(PRECOND);
|
||||
// Some(|ret_val| POSTCOND)
|
||||
// } else {
|
||||
|
|
@ -45,8 +51,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let precond = self.lower_precond(req);
|
||||
let postcond_checker = self.lower_postcond_checker(ens);
|
||||
|
||||
let contract_check =
|
||||
self.lower_contract_check_with_postcond(Some(precond), postcond_checker);
|
||||
let contract_check = self.lower_contract_check_with_postcond(
|
||||
contract_decls,
|
||||
Some(precond),
|
||||
postcond_checker,
|
||||
);
|
||||
|
||||
let wrapped_body =
|
||||
self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
|
||||
|
|
@ -68,15 +77,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// let ret = { body };
|
||||
//
|
||||
// if contract_checks {
|
||||
// CONTRACT_DECLARATIONS;
|
||||
// contract_check_ensures(__postcond, ret)
|
||||
// } else {
|
||||
// ret
|
||||
// }
|
||||
// }
|
||||
|
||||
let postcond_checker = self.lower_postcond_checker(ens);
|
||||
let contract_check =
|
||||
self.lower_contract_check_with_postcond(None, postcond_checker);
|
||||
self.lower_contract_check_with_postcond(contract_decls, None, postcond_checker);
|
||||
|
||||
let wrapped_body =
|
||||
self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
|
||||
|
|
@ -91,12 +100,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
//
|
||||
// {
|
||||
// if contracts_checks {
|
||||
// CONTRACT_DECLARATIONS;
|
||||
// contract_requires(PRECOND);
|
||||
// }
|
||||
// body
|
||||
// }
|
||||
let precond = self.lower_precond(req);
|
||||
let precond_check = self.lower_contract_check_just_precond(precond);
|
||||
let precond_check = self.lower_contract_check_just_precond(contract_decls, precond);
|
||||
|
||||
let body = self.arena.alloc(body(self));
|
||||
|
||||
|
|
@ -145,9 +155,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
fn lower_contract_check_just_precond(
|
||||
&mut self,
|
||||
contract_decls: &'hir [rustc_hir::Stmt<'hir>],
|
||||
precond: rustc_hir::Stmt<'hir>,
|
||||
) -> rustc_hir::Stmt<'hir> {
|
||||
let stmts = self.arena.alloc_from_iter([precond].into_iter());
|
||||
let stmts = self
|
||||
.arena
|
||||
.alloc_from_iter(contract_decls.into_iter().map(|d| *d).chain([precond].into_iter()));
|
||||
|
||||
let then_block_stmts = self.block_all(precond.span, stmts, None);
|
||||
let then_block = self.arena.alloc(self.expr_block(&then_block_stmts));
|
||||
|
|
@ -164,10 +177,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
fn lower_contract_check_with_postcond(
|
||||
&mut self,
|
||||
contract_decls: &'hir [rustc_hir::Stmt<'hir>],
|
||||
precond: Option<rustc_hir::Stmt<'hir>>,
|
||||
postcond_checker: &'hir rustc_hir::Expr<'hir>,
|
||||
) -> &'hir rustc_hir::Expr<'hir> {
|
||||
let stmts = self.arena.alloc_from_iter(precond.into_iter());
|
||||
let stmts = self
|
||||
.arena
|
||||
.alloc_from_iter(contract_decls.into_iter().map(|d| *d).chain(precond.into_iter()));
|
||||
let span = match precond {
|
||||
Some(precond) => precond.span,
|
||||
None => postcond_checker.span,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_hir::{HirId, Target, find_attr};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
|
@ -62,13 +63,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> {
|
||||
ensure_sufficient_stack(|| {
|
||||
let mut span = self.lower_span(e.span);
|
||||
match &e.kind {
|
||||
// Parenthesis expression does not have a HirId and is handled specially.
|
||||
ExprKind::Paren(ex) => {
|
||||
let mut ex = self.lower_expr_mut(ex);
|
||||
// Include parens in span, but only if it is a super-span.
|
||||
if e.span.contains(ex.span) {
|
||||
ex.span = self.lower_span(e.span);
|
||||
ex.span = self.lower_span(e.span.with_ctxt(ex.span.ctxt()));
|
||||
}
|
||||
// Merge attributes into the inner expression.
|
||||
if !e.attrs.is_empty() {
|
||||
|
|
@ -287,7 +289,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
self.lower_span(*brackets_span),
|
||||
),
|
||||
ExprKind::Range(e1, e2, lims) => {
|
||||
self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), *lims)
|
||||
span = self.mark_span_with_reason(DesugaringKind::RangeExpr, span, None);
|
||||
self.lower_expr_range(span, e1.as_deref(), e2.as_deref(), *lims)
|
||||
}
|
||||
ExprKind::Underscore => {
|
||||
let guar = self.dcx().emit_err(UnderscoreExprLhsAssign { span: e.span });
|
||||
|
|
@ -379,7 +382,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span),
|
||||
};
|
||||
|
||||
hir::Expr { hir_id: expr_hir_id, kind, span: self.lower_span(e.span) }
|
||||
hir::Expr { hir_id: expr_hir_id, kind, span }
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -461,7 +464,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
for (idx, arg) in args.iter().cloned().enumerate() {
|
||||
if legacy_args_idx.contains(&idx) {
|
||||
let node_id = self.next_node_id();
|
||||
self.create_def(node_id, None, DefKind::AnonConst, f.span);
|
||||
self.create_def(
|
||||
node_id,
|
||||
None,
|
||||
DefKind::AnonConst,
|
||||
DefPathData::LateAnonConst,
|
||||
f.span,
|
||||
);
|
||||
let mut visitor = WillCreateDefIdsVisitor {};
|
||||
let const_value = if let ControlFlow::Break(span) = visitor.visit_expr(&arg) {
|
||||
Box::new(Expr {
|
||||
|
|
@ -1233,13 +1242,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let rhs = self.lower_expr(rhs);
|
||||
|
||||
// Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`.
|
||||
let destructure_let = self.stmt_let_pat(
|
||||
None,
|
||||
whole_span,
|
||||
Some(rhs),
|
||||
pat,
|
||||
hir::LocalSource::AssignDesugar(self.lower_span(eq_sign_span)),
|
||||
);
|
||||
let destructure_let =
|
||||
self.stmt_let_pat(None, whole_span, Some(rhs), pat, hir::LocalSource::AssignDesugar);
|
||||
|
||||
// `a = lhs1; b = lhs2;`.
|
||||
let stmts = self.arena.alloc_from_iter(std::iter::once(destructure_let).chain(assignments));
|
||||
|
|
@ -1475,7 +1479,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
|
||||
let e1 = self.lower_expr_mut(e1);
|
||||
let e2 = self.lower_expr_mut(e2);
|
||||
let fn_path = hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, self.lower_span(span));
|
||||
let fn_path = hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, span);
|
||||
let fn_expr = self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path)));
|
||||
hir::ExprKind::Call(fn_expr, arena_vec![self; e1, e2])
|
||||
}
|
||||
|
|
@ -1552,14 +1556,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
)
|
||||
}))
|
||||
.map(|(s, e)| {
|
||||
let span = self.lower_span(e.span);
|
||||
let span = self.mark_span_with_reason(DesugaringKind::RangeExpr, span, None);
|
||||
let expr = self.lower_expr(e);
|
||||
let ident = Ident::new(s, self.lower_span(e.span));
|
||||
self.expr_field(ident, expr, e.span)
|
||||
let ident = Ident::new(s, span);
|
||||
self.expr_field(ident, expr, span)
|
||||
}),
|
||||
);
|
||||
|
||||
hir::ExprKind::Struct(
|
||||
self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))),
|
||||
self.arena.alloc(hir::QPath::LangItem(lang_item, span)),
|
||||
fields,
|
||||
hir::StructTailExpr::None,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ use rustc_data_structures::tagged_ptr::TaggedRef;
|
|||
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle};
|
||||
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_hir::definitions::{DefPathData, DisambiguatorState};
|
||||
use rustc_hir::lints::DelayedLint;
|
||||
use rustc_hir::{
|
||||
self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LifetimeSource,
|
||||
|
|
@ -93,6 +94,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
|||
struct LoweringContext<'a, 'hir> {
|
||||
tcx: TyCtxt<'hir>,
|
||||
resolver: &'a mut ResolverAstLowering,
|
||||
disambiguator: DisambiguatorState,
|
||||
|
||||
/// Used to allocate HIR nodes.
|
||||
arena: &'hir hir::Arena<'hir>,
|
||||
|
|
@ -155,6 +157,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// Pseudo-globals.
|
||||
tcx,
|
||||
resolver,
|
||||
disambiguator: DisambiguatorState::new(),
|
||||
arena: tcx.hir_arena,
|
||||
|
||||
// HirId handling.
|
||||
|
|
@ -546,6 +549,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
node_id: ast::NodeId,
|
||||
name: Option<Symbol>,
|
||||
def_kind: DefKind,
|
||||
def_path_data: DefPathData,
|
||||
span: Span,
|
||||
) -> LocalDefId {
|
||||
let parent = self.current_hir_id_owner.def_id;
|
||||
|
|
@ -561,7 +565,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let def_id = self
|
||||
.tcx
|
||||
.at(span)
|
||||
.create_def(parent, name, def_kind, None, &mut self.resolver.disambiguator)
|
||||
.create_def(parent, name, def_kind, Some(def_path_data), &mut self.disambiguator)
|
||||
.def_id();
|
||||
|
||||
debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
|
||||
|
|
@ -846,6 +850,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
param,
|
||||
Some(kw::UnderscoreLifetime),
|
||||
DefKind::LifetimeParam,
|
||||
DefPathData::DesugaredAnonymousLifetime,
|
||||
ident.span,
|
||||
);
|
||||
debug!(?_def_id);
|
||||
|
|
@ -2290,7 +2295,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// We're lowering a const argument that was originally thought to be a type argument,
|
||||
// so the def collector didn't create the def ahead of time. That's why we have to do
|
||||
// it here.
|
||||
let def_id = self.create_def(node_id, None, DefKind::AnonConst, span);
|
||||
let def_id = self.create_def(
|
||||
node_id,
|
||||
None,
|
||||
DefKind::AnonConst,
|
||||
DefPathData::LateAnonConst,
|
||||
span,
|
||||
);
|
||||
let hir_id = self.lower_node_id(node_id);
|
||||
|
||||
let path_expr = Expr {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::sync::Arc;
|
|||
use rustc_ast::*;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_hir::{self as hir, LangItem, Target};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_span::source_map::{Spanned, respan};
|
||||
|
|
@ -527,7 +528,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// We're generating a range end that didn't exist in the AST,
|
||||
// so the def collector didn't create the def ahead of time. That's why we have to do
|
||||
// it here.
|
||||
let def_id = self.create_def(node_id, None, DefKind::AnonConst, span);
|
||||
let def_id =
|
||||
self.create_def(node_id, None, DefKind::AnonConst, DefPathData::LateAnonConst, span);
|
||||
let hir_id = self.lower_node_id(node_id);
|
||||
|
||||
let unstable_span = self.mark_span_with_reason(
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ impl Printer {
|
|||
}
|
||||
}
|
||||
|
||||
// This is is where `BoxMarker`s are produced.
|
||||
// This is where `BoxMarker`s are produced.
|
||||
fn scan_begin(&mut self, token: BeginToken) -> BoxMarker {
|
||||
if self.scan_stack.is_empty() {
|
||||
self.left_total = 1;
|
||||
|
|
@ -310,7 +310,7 @@ impl Printer {
|
|||
BoxMarker
|
||||
}
|
||||
|
||||
// This is is where `BoxMarker`s are consumed.
|
||||
// This is where `BoxMarker`s are consumed.
|
||||
fn scan_end(&mut self, b: BoxMarker) {
|
||||
if self.scan_stack.is_empty() {
|
||||
self.print_end();
|
||||
|
|
|
|||
|
|
@ -326,7 +326,8 @@ pub fn parse_cfg_attr(
|
|||
}) {
|
||||
Ok(r) => return Some(r),
|
||||
Err(e) => {
|
||||
let suggestions = CFG_ATTR_TEMPLATE.suggestions(cfg_attr.style, sym::cfg_attr);
|
||||
let suggestions =
|
||||
CFG_ATTR_TEMPLATE.suggestions(Some(cfg_attr.style), sym::cfg_attr);
|
||||
e.with_span_suggestions(
|
||||
cfg_attr.span,
|
||||
"must be of the form",
|
||||
|
|
@ -356,7 +357,7 @@ pub fn parse_cfg_attr(
|
|||
template: CFG_ATTR_TEMPLATE,
|
||||
attribute: AttrPath::from_ast(&cfg_attr.get_normal_item().path),
|
||||
reason,
|
||||
attr_style: cfg_attr.style,
|
||||
suggestions: CFG_ATTR_TEMPLATE.suggestions(Some(cfg_attr.style), sym::cfg_attr),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -388,6 +389,7 @@ fn parse_cfg_attr_internal<'a>(
|
|||
let cfg_predicate = AttributeParser::parse_single_args(
|
||||
sess,
|
||||
attribute.span,
|
||||
attribute.get_normal_item().span(),
|
||||
attribute.style,
|
||||
AttrPath {
|
||||
segments: attribute
|
||||
|
|
|
|||
|
|
@ -56,8 +56,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
|
|||
}
|
||||
}
|
||||
ArgParser::NameValue(_) => {
|
||||
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(cx.attr_style, "inline");
|
||||
let suggestions = cx.suggestions();
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -71,8 +71,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
|
|||
// Specifically `#[link = "dl"]` is accepted with a FCW
|
||||
// For more information, see https://github.com/rust-lang/rust/pull/143193
|
||||
ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => {
|
||||
let suggestions = <Self as CombineAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(cx.attr_style, "link");
|
||||
let suggestions = cx.suggestions();
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use rustc_ast::AttrStyle;
|
||||
use rustc_errors::DiagArgValue;
|
||||
use rustc_hir::attrs::MacroUseArgs;
|
||||
|
||||
|
|
@ -102,7 +101,7 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
|
|||
}
|
||||
}
|
||||
ArgParser::NameValue(_) => {
|
||||
let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use);
|
||||
let suggestions = cx.suggestions();
|
||||
cx.emit_err(IllFormedAttributeInputLint {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
|
|
@ -149,19 +148,14 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
|
|||
]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let suggestions = || {
|
||||
<Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(AttrStyle::Inner, "macro_export")
|
||||
};
|
||||
let local_inner_macros = match args {
|
||||
ArgParser::NoArgs => false,
|
||||
ArgParser::List(list) => {
|
||||
let Some(l) = list.single() else {
|
||||
let span = cx.attr_span;
|
||||
let suggestions = cx.suggestions();
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::InvalidMacroExportArguments {
|
||||
suggestions: suggestions(),
|
||||
},
|
||||
AttributeLintKind::InvalidMacroExportArguments { suggestions },
|
||||
span,
|
||||
);
|
||||
return None;
|
||||
|
|
@ -170,10 +164,9 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
|
|||
Some(sym::local_inner_macros) => true,
|
||||
_ => {
|
||||
let span = cx.attr_span;
|
||||
let suggestions = cx.suggestions();
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::InvalidMacroExportArguments {
|
||||
suggestions: suggestions(),
|
||||
},
|
||||
AttributeLintKind::InvalidMacroExportArguments { suggestions },
|
||||
span,
|
||||
);
|
||||
return None;
|
||||
|
|
@ -182,7 +175,7 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
|
|||
}
|
||||
ArgParser::NameValue(_) => {
|
||||
let span = cx.attr_span;
|
||||
let suggestions = suggestions();
|
||||
let suggestions = cx.suggestions();
|
||||
cx.emit_err(IllFormedAttributeInputLint {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
|
|||
Some(value_str)
|
||||
}
|
||||
ArgParser::List(_) => {
|
||||
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(cx.attr_style, "must_use");
|
||||
let suggestions = cx.suggestions();
|
||||
cx.emit_err(IllFormedAttributeInputLint {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
|
|||
ArgParser::NoArgs => None,
|
||||
ArgParser::NameValue(name_value) => {
|
||||
let Some(str_value) = name_value.value_as_str() else {
|
||||
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(cx.attr_style, "ignore");
|
||||
let suggestions = cx.suggestions();
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::IllFormedAttributeInput { suggestions },
|
||||
|
|
@ -32,8 +31,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
|
|||
Some(str_value)
|
||||
}
|
||||
ArgParser::List(_) => {
|
||||
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(cx.attr_style, "ignore");
|
||||
let suggestions = cx.suggestions();
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -337,8 +337,16 @@ pub struct Late;
|
|||
/// Gives [`AttributeParser`]s enough information to create errors, for example.
|
||||
pub struct AcceptContext<'f, 'sess, S: Stage> {
|
||||
pub(crate) shared: SharedContext<'f, 'sess, S>,
|
||||
/// The span of the attribute currently being parsed
|
||||
|
||||
/// The outer span of the attribute currently being parsed
|
||||
/// #[attribute(...)]
|
||||
/// ^^^^^^^^^^^^^^^^^ outer span
|
||||
/// For attributes in `cfg_attr`, the outer span and inner spans are equal.
|
||||
pub(crate) attr_span: Span,
|
||||
/// The inner span of the attribute currently being parsed
|
||||
/// #[attribute(...)]
|
||||
/// ^^^^^^^^^^^^^^ inner span
|
||||
pub(crate) inner_span: Span,
|
||||
|
||||
/// Whether it is an inner or outer attribute
|
||||
pub(crate) attr_style: AttrStyle,
|
||||
|
|
@ -427,7 +435,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
|
||||
}),
|
||||
},
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -438,7 +446,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedIntegerLiteral,
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -449,7 +457,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedList,
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -460,7 +468,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedNoArgs,
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -472,7 +480,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedIdentifier,
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -485,7 +493,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedNameValue(name),
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -497,7 +505,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::DuplicateKey(key),
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -510,7 +518,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::UnexpectedLiteral,
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -521,7 +529,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedSingleArgument,
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -532,7 +540,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -552,7 +560,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
strings: false,
|
||||
list: false,
|
||||
},
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -573,7 +581,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
strings: false,
|
||||
list: true,
|
||||
},
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -593,7 +601,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
strings: true,
|
||||
list: false,
|
||||
},
|
||||
attr_style: self.attr_style,
|
||||
suggestions: self.suggestions(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -605,6 +613,13 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
span,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn suggestions(&self) -> Vec<String> {
|
||||
// If the outer and inner spans are equal, we are parsing an attribute from `cfg_attr`,
|
||||
// So don't display an attribute style in the suggestions
|
||||
let style = (self.attr_span != self.inner_span).then_some(self.attr_style);
|
||||
self.template.suggestions(style, &self.attr_path)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ impl<'sess> AttributeParser<'sess, Early> {
|
|||
Self::parse_single_args(
|
||||
sess,
|
||||
attr.span,
|
||||
normal_attr.item.span(),
|
||||
attr.style,
|
||||
path.get_attribute_path(),
|
||||
target_span,
|
||||
|
|
@ -159,6 +160,7 @@ impl<'sess> AttributeParser<'sess, Early> {
|
|||
pub fn parse_single_args<T, I>(
|
||||
sess: &'sess Session,
|
||||
attr_span: Span,
|
||||
inner_span: Span,
|
||||
attr_style: AttrStyle,
|
||||
attr_path: AttrPath,
|
||||
target_span: Span,
|
||||
|
|
@ -186,6 +188,7 @@ impl<'sess> AttributeParser<'sess, Early> {
|
|||
},
|
||||
},
|
||||
attr_span,
|
||||
inner_span,
|
||||
attr_style,
|
||||
template,
|
||||
attr_path,
|
||||
|
|
@ -305,6 +308,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
emit_lint: &mut emit_lint,
|
||||
},
|
||||
attr_span: lower_span(attr.span),
|
||||
inner_span: lower_span(attr.get_normal_item().span()),
|
||||
attr_style: attr.style,
|
||||
template: &accept.template,
|
||||
attr_path: path.get_attribute_path(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::num::IntErrorKind;
|
||||
|
||||
use rustc_ast::{self as ast, AttrStyle, Path};
|
||||
use rustc_ast::{self as ast, Path};
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{
|
||||
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
|
||||
|
|
@ -613,10 +613,10 @@ pub(crate) enum AttributeParseErrorReason<'a> {
|
|||
pub(crate) struct AttributeParseError<'a> {
|
||||
pub(crate) span: Span,
|
||||
pub(crate) attr_span: Span,
|
||||
pub(crate) attr_style: AttrStyle,
|
||||
pub(crate) template: AttributeTemplate,
|
||||
pub(crate) attribute: AttrPath,
|
||||
pub(crate) reason: AttributeParseErrorReason<'a>,
|
||||
pub(crate) suggestions: Vec<String>,
|
||||
}
|
||||
|
||||
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
|
||||
|
|
@ -752,16 +752,15 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
|
|||
if let Some(link) = self.template.docs {
|
||||
diag.note(format!("for more information, visit <{link}>"));
|
||||
}
|
||||
let suggestions = self.template.suggestions(self.attr_style, &name);
|
||||
|
||||
diag.span_suggestions(
|
||||
self.attr_span,
|
||||
if suggestions.len() == 1 {
|
||||
if self.suggestions.len() == 1 {
|
||||
"must be of the form"
|
||||
} else {
|
||||
"try changing it to one of the following valid forms of the attribute"
|
||||
},
|
||||
suggestions,
|
||||
self.suggestions,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ impl AttrProcMacro for ExpandRequires {
|
|||
annotation: TokenStream,
|
||||
annotated: TokenStream,
|
||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||
expand_requires_tts(ecx, span, annotation, annotated)
|
||||
expand_contract_clause_tts(ecx, span, annotation, annotated, kw::ContractRequires)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ impl AttrProcMacro for ExpandEnsures {
|
|||
annotation: TokenStream,
|
||||
annotated: TokenStream,
|
||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||
expand_ensures_tts(ecx, span, annotation, annotated)
|
||||
expand_contract_clause_tts(ecx, span, annotation, annotated, kw::ContractEnsures)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -130,42 +130,17 @@ fn expand_contract_clause(
|
|||
Ok(new_tts)
|
||||
}
|
||||
|
||||
fn expand_requires_tts(
|
||||
fn expand_contract_clause_tts(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
attr_span: Span,
|
||||
annotation: TokenStream,
|
||||
annotated: TokenStream,
|
||||
clause_keyword: rustc_span::Symbol,
|
||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||
let feature_span = ecx.with_def_site_ctxt(attr_span);
|
||||
expand_contract_clause(ecx, attr_span, annotated, |new_tts| {
|
||||
new_tts.push_tree(TokenTree::Token(
|
||||
token::Token::from_ast_ident(Ident::new(kw::ContractRequires, feature_span)),
|
||||
Spacing::Joint,
|
||||
));
|
||||
new_tts.push_tree(TokenTree::Token(
|
||||
token::Token::new(token::TokenKind::OrOr, attr_span),
|
||||
Spacing::Alone,
|
||||
));
|
||||
new_tts.push_tree(TokenTree::Delimited(
|
||||
DelimSpan::from_single(attr_span),
|
||||
DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden),
|
||||
token::Delimiter::Brace,
|
||||
annotation,
|
||||
));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn expand_ensures_tts(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
attr_span: Span,
|
||||
annotation: TokenStream,
|
||||
annotated: TokenStream,
|
||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||
let feature_span = ecx.with_def_site_ctxt(attr_span);
|
||||
expand_contract_clause(ecx, attr_span, annotated, |new_tts| {
|
||||
new_tts.push_tree(TokenTree::Token(
|
||||
token::Token::from_ast_ident(Ident::new(kw::ContractEnsures, feature_span)),
|
||||
token::Token::from_ast_ident(Ident::new(clause_keyword, feature_span)),
|
||||
Spacing::Joint,
|
||||
));
|
||||
new_tts.push_tree(TokenTree::Delimited(
|
||||
|
|
|
|||
|
|
@ -671,18 +671,7 @@ pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box<OngoingCodegen> {
|
|||
}
|
||||
.to_owned();
|
||||
|
||||
let cgus = if tcx.sess.opts.output_types.should_codegen() {
|
||||
tcx.collect_and_partition_mono_items(()).codegen_units
|
||||
} else {
|
||||
// If only `--emit metadata` is used, we shouldn't perform any codegen.
|
||||
// Also `tcx.collect_and_partition_mono_items` may panic in that case.
|
||||
return Box::new(OngoingCodegen {
|
||||
modules: vec![],
|
||||
allocator_module: None,
|
||||
crate_info: CrateInfo::new(tcx, target_cpu),
|
||||
concurrency_limiter: ConcurrencyLimiter::new(0),
|
||||
});
|
||||
};
|
||||
let cgus = tcx.collect_and_partition_mono_items(()).codegen_units;
|
||||
|
||||
if tcx.dep_graph.is_fully_enabled() {
|
||||
for cgu in cgus {
|
||||
|
|
|
|||
|
|
@ -33,9 +33,7 @@ fn create_jit_module(tcx: TyCtxt<'_>) -> (UnwindModule<JITModule>, CodegenCx) {
|
|||
}
|
||||
|
||||
pub(crate) fn run_jit(tcx: TyCtxt<'_>, jit_args: Vec<String>) -> ! {
|
||||
if !tcx.sess.opts.output_types.should_codegen() {
|
||||
tcx.dcx().fatal("JIT mode doesn't work with `cargo check`");
|
||||
}
|
||||
// FIXME error on check mode or crate types other than bin in CodegenBackend::init()
|
||||
|
||||
if !tcx.crate_types().contains(&rustc_session::config::CrateType::Executable) {
|
||||
tcx.dcx().fatal("can't jit non-executable crate");
|
||||
|
|
|
|||
|
|
@ -39,13 +39,11 @@ trait ArgAttributesExt {
|
|||
const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] =
|
||||
[(ArgAttribute::InReg, llvm::AttributeKind::InReg)];
|
||||
|
||||
const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 6] = [
|
||||
const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 4] = [
|
||||
(ArgAttribute::NoAlias, llvm::AttributeKind::NoAlias),
|
||||
(ArgAttribute::CapturesAddress, llvm::AttributeKind::CapturesAddress),
|
||||
(ArgAttribute::NonNull, llvm::AttributeKind::NonNull),
|
||||
(ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly),
|
||||
(ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef),
|
||||
(ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly),
|
||||
];
|
||||
|
||||
fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 8]> {
|
||||
|
|
@ -81,15 +79,23 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'
|
|||
}
|
||||
for (attr, llattr) in OPTIMIZATION_ATTRIBUTES {
|
||||
if regular.contains(attr) {
|
||||
// captures(...) is only available since LLVM 21.
|
||||
if (attr == ArgAttribute::CapturesReadOnly || attr == ArgAttribute::CapturesAddress)
|
||||
&& llvm_util::get_version() < (21, 0, 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
attrs.push(llattr.create_attr(cx.llcx));
|
||||
}
|
||||
}
|
||||
// captures(...) is only available since LLVM 21.
|
||||
if (21, 0, 0) <= llvm_util::get_version() {
|
||||
const CAPTURES_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 3] = [
|
||||
(ArgAttribute::CapturesNone, llvm::AttributeKind::CapturesNone),
|
||||
(ArgAttribute::CapturesAddress, llvm::AttributeKind::CapturesAddress),
|
||||
(ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly),
|
||||
];
|
||||
for (attr, llattr) in CAPTURES_ATTRIBUTES {
|
||||
if regular.contains(attr) {
|
||||
attrs.push(llattr.create_attr(cx.llcx));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if cx.tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::MEMORY) {
|
||||
// If we're not optimising, *but* memory sanitizer is on, emit noundef, since it affects
|
||||
// memory sanitizer's behavior.
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ impl GlobalFileTable {
|
|||
for file in all_files {
|
||||
raw_file_table.entry(file.stable_id).or_insert_with(|| {
|
||||
file.name
|
||||
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
|
||||
.for_scope(tcx.sess, RemapPathScopeComponents::COVERAGE)
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
});
|
||||
|
|
@ -147,7 +147,7 @@ impl GlobalFileTable {
|
|||
.sess
|
||||
.opts
|
||||
.working_dir
|
||||
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
|
||||
.for_scope(tcx.sess, RemapPathScopeComponents::COVERAGE)
|
||||
.to_string_lossy();
|
||||
table.push(base_dir.as_ref());
|
||||
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ pub(crate) enum AttributeKind {
|
|||
DeadOnUnwind = 43,
|
||||
DeadOnReturn = 44,
|
||||
CapturesReadOnly = 45,
|
||||
CapturesNone = 46,
|
||||
}
|
||||
|
||||
/// LLVMIntPredicate
|
||||
|
|
|
|||
|
|
@ -1274,13 +1274,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
})
|
||||
.expect("failed to spawn helper thread");
|
||||
|
||||
let ol =
|
||||
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
|
||||
// If we know that we won’t be doing codegen, create target machines without optimisation.
|
||||
config::OptLevel::No
|
||||
} else {
|
||||
tcx.backend_optimization_level(())
|
||||
};
|
||||
let ol = tcx.backend_optimization_level(());
|
||||
let backend_features = tcx.global_backend_features(());
|
||||
|
||||
let remark_dir = if let Some(ref dir) = sess.opts.unstable_opts.remark_dir {
|
||||
|
|
|
|||
|
|
@ -684,17 +684,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
|||
tcx: TyCtxt<'_>,
|
||||
target_cpu: String,
|
||||
) -> OngoingCodegen<B> {
|
||||
// Skip crate items and just output metadata in -Z no-codegen mode.
|
||||
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
|
||||
let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, None);
|
||||
|
||||
ongoing_codegen.codegen_finished(tcx);
|
||||
|
||||
ongoing_codegen.check_for_errors(tcx.sess);
|
||||
|
||||
return ongoing_codegen;
|
||||
}
|
||||
|
||||
if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() {
|
||||
// The target has no default cpu, but none is set explicitly
|
||||
tcx.dcx().emit_fatal(errors::CpuRequired);
|
||||
|
|
|
|||
|
|
@ -120,8 +120,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
| sym::atomic_singlethreadfence
|
||||
| sym::caller_location => {}
|
||||
_ => {
|
||||
span_bug!(span, "nullary intrinsic {name} must either be in a const block or explicitly opted out because it is inherently a runtime intrinsic
|
||||
");
|
||||
span_bug!(
|
||||
span,
|
||||
"Nullary intrinsic {name} must be called in a const block. \
|
||||
If you are seeing this message from code outside the standard library, the \
|
||||
unstable implementation details of the relevant intrinsic may have changed. \
|
||||
Consider using stable APIs instead. \
|
||||
If you are adding a new nullary intrinsic that is inherently a runtime \
|
||||
intrinsic, update this check."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
|||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::{self, OutputFilenames, PrintRequest};
|
||||
use rustc_session::config::{self, CrateType, OutputFilenames, PrintRequest};
|
||||
use rustc_span::Symbol;
|
||||
|
||||
use super::CodegenObject;
|
||||
|
|
@ -62,6 +62,18 @@ pub trait CodegenBackend {
|
|||
}
|
||||
}
|
||||
|
||||
fn supported_crate_types(&self, _sess: &Session) -> Vec<CrateType> {
|
||||
vec![
|
||||
CrateType::Executable,
|
||||
CrateType::Dylib,
|
||||
CrateType::Rlib,
|
||||
CrateType::Staticlib,
|
||||
CrateType::Cdylib,
|
||||
CrateType::ProcMacro,
|
||||
CrateType::Sdylib,
|
||||
]
|
||||
}
|
||||
|
||||
fn print_passes(&self) {}
|
||||
|
||||
fn print_version(&self) {}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use rustc_hir::def::DefKind;
|
|||
use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo, ReportedErrorInfo};
|
||||
use rustc_middle::mir::{self, ConstAlloc, ConstValue};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::HasTypingEnv;
|
||||
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, throw_inval};
|
||||
|
|
@ -24,13 +24,11 @@ use crate::interpret::{
|
|||
};
|
||||
use crate::{CTRL_C_RECEIVED, errors};
|
||||
|
||||
// Returns a pointer to where the result lives
|
||||
#[instrument(level = "trace", skip(ecx, body))]
|
||||
fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
||||
fn setup_for_eval<'tcx>(
|
||||
ecx: &mut CompileTimeInterpCx<'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
body: &'tcx mir::Body<'tcx>,
|
||||
) -> InterpResult<'tcx, R> {
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> InterpResult<'tcx, (InternKind, MPlaceTy<'tcx>)> {
|
||||
let tcx = *ecx.tcx;
|
||||
assert!(
|
||||
cid.promoted.is_some()
|
||||
|
|
@ -46,7 +44,6 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
|||
"Unexpected DefKind: {:?}",
|
||||
ecx.tcx.def_kind(cid.instance.def_id())
|
||||
);
|
||||
let layout = ecx.layout_of(body.bound_return_ty().instantiate(tcx, cid.instance.args))?;
|
||||
assert!(layout.is_sized());
|
||||
|
||||
let intern_kind = if cid.promoted.is_some() {
|
||||
|
|
@ -58,12 +55,25 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
|||
}
|
||||
};
|
||||
|
||||
let ret = if let InternKind::Static(_) = intern_kind {
|
||||
create_static_alloc(ecx, cid.instance.def_id().expect_local(), layout)?
|
||||
let return_place = if let InternKind::Static(_) = intern_kind {
|
||||
create_static_alloc(ecx, cid.instance.def_id().expect_local(), layout)
|
||||
} else {
|
||||
ecx.allocate(layout, MemoryKind::Stack)?
|
||||
ecx.allocate(layout, MemoryKind::Stack)
|
||||
};
|
||||
|
||||
return_place.map(|ret| (intern_kind, ret))
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(ecx, body))]
|
||||
fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
||||
ecx: &mut CompileTimeInterpCx<'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
body: &'tcx mir::Body<'tcx>,
|
||||
) -> InterpResult<'tcx, R> {
|
||||
let tcx = *ecx.tcx;
|
||||
let layout = ecx.layout_of(body.bound_return_ty().instantiate(tcx, cid.instance.args))?;
|
||||
let (intern_kind, ret) = setup_for_eval(ecx, cid, layout)?;
|
||||
|
||||
trace!(
|
||||
"eval_body_using_ecx: pushing stack frame for global: {}{}",
|
||||
with_no_trimmed_paths!(ecx.tcx.def_path_str(cid.instance.def_id())),
|
||||
|
|
@ -87,6 +97,31 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
|||
}
|
||||
}
|
||||
|
||||
intern_and_validate(ecx, cid, intern_kind, ret)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(ecx))]
|
||||
fn eval_trivial_const_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
||||
ecx: &mut CompileTimeInterpCx<'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
val: ConstValue,
|
||||
ty: Ty<'tcx>,
|
||||
) -> InterpResult<'tcx, R> {
|
||||
let layout = ecx.layout_of(ty)?;
|
||||
let (intern_kind, return_place) = setup_for_eval(ecx, cid, layout)?;
|
||||
|
||||
let opty = ecx.const_val_to_op(val, ty, Some(layout))?;
|
||||
ecx.copy_op(&opty, &return_place)?;
|
||||
|
||||
intern_and_validate(ecx, cid, intern_kind, return_place)
|
||||
}
|
||||
|
||||
fn intern_and_validate<'tcx, R: InterpretationResult<'tcx>>(
|
||||
ecx: &mut CompileTimeInterpCx<'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
intern_kind: InternKind,
|
||||
ret: MPlaceTy<'tcx>,
|
||||
) -> InterpResult<'tcx, R> {
|
||||
// Intern the result
|
||||
let intern_result = intern_const_alloc_recursive(ecx, intern_kind, &ret);
|
||||
|
||||
|
|
@ -292,6 +327,9 @@ pub fn eval_to_const_value_raw_provider<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
|
||||
) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> {
|
||||
if let Some((value, _ty)) = tcx.trivial_const(key.value.instance.def_id()) {
|
||||
return Ok(value);
|
||||
}
|
||||
tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key))
|
||||
}
|
||||
|
||||
|
|
@ -368,10 +406,14 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
|
|||
// so we have to reject reading mutable global memory.
|
||||
CompileTimeMachine::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
|
||||
);
|
||||
let res = ecx.load_mir(cid.instance.def, cid.promoted);
|
||||
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body))
|
||||
.report_err()
|
||||
.map_err(|error| report_eval_error(&ecx, cid, error))
|
||||
|
||||
let result = if let Some((value, ty)) = tcx.trivial_const(def) {
|
||||
eval_trivial_const_using_ecx(&mut ecx, cid, value, ty)
|
||||
} else {
|
||||
ecx.load_mir(cid.instance.def, cid.promoted)
|
||||
.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body))
|
||||
};
|
||||
result.report_err().map_err(|error| report_eval_error(&ecx, cid, error))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ impl EnteredTraceSpan for tracing::span::EnteredSpan {
|
|||
/// ### `tracing_separate_thread` parameter
|
||||
///
|
||||
/// This macro was introduced to obtain better traces of Miri without impacting release performance.
|
||||
/// Miri saves traces using the the `tracing_chrome` `tracing::Layer` so that they can be visualized
|
||||
/// Miri saves traces using the `tracing_chrome` `tracing::Layer` so that they can be visualized
|
||||
/// in <https://ui.perfetto.dev>. To instruct `tracing_chrome` to put some spans on a separate trace
|
||||
/// thread/line than other spans when viewed in <https://ui.perfetto.dev>, you can pass
|
||||
/// `tracing_separate_thread = tracing::field::Empty` to the tracing macros. This is useful to
|
||||
|
|
|
|||
|
|
@ -686,7 +686,8 @@ fn print_crate_info(
|
|||
};
|
||||
let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess);
|
||||
let crate_name = passes::get_crate_name(sess, attrs);
|
||||
let crate_types = collect_crate_types(sess, attrs);
|
||||
let crate_types =
|
||||
collect_crate_types(sess, &codegen_backend.supported_crate_types(sess), attrs);
|
||||
for &style in &crate_types {
|
||||
let fname = rustc_session::output::filename_for_input(
|
||||
sess, style, crate_name, &t_outputs,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
annotate-snippets = "0.11"
|
||||
annotate-snippets = "0.12.7"
|
||||
anstream = "0.6.20"
|
||||
anstyle = "1.0.13"
|
||||
derive_setters = "0.1.6"
|
||||
|
|
|
|||
|
|
@ -5,32 +5,70 @@
|
|||
//!
|
||||
//! [annotate_snippets]: https://docs.rs/crate/annotate-snippets/
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::error::Report;
|
||||
use std::fmt::Debug;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::sync::Arc;
|
||||
|
||||
use annotate_snippets::{Renderer, Snippet};
|
||||
use rustc_error_messages::FluentArgs;
|
||||
use rustc_span::SourceFile;
|
||||
use annotate_snippets::renderer::DEFAULT_TERM_WIDTH;
|
||||
use annotate_snippets::{AnnotationKind, Group, Origin, Padding, Patch, Renderer, Snippet};
|
||||
use anstream::ColorChoice;
|
||||
use derive_setters::Setters;
|
||||
use rustc_data_structures::sync::IntoDynSyncSend;
|
||||
use rustc_error_messages::{FluentArgs, SpanLabel};
|
||||
use rustc_lint_defs::pluralize;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, FileName, Pos, SourceFile, Span};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::emitter::FileWithAnnotatedLines;
|
||||
use crate::emitter::{
|
||||
ConfusionType, Destination, MAX_SUGGESTIONS, OutputTheme, detect_confusion_type, is_different,
|
||||
normalize_whitespace, should_show_source_code,
|
||||
};
|
||||
use crate::registry::Registry;
|
||||
use crate::snippet::Line;
|
||||
use crate::translation::{Translator, to_fluent_args};
|
||||
use crate::{
|
||||
CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, Level, MultiSpan, Style, Subdiag,
|
||||
SuggestionStyle, TerminalUrl,
|
||||
};
|
||||
|
||||
/// Generates diagnostics using annotate-snippet
|
||||
#[derive(Setters)]
|
||||
pub struct AnnotateSnippetEmitter {
|
||||
source_map: Option<Arc<SourceMap>>,
|
||||
#[setters(skip)]
|
||||
dst: IntoDynSyncSend<Destination>,
|
||||
sm: Option<Arc<SourceMap>>,
|
||||
#[setters(skip)]
|
||||
translator: Translator,
|
||||
|
||||
/// If true, hides the longer explanation text
|
||||
short_message: bool,
|
||||
/// If true, will normalize line numbers with `LL` to prevent noise in UI test diffs.
|
||||
ui_testing: bool,
|
||||
ignored_directories_in_source_blocks: Vec<String>,
|
||||
diagnostic_width: Option<usize>,
|
||||
|
||||
macro_backtrace: bool,
|
||||
track_diagnostics: bool,
|
||||
terminal_url: TerminalUrl,
|
||||
theme: OutputTheme,
|
||||
}
|
||||
|
||||
impl Debug for AnnotateSnippetEmitter {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AnnotateSnippetEmitter")
|
||||
.field("short_message", &self.short_message)
|
||||
.field("ui_testing", &self.ui_testing)
|
||||
.field(
|
||||
"ignored_directories_in_source_blocks",
|
||||
&self.ignored_directories_in_source_blocks,
|
||||
)
|
||||
.field("diagnostic_width", &self.diagnostic_width)
|
||||
.field("macro_backtrace", &self.macro_backtrace)
|
||||
.field("track_diagnostics", &self.track_diagnostics)
|
||||
.field("terminal_url", &self.terminal_url)
|
||||
.field("theme", &self.theme)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Emitter for AnnotateSnippetEmitter {
|
||||
|
|
@ -38,6 +76,10 @@ impl Emitter for AnnotateSnippetEmitter {
|
|||
fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) {
|
||||
let fluent_args = to_fluent_args(diag.args.iter());
|
||||
|
||||
if self.track_diagnostics && diag.span.has_primary_spans() && !diag.span.is_dummy() {
|
||||
diag.children.insert(0, diag.emitted_at_sub_diag());
|
||||
}
|
||||
|
||||
let mut suggestions = diag.suggestions.unwrap_tag();
|
||||
self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
|
||||
|
||||
|
|
@ -55,12 +97,12 @@ impl Emitter for AnnotateSnippetEmitter {
|
|||
&diag.code,
|
||||
&diag.span,
|
||||
&diag.children,
|
||||
&suggestions,
|
||||
suggestions,
|
||||
);
|
||||
}
|
||||
|
||||
fn source_map(&self) -> Option<&SourceMap> {
|
||||
self.source_map.as_deref()
|
||||
self.sm.as_deref()
|
||||
}
|
||||
|
||||
fn should_show_explain(&self) -> bool {
|
||||
|
|
@ -70,128 +112,648 @@ impl Emitter for AnnotateSnippetEmitter {
|
|||
fn translator(&self) -> &Translator {
|
||||
&self.translator
|
||||
}
|
||||
|
||||
fn supports_color(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides the source string for the given `line` of `file`
|
||||
fn source_string(file: Arc<SourceFile>, line: &Line) -> String {
|
||||
file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Maps [`crate::Level`] to [`annotate_snippets::Level`]
|
||||
fn annotation_level_for_level(level: Level) -> annotate_snippets::Level {
|
||||
fn annotation_level_for_level(level: Level) -> annotate_snippets::level::Level<'static> {
|
||||
match level {
|
||||
Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => {
|
||||
annotate_snippets::Level::Error
|
||||
Level::Bug | Level::DelayedBug => {
|
||||
annotate_snippets::Level::ERROR.with_name("error: internal compiler error")
|
||||
}
|
||||
Level::ForceWarning | Level::Warning => annotate_snippets::Level::Warning,
|
||||
Level::Note | Level::OnceNote => annotate_snippets::Level::Note,
|
||||
Level::Help | Level::OnceHelp => annotate_snippets::Level::Help,
|
||||
// FIXME(#59346): Not sure how to map this level
|
||||
Level::FailureNote => annotate_snippets::Level::Error,
|
||||
Level::Fatal | Level::Error => annotate_snippets::level::ERROR,
|
||||
Level::ForceWarning | Level::Warning => annotate_snippets::Level::WARNING,
|
||||
Level::Note | Level::OnceNote => annotate_snippets::Level::NOTE,
|
||||
Level::Help | Level::OnceHelp => annotate_snippets::Level::HELP,
|
||||
Level::FailureNote => annotate_snippets::Level::NOTE.no_name(),
|
||||
Level::Allow => panic!("Should not call with Allow"),
|
||||
Level::Expect => panic!("Should not call with Expect"),
|
||||
}
|
||||
}
|
||||
|
||||
impl AnnotateSnippetEmitter {
|
||||
pub fn new(
|
||||
source_map: Option<Arc<SourceMap>>,
|
||||
translator: Translator,
|
||||
short_message: bool,
|
||||
macro_backtrace: bool,
|
||||
) -> Self {
|
||||
Self { source_map, translator, short_message, ui_testing: false, macro_backtrace }
|
||||
}
|
||||
|
||||
/// Allows to modify `Self` to enable or disable the `ui_testing` flag.
|
||||
///
|
||||
/// If this is set to true, line numbers will be normalized as `LL` in the output.
|
||||
pub fn ui_testing(mut self, ui_testing: bool) -> Self {
|
||||
self.ui_testing = ui_testing;
|
||||
self
|
||||
pub fn new(dst: Destination, translator: Translator) -> Self {
|
||||
Self {
|
||||
dst: IntoDynSyncSend(dst),
|
||||
sm: None,
|
||||
translator,
|
||||
short_message: false,
|
||||
ui_testing: false,
|
||||
ignored_directories_in_source_blocks: Vec::new(),
|
||||
diagnostic_width: None,
|
||||
macro_backtrace: false,
|
||||
track_diagnostics: false,
|
||||
terminal_url: TerminalUrl::No,
|
||||
theme: OutputTheme::Ascii,
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_messages_default(
|
||||
&mut self,
|
||||
level: &Level,
|
||||
messages: &[(DiagMessage, Style)],
|
||||
msgs: &[(DiagMessage, Style)],
|
||||
args: &FluentArgs<'_>,
|
||||
code: &Option<ErrCode>,
|
||||
msp: &MultiSpan,
|
||||
_children: &[Subdiag],
|
||||
_suggestions: &[CodeSuggestion],
|
||||
children: &[Subdiag],
|
||||
suggestions: Vec<CodeSuggestion>,
|
||||
) {
|
||||
let message = self.translator.translate_messages(messages, args);
|
||||
if let Some(source_map) = &self.source_map {
|
||||
// Make sure our primary file comes first
|
||||
let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() {
|
||||
if primary_span.is_dummy() {
|
||||
// FIXME(#59346): Not sure when this is the case and what
|
||||
// should be done if it happens
|
||||
return;
|
||||
} else {
|
||||
source_map.lookup_char_pos(primary_span.lo())
|
||||
}
|
||||
} else {
|
||||
// FIXME(#59346): Not sure when this is the case and what
|
||||
// should be done if it happens
|
||||
return;
|
||||
};
|
||||
let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp);
|
||||
if let Ok(pos) =
|
||||
annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name))
|
||||
{
|
||||
annotated_files.swap(0, pos);
|
||||
}
|
||||
// owned: file name, line source, line index, annotations
|
||||
type Owned = (String, String, usize, Vec<crate::snippet::Annotation>);
|
||||
let annotated_files: Vec<Owned> = annotated_files
|
||||
.into_iter()
|
||||
.flat_map(|annotated_file| {
|
||||
let file = annotated_file.file;
|
||||
annotated_file
|
||||
.lines
|
||||
.into_iter()
|
||||
.map(|line| {
|
||||
// Ensure the source file is present before we try
|
||||
// to load a string from it.
|
||||
// FIXME(#115869): support -Z ignore-directory-in-diagnostics-source-blocks
|
||||
source_map.ensure_source_file_source_present(&file);
|
||||
(
|
||||
format!("{}", source_map.filename_for_diagnostics(&file.name)),
|
||||
source_string(Arc::clone(&file), &line),
|
||||
line.line_index,
|
||||
line.annotations,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Owned>>()
|
||||
})
|
||||
.collect();
|
||||
let code = code.map(|code| code.to_string());
|
||||
let renderer = self.renderer();
|
||||
let annotation_level = annotation_level_for_level(*level);
|
||||
|
||||
let snippets =
|
||||
annotated_files.iter().map(|(file_name, source, line_index, annotations)| {
|
||||
Snippet::source(source)
|
||||
.line_start(*line_index)
|
||||
.origin(file_name)
|
||||
// FIXME(#59346): Not really sure when `fold` should be true or false
|
||||
.fold(false)
|
||||
.annotations(annotations.iter().map(|annotation| {
|
||||
annotation_level_for_level(*level)
|
||||
.span(annotation.start_col.display..annotation.end_col.display)
|
||||
.label(annotation.label.as_deref().unwrap_or_default())
|
||||
}))
|
||||
});
|
||||
let mut message = annotation_level_for_level(*level).title(&message).snippets(snippets);
|
||||
if let Some(code) = code.as_deref() {
|
||||
message = message.id(code)
|
||||
// If at least one portion of the message is styled, we need to
|
||||
// "pre-style" the message
|
||||
let mut title = if msgs.iter().any(|(_, style)| style != &crate::Style::NoStyle) {
|
||||
annotation_level
|
||||
.clone()
|
||||
.secondary_title(Cow::Owned(self.pre_style_msgs(msgs, *level, args)))
|
||||
} else {
|
||||
annotation_level.clone().primary_title(self.translator.translate_messages(msgs, args))
|
||||
};
|
||||
|
||||
if let Some(c) = code {
|
||||
title = title.id(c.to_string());
|
||||
if let TerminalUrl::Yes = self.terminal_url {
|
||||
title = title.id_url(format!("https://doc.rust-lang.org/error_codes/{c}.html"));
|
||||
}
|
||||
// FIXME(#59346): Figure out if we can _always_ print to stderr or not.
|
||||
// `emitter.rs` has the `Destination` enum that lists various possible output
|
||||
// destinations.
|
||||
let renderer = Renderer::plain().anonymized_line_numbers(self.ui_testing);
|
||||
eprintln!("{}", renderer.render(message))
|
||||
}
|
||||
// FIXME(#59346): Is it ok to return None if there's no source_map?
|
||||
|
||||
let mut report = vec![];
|
||||
let mut group = Group::with_title(title);
|
||||
|
||||
// If we don't have span information, emit and exit
|
||||
let Some(sm) = self.sm.as_ref() else {
|
||||
group = group.elements(children.iter().map(|c| {
|
||||
let msg = self.translator.translate_messages(&c.messages, args).to_string();
|
||||
let level = annotation_level_for_level(c.level);
|
||||
level.message(msg)
|
||||
}));
|
||||
|
||||
report.push(group);
|
||||
if let Err(e) = emit_to_destination(
|
||||
renderer.render(&report),
|
||||
level,
|
||||
&mut self.dst,
|
||||
self.short_message,
|
||||
) {
|
||||
panic!("failed to emit error: {e}");
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
let mut file_ann = collect_annotations(args, msp, sm, &self.translator);
|
||||
|
||||
// Make sure our primary file comes first
|
||||
let primary_span = msp.primary_span().unwrap_or_default();
|
||||
if !primary_span.is_dummy() {
|
||||
let primary_lo = sm.lookup_char_pos(primary_span.lo());
|
||||
if let Ok(pos) = file_ann.binary_search_by(|(f, _)| f.name.cmp(&primary_lo.file.name)) {
|
||||
file_ann.swap(0, pos);
|
||||
}
|
||||
|
||||
for (file_idx, (file, annotations)) in file_ann.into_iter().enumerate() {
|
||||
if should_show_source_code(&self.ignored_directories_in_source_blocks, sm, &file) {
|
||||
if let Some(snippet) = self.annotated_snippet(annotations, &file.name, sm) {
|
||||
group = group.element(snippet);
|
||||
}
|
||||
// we can't annotate anything if the source is unavailable.
|
||||
} else if !self.short_message {
|
||||
// We'll just print unannotated messages
|
||||
group = self.unannotated_messages(
|
||||
annotations,
|
||||
&file.name,
|
||||
sm,
|
||||
file_idx,
|
||||
&mut report,
|
||||
group,
|
||||
&annotation_level,
|
||||
);
|
||||
// If this is the last annotation for a file, and
|
||||
// this is the last file, and the first child is a
|
||||
// "secondary" message, we need to add padding
|
||||
// ╭▸ /rustc/FAKE_PREFIX/library/core/src/clone.rs:236:13
|
||||
// │
|
||||
// ├ note: the late bound lifetime parameter
|
||||
// │ (<- It adds *this*)
|
||||
// ╰ warning: this was previously accepted
|
||||
if let Some(c) = children.first()
|
||||
&& (!c.span.has_primary_spans() && !c.span.has_span_labels())
|
||||
{
|
||||
group = group.element(Padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for c in children {
|
||||
let level = annotation_level_for_level(c.level);
|
||||
|
||||
// If at least one portion of the message is styled, we need to
|
||||
// "pre-style" the message
|
||||
let msg = if c.messages.iter().any(|(_, style)| style != &crate::Style::NoStyle) {
|
||||
Cow::Owned(self.pre_style_msgs(&c.messages, c.level, args))
|
||||
} else {
|
||||
self.translator.translate_messages(&c.messages, args)
|
||||
};
|
||||
|
||||
// This is a secondary message with no span info
|
||||
if !c.span.has_primary_spans() && !c.span.has_span_labels() {
|
||||
group = group.element(level.clone().message(msg));
|
||||
continue;
|
||||
}
|
||||
|
||||
report.push(std::mem::replace(
|
||||
&mut group,
|
||||
Group::with_title(level.clone().secondary_title(msg)),
|
||||
));
|
||||
|
||||
let mut file_ann = collect_annotations(args, &c.span, sm, &self.translator);
|
||||
let primary_span = c.span.primary_span().unwrap_or_default();
|
||||
if !primary_span.is_dummy() {
|
||||
let primary_lo = sm.lookup_char_pos(primary_span.lo());
|
||||
if let Ok(pos) =
|
||||
file_ann.binary_search_by(|(f, _)| f.name.cmp(&primary_lo.file.name))
|
||||
{
|
||||
file_ann.swap(0, pos);
|
||||
}
|
||||
}
|
||||
|
||||
for (file_idx, (file, annotations)) in file_ann.into_iter().enumerate() {
|
||||
if should_show_source_code(&self.ignored_directories_in_source_blocks, sm, &file) {
|
||||
if let Some(snippet) = self.annotated_snippet(annotations, &file.name, sm) {
|
||||
group = group.element(snippet);
|
||||
}
|
||||
// we can't annotate anything if the source is unavailable.
|
||||
} else if !self.short_message {
|
||||
// We'll just print unannotated messages
|
||||
group = self.unannotated_messages(
|
||||
annotations,
|
||||
&file.name,
|
||||
sm,
|
||||
file_idx,
|
||||
&mut report,
|
||||
group,
|
||||
&level,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let suggestions_expected = suggestions
|
||||
.iter()
|
||||
.filter(|s| {
|
||||
matches!(
|
||||
s.style,
|
||||
SuggestionStyle::HideCodeInline
|
||||
| SuggestionStyle::ShowCode
|
||||
| SuggestionStyle::ShowAlways
|
||||
)
|
||||
})
|
||||
.count();
|
||||
for suggestion in suggestions {
|
||||
match suggestion.style {
|
||||
SuggestionStyle::CompletelyHidden => {
|
||||
// do not display this suggestion, it is meant only for tools
|
||||
}
|
||||
SuggestionStyle::HideCodeAlways => {
|
||||
let msg = self
|
||||
.translator
|
||||
.translate_messages(&[(suggestion.msg.to_owned(), Style::HeaderMsg)], args);
|
||||
group = group.element(annotate_snippets::Level::HELP.message(msg));
|
||||
}
|
||||
SuggestionStyle::HideCodeInline
|
||||
| SuggestionStyle::ShowCode
|
||||
| SuggestionStyle::ShowAlways => {
|
||||
let substitutions = suggestion
|
||||
.substitutions
|
||||
.into_iter()
|
||||
.filter_map(|mut subst| {
|
||||
// Suggestions coming from macros can have malformed spans. This is a heavy
|
||||
// handed approach to avoid ICEs by ignoring the suggestion outright.
|
||||
let invalid =
|
||||
subst.parts.iter().any(|item| sm.is_valid_span(item.span).is_err());
|
||||
if invalid {
|
||||
debug!("suggestion contains an invalid span: {:?}", subst);
|
||||
}
|
||||
|
||||
// Assumption: all spans are in the same file, and all spans
|
||||
// are disjoint. Sort in ascending order.
|
||||
subst.parts.sort_by_key(|part| part.span.lo());
|
||||
// Verify the assumption that all spans are disjoint
|
||||
assert_eq!(
|
||||
subst.parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
|
||||
None,
|
||||
"all spans must be disjoint",
|
||||
);
|
||||
|
||||
// Account for cases where we are suggesting the same code that's already
|
||||
// there. This shouldn't happen often, but in some cases for multipart
|
||||
// suggestions it's much easier to handle it here than in the origin.
|
||||
subst.parts.retain(|p| is_different(sm, &p.snippet, p.span));
|
||||
|
||||
let item_span = subst.parts.first()?;
|
||||
let file = sm.lookup_source_file(item_span.span.lo());
|
||||
if !invalid
|
||||
&& should_show_source_code(
|
||||
&self.ignored_directories_in_source_blocks,
|
||||
sm,
|
||||
&file,
|
||||
)
|
||||
{
|
||||
Some(subst)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if substitutions.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut msg = self
|
||||
.translator
|
||||
.translate_message(&suggestion.msg, args)
|
||||
.map_err(Report::new)
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let lo = substitutions
|
||||
.iter()
|
||||
.find_map(|sub| sub.parts.first().map(|p| p.span.lo()))
|
||||
.unwrap();
|
||||
let file = sm.lookup_source_file(lo);
|
||||
|
||||
let filename =
|
||||
sm.filename_for_diagnostics(&file.name).to_string_lossy().to_string();
|
||||
|
||||
let other_suggestions = substitutions.len().saturating_sub(MAX_SUGGESTIONS);
|
||||
|
||||
let subs = substitutions
|
||||
.into_iter()
|
||||
.take(MAX_SUGGESTIONS)
|
||||
.filter_map(|sub| {
|
||||
let mut confusion_type = ConfusionType::None;
|
||||
for part in &sub.parts {
|
||||
let part_confusion =
|
||||
detect_confusion_type(sm, &part.snippet, part.span);
|
||||
confusion_type = confusion_type.combine(part_confusion);
|
||||
}
|
||||
|
||||
if !matches!(confusion_type, ConfusionType::None) {
|
||||
msg.push_str(confusion_type.label_text());
|
||||
}
|
||||
|
||||
let mut parts = sub
|
||||
.parts
|
||||
.into_iter()
|
||||
.filter_map(|p| {
|
||||
if is_different(sm, &p.snippet, p.span) {
|
||||
Some((p.span, p.snippet))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if parts.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let spans = parts.iter().map(|(span, _)| *span).collect::<Vec<_>>();
|
||||
// The suggestion adds an entire line of code, ending on a newline, so we'll also
|
||||
// print the *following* line, to provide context of what we're advising people to
|
||||
// do. Otherwise you would only see contextless code that can be confused for
|
||||
// already existing code, despite the colors and UI elements.
|
||||
// We special case `#[derive(_)]\n` and other attribute suggestions, because those
|
||||
// are the ones where context is most useful.
|
||||
let fold = if let [(p, snippet)] = &mut parts[..]
|
||||
&& snippet.trim().starts_with("#[")
|
||||
// This allows for spaces to come between the attribute and the newline
|
||||
&& snippet.trim().ends_with("]")
|
||||
&& snippet.ends_with('\n')
|
||||
&& p.hi() == p.lo()
|
||||
&& let Ok(b) = sm.span_to_prev_source(*p)
|
||||
&& let b = b.rsplit_once('\n').unwrap_or_else(|| ("", &b)).1
|
||||
&& b.trim().is_empty()
|
||||
{
|
||||
// FIXME: This is a hack:
|
||||
// The span for attribute suggestions often times points to the
|
||||
// beginning of an item, disregarding leading whitespace. This
|
||||
// causes the attribute to be properly indented, but leaves original
|
||||
// item without indentation when rendered.
|
||||
// This fixes that problem by adjusting the span to point to the start
|
||||
// of the whitespace, and adds the whitespace to the replacement.
|
||||
//
|
||||
// Source: " extern "custom" fn negate(a: i64) -> i64 {\n"
|
||||
// Span: 4..4
|
||||
// Replacement: "#[unsafe(naked)]\n"
|
||||
//
|
||||
// Before:
|
||||
// help: convert this to an `#[unsafe(naked)]` function
|
||||
// |
|
||||
// LL + #[unsafe(naked)]
|
||||
// LL | extern "custom" fn negate(a: i64) -> i64 {
|
||||
// |
|
||||
//
|
||||
// After
|
||||
// help: convert this to an `#[unsafe(naked)]` function
|
||||
// |
|
||||
// LL + #[unsafe(naked)]
|
||||
// LL | extern "custom" fn negate(a: i64) -> i64 {
|
||||
// |
|
||||
if !b.is_empty() && !snippet.ends_with(b) {
|
||||
snippet.insert_str(0, b);
|
||||
let offset = BytePos(b.len() as u32);
|
||||
*p = p.with_lo(p.lo() - offset).shrink_to_lo();
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if let Some((bounding_span, source, line_offset)) =
|
||||
shrink_file(spans.as_slice(), &file.name, sm)
|
||||
{
|
||||
let adj_lo = bounding_span.lo().to_usize();
|
||||
Some(
|
||||
Snippet::source(source)
|
||||
.line_start(line_offset)
|
||||
.path(filename.clone())
|
||||
.fold(fold)
|
||||
.patches(parts.into_iter().map(
|
||||
|(span, replacement)| {
|
||||
let lo =
|
||||
span.lo().to_usize().saturating_sub(adj_lo);
|
||||
let hi =
|
||||
span.hi().to_usize().saturating_sub(adj_lo);
|
||||
|
||||
Patch::new(lo..hi, replacement)
|
||||
},
|
||||
)),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if !subs.is_empty() {
|
||||
report.push(std::mem::replace(
|
||||
&mut group,
|
||||
Group::with_title(annotate_snippets::Level::HELP.secondary_title(msg)),
|
||||
));
|
||||
|
||||
group = group.elements(subs);
|
||||
if other_suggestions > 0 {
|
||||
group = group.element(
|
||||
annotate_snippets::Level::NOTE.no_name().message(format!(
|
||||
"and {} other candidate{}",
|
||||
other_suggestions,
|
||||
pluralize!(other_suggestions)
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This hack should be removed once annotate_snippets is the
|
||||
// default emitter.
|
||||
if suggestions_expected > 0 && report.is_empty() {
|
||||
group = group.element(Padding);
|
||||
}
|
||||
|
||||
if !group.is_empty() {
|
||||
report.push(group);
|
||||
}
|
||||
if let Err(e) =
|
||||
emit_to_destination(renderer.render(&report), level, &mut self.dst, self.short_message)
|
||||
{
|
||||
panic!("failed to emit error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
fn renderer(&self) -> Renderer {
|
||||
let width = if let Some(width) = self.diagnostic_width {
|
||||
width
|
||||
} else if self.ui_testing || cfg!(miri) {
|
||||
DEFAULT_TERM_WIDTH
|
||||
} else {
|
||||
termize::dimensions().map(|(w, _)| w).unwrap_or(DEFAULT_TERM_WIDTH)
|
||||
};
|
||||
let decor_style = match self.theme {
|
||||
OutputTheme::Ascii => annotate_snippets::renderer::DecorStyle::Ascii,
|
||||
OutputTheme::Unicode => annotate_snippets::renderer::DecorStyle::Unicode,
|
||||
};
|
||||
|
||||
match self.dst.current_choice() {
|
||||
ColorChoice::AlwaysAnsi | ColorChoice::Always | ColorChoice::Auto => Renderer::styled(),
|
||||
ColorChoice::Never => Renderer::plain(),
|
||||
}
|
||||
.term_width(width)
|
||||
.anonymized_line_numbers(self.ui_testing)
|
||||
.decor_style(decor_style)
|
||||
.short_message(self.short_message)
|
||||
}
|
||||
|
||||
fn pre_style_msgs(
|
||||
&self,
|
||||
msgs: &[(DiagMessage, Style)],
|
||||
level: Level,
|
||||
args: &FluentArgs<'_>,
|
||||
) -> String {
|
||||
msgs.iter()
|
||||
.filter_map(|(m, style)| {
|
||||
let text = self.translator.translate_message(m, args).map_err(Report::new).unwrap();
|
||||
let style = style.anstyle(level);
|
||||
if text.is_empty() { None } else { Some(format!("{style}{text}{style:#}")) }
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn annotated_snippet<'a>(
|
||||
&self,
|
||||
annotations: Vec<Annotation>,
|
||||
file_name: &FileName,
|
||||
sm: &Arc<SourceMap>,
|
||||
) -> Option<Snippet<'a, annotate_snippets::Annotation<'a>>> {
|
||||
let spans = annotations.iter().map(|a| a.span).collect::<Vec<_>>();
|
||||
if let Some((bounding_span, source, offset_line)) = shrink_file(&spans, file_name, sm) {
|
||||
let adj_lo = bounding_span.lo().to_usize();
|
||||
let filename = sm.filename_for_diagnostics(file_name).to_string_lossy().to_string();
|
||||
Some(Snippet::source(source).line_start(offset_line).path(filename).annotations(
|
||||
annotations.into_iter().map(move |a| {
|
||||
let lo = a.span.lo().to_usize().saturating_sub(adj_lo);
|
||||
let hi = a.span.hi().to_usize().saturating_sub(adj_lo);
|
||||
let ann = a.kind.span(lo..hi);
|
||||
if let Some(label) = a.label { ann.label(label) } else { ann }
|
||||
}),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn unannotated_messages<'a>(
|
||||
&self,
|
||||
annotations: Vec<Annotation>,
|
||||
file_name: &FileName,
|
||||
sm: &Arc<SourceMap>,
|
||||
file_idx: usize,
|
||||
report: &mut Vec<Group<'a>>,
|
||||
mut group: Group<'a>,
|
||||
level: &annotate_snippets::level::Level<'static>,
|
||||
) -> Group<'a> {
|
||||
let filename = sm.filename_for_diagnostics(file_name).to_string_lossy().to_string();
|
||||
let mut line_tracker = vec![];
|
||||
for (i, a) in annotations.into_iter().enumerate() {
|
||||
let lo = sm.lookup_char_pos(a.span.lo());
|
||||
let hi = sm.lookup_char_pos(a.span.hi());
|
||||
if i == 0 || (a.label.is_some()) {
|
||||
// Render each new file after the first in its own Group
|
||||
// ╭▸ $DIR/deriving-meta-unknown-trait.rs:1:10
|
||||
// │
|
||||
// LL │ #[derive(Eqr)]
|
||||
// │ ━━━
|
||||
// ╰╴ (<- It makes it so *this* will get printed)
|
||||
// ╭▸ $SRC_DIR/core/src/option.rs:594:0
|
||||
// ⸬ $SRC_DIR/core/src/option.rs:602:4
|
||||
// │
|
||||
// ╰ note: not covered
|
||||
if i == 0 && file_idx != 0 {
|
||||
report.push(std::mem::replace(&mut group, Group::with_level(level.clone())));
|
||||
}
|
||||
|
||||
if !line_tracker.contains(&lo.line) {
|
||||
line_tracker.push(lo.line);
|
||||
// ╭▸ $SRC_DIR/core/src/option.rs:594:0 (<- It adds *this*)
|
||||
// ⸬ $SRC_DIR/core/src/option.rs:602:4
|
||||
// │
|
||||
// ╰ note: not covered
|
||||
group = group.element(
|
||||
Origin::path(filename.clone())
|
||||
.line(sm.doctest_offset_line(file_name, lo.line))
|
||||
.char_column(lo.col_display),
|
||||
);
|
||||
}
|
||||
|
||||
if hi.line > lo.line
|
||||
&& a.label.as_ref().is_some_and(|l| !l.is_empty())
|
||||
&& !line_tracker.contains(&hi.line)
|
||||
{
|
||||
line_tracker.push(hi.line);
|
||||
// ╭▸ $SRC_DIR/core/src/option.rs:594:0
|
||||
// ⸬ $SRC_DIR/core/src/option.rs:602:4 (<- It adds *this*)
|
||||
// │
|
||||
// ╰ note: not covered
|
||||
group = group.element(
|
||||
Origin::path(filename.clone())
|
||||
.line(sm.doctest_offset_line(file_name, hi.line))
|
||||
.char_column(hi.col_display),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(label) = a.label
|
||||
&& !label.is_empty()
|
||||
{
|
||||
// ╭▸ $SRC_DIR/core/src/option.rs:594:0
|
||||
// ⸬ $SRC_DIR/core/src/option.rs:602:4
|
||||
// │ (<- It adds *this*)
|
||||
// ╰ note: not covered (<- and *this*)
|
||||
group = group
|
||||
.element(Padding)
|
||||
.element(annotate_snippets::Level::NOTE.message(label));
|
||||
}
|
||||
}
|
||||
}
|
||||
group
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_to_destination(
|
||||
rendered: String,
|
||||
lvl: &Level,
|
||||
dst: &mut Destination,
|
||||
short_message: bool,
|
||||
) -> io::Result<()> {
|
||||
use crate::lock;
|
||||
let _buffer_lock = lock::acquire_global_lock("rustc_errors");
|
||||
writeln!(dst, "{rendered}")?;
|
||||
if !short_message && !lvl.is_failure_note() {
|
||||
writeln!(dst)?;
|
||||
}
|
||||
dst.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Annotation {
|
||||
kind: AnnotationKind,
|
||||
span: Span,
|
||||
label: Option<String>,
|
||||
}
|
||||
|
||||
fn collect_annotations(
|
||||
args: &FluentArgs<'_>,
|
||||
msp: &MultiSpan,
|
||||
sm: &Arc<SourceMap>,
|
||||
translator: &Translator,
|
||||
) -> Vec<(Arc<SourceFile>, Vec<Annotation>)> {
|
||||
let mut output: Vec<(Arc<SourceFile>, Vec<Annotation>)> = vec![];
|
||||
|
||||
for SpanLabel { span, is_primary, label } in msp.span_labels() {
|
||||
// If we don't have a useful span, pick the primary span if that exists.
|
||||
// Worst case we'll just print an error at the top of the main file.
|
||||
let span = match (span.is_dummy(), msp.primary_span()) {
|
||||
(_, None) | (false, _) => span,
|
||||
(true, Some(span)) => span,
|
||||
};
|
||||
let file = sm.lookup_source_file(span.lo());
|
||||
|
||||
let kind = if is_primary { AnnotationKind::Primary } else { AnnotationKind::Context };
|
||||
|
||||
let label = label.as_ref().map(|m| {
|
||||
normalize_whitespace(
|
||||
&translator.translate_message(m, args).map_err(Report::new).unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
let ann = Annotation { kind, span, label };
|
||||
if sm.is_valid_span(ann.span).is_ok() {
|
||||
// Look through each of our files for the one we're adding to. We
|
||||
// use each files `stable_id` to avoid issues with file name
|
||||
// collisions when multiple versions of the same crate are present
|
||||
// in the dependency graph
|
||||
if let Some((_, annotations)) =
|
||||
output.iter_mut().find(|(f, _)| f.stable_id == file.stable_id)
|
||||
{
|
||||
annotations.push(ann);
|
||||
} else {
|
||||
output.push((file, vec![ann]));
|
||||
}
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
fn shrink_file(
|
||||
spans: &[Span],
|
||||
file_name: &FileName,
|
||||
sm: &Arc<SourceMap>,
|
||||
) -> Option<(Span, String, usize)> {
|
||||
let lo_byte = spans.iter().map(|s| s.lo()).min()?;
|
||||
let lo_loc = sm.lookup_char_pos(lo_byte);
|
||||
let lo = lo_loc.file.line_bounds(lo_loc.line.saturating_sub(1)).start;
|
||||
|
||||
let hi_byte = spans.iter().map(|s| s.hi()).max()?;
|
||||
let hi_loc = sm.lookup_char_pos(hi_byte);
|
||||
let hi = lo_loc.file.line_bounds(hi_loc.line.saturating_sub(1)).end;
|
||||
|
||||
let bounding_span = Span::with_root_ctxt(lo, hi);
|
||||
let source = sm.span_to_snippet(bounding_span).unwrap_or_default();
|
||||
let offset_line = sm.doctest_offset_line(file_name, lo_loc.line);
|
||||
|
||||
Some((bounding_span, source, offset_line))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,24 +133,29 @@ pub struct AttributeTemplate {
|
|||
}
|
||||
|
||||
impl AttributeTemplate {
|
||||
pub fn suggestions(&self, style: AttrStyle, name: impl std::fmt::Display) -> Vec<String> {
|
||||
pub fn suggestions(
|
||||
&self,
|
||||
style: Option<AttrStyle>,
|
||||
name: impl std::fmt::Display,
|
||||
) -> Vec<String> {
|
||||
let mut suggestions = vec![];
|
||||
let inner = match style {
|
||||
AttrStyle::Outer => "",
|
||||
AttrStyle::Inner => "!",
|
||||
let (start, end) = match style {
|
||||
Some(AttrStyle::Outer) => ("#[", "]"),
|
||||
Some(AttrStyle::Inner) => ("#![", "]"),
|
||||
None => ("", ""),
|
||||
};
|
||||
if self.word {
|
||||
suggestions.push(format!("#{inner}[{name}]"));
|
||||
suggestions.push(format!("{start}{name}{end}"));
|
||||
}
|
||||
if let Some(descr) = self.list {
|
||||
for descr in descr {
|
||||
suggestions.push(format!("#{inner}[{name}({descr})]"));
|
||||
suggestions.push(format!("{start}{name}({descr}){end}"));
|
||||
}
|
||||
}
|
||||
suggestions.extend(self.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]")));
|
||||
suggestions.extend(self.one_of.iter().map(|&word| format!("{start}{name}({word}){end}")));
|
||||
if let Some(descr) = self.name_value_str {
|
||||
for descr in descr {
|
||||
suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
|
||||
suggestions.push(format!("{start}{name} = \"{descr}\"{end}"));
|
||||
}
|
||||
}
|
||||
suggestions.sort();
|
||||
|
|
|
|||
|
|
@ -102,9 +102,9 @@ declare_features! (
|
|||
/// Allows deriving traits as per `SmartPointer` specification
|
||||
(removed, derive_smart_pointer, "1.84.0", Some(123430), Some("replaced by `CoercePointee`"), 131284),
|
||||
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
|
||||
(removed, doc_auto_cfg, "CURRENT_RUSTC_VERSION", Some(43781), Some("merged into `doc_cfg`"), 138907),
|
||||
(removed, doc_auto_cfg, "1.92.0", Some(43781), Some("merged into `doc_cfg`"), 138907),
|
||||
/// Allows `#[doc(cfg_hide(...))]`.
|
||||
(removed, doc_cfg_hide, "CURRENT_RUSTC_VERSION", Some(43781), Some("merged into `doc_cfg`"), 138907),
|
||||
(removed, doc_cfg_hide, "1.92.0", Some(43781), Some("merged into `doc_cfg`"), 138907),
|
||||
/// Allows using `#[doc(keyword = "...")]`.
|
||||
(removed, doc_keyword, "1.58.0", Some(51315),
|
||||
Some("merged into `#![feature(rustdoc_internals)]`"), 90420),
|
||||
|
|
|
|||
|
|
@ -302,6 +302,10 @@ pub enum DefPathData {
|
|||
Ctor,
|
||||
/// A constant expression (see `{ast,hir}::AnonConst`).
|
||||
AnonConst,
|
||||
/// A constant expression created during AST->HIR lowering..
|
||||
LateAnonConst,
|
||||
/// A fresh anonymous lifetime created by desugaring elided lifetimes.
|
||||
DesugaredAnonymousLifetime,
|
||||
/// An existential `impl Trait` type node.
|
||||
/// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name.
|
||||
OpaqueTy,
|
||||
|
|
@ -454,6 +458,8 @@ impl DefPathData {
|
|||
TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name)
|
||||
| OpaqueLifetime(name) => Some(name),
|
||||
|
||||
DesugaredAnonymousLifetime => Some(kw::UnderscoreLifetime),
|
||||
|
||||
Impl
|
||||
| ForeignMod
|
||||
| CrateRoot
|
||||
|
|
@ -462,6 +468,7 @@ impl DefPathData {
|
|||
| Closure
|
||||
| Ctor
|
||||
| AnonConst
|
||||
| LateAnonConst
|
||||
| OpaqueTy
|
||||
| AnonAssocTy(..)
|
||||
| SyntheticCoroutineBody
|
||||
|
|
@ -475,6 +482,8 @@ impl DefPathData {
|
|||
TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) | AnonAssocTy(name)
|
||||
| OpaqueLifetime(name) => Some(name),
|
||||
|
||||
DesugaredAnonymousLifetime => Some(kw::UnderscoreLifetime),
|
||||
|
||||
Impl
|
||||
| ForeignMod
|
||||
| CrateRoot
|
||||
|
|
@ -483,6 +492,7 @@ impl DefPathData {
|
|||
| Closure
|
||||
| Ctor
|
||||
| AnonConst
|
||||
| LateAnonConst
|
||||
| OpaqueTy
|
||||
| SyntheticCoroutineBody
|
||||
| NestedStatic => None,
|
||||
|
|
@ -502,7 +512,8 @@ impl DefPathData {
|
|||
GlobalAsm => DefPathDataName::Anon { namespace: sym::global_asm },
|
||||
Closure => DefPathDataName::Anon { namespace: sym::closure },
|
||||
Ctor => DefPathDataName::Anon { namespace: sym::constructor },
|
||||
AnonConst => DefPathDataName::Anon { namespace: sym::constant },
|
||||
AnonConst | LateAnonConst => DefPathDataName::Anon { namespace: sym::constant },
|
||||
DesugaredAnonymousLifetime => DefPathDataName::Named(kw::UnderscoreLifetime),
|
||||
OpaqueTy => DefPathDataName::Anon { namespace: sym::opaque },
|
||||
AnonAssocTy(..) => DefPathDataName::Anon { namespace: sym::anon_assoc },
|
||||
SyntheticCoroutineBody => DefPathDataName::Anon { namespace: sym::synthetic },
|
||||
|
|
|
|||
|
|
@ -2460,6 +2460,12 @@ impl Expr<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// If this is a desugared range expression,
|
||||
/// returns the span of the range without desugaring context.
|
||||
pub fn range_span(&self) -> Option<Span> {
|
||||
is_range_literal(self).then(|| self.span.parent_callsite().unwrap())
|
||||
}
|
||||
|
||||
/// Check if expression is an integer literal that can be used
|
||||
/// where `usize` is expected.
|
||||
pub fn is_size_lit(&self) -> bool {
|
||||
|
|
@ -2969,8 +2975,7 @@ pub enum LocalSource {
|
|||
/// A desugared `<expr>.await`.
|
||||
AwaitDesugar,
|
||||
/// A desugared `expr = expr`, where the LHS is a tuple, struct, array or underscore expression.
|
||||
/// The span is that of the `=` sign.
|
||||
AssignDesugar(Span),
|
||||
AssignDesugar,
|
||||
/// A contract `#[ensures(..)]` attribute injects a let binding for the check that runs at point of return.
|
||||
Contract,
|
||||
}
|
||||
|
|
@ -5007,7 +5012,7 @@ mod size_asserts {
|
|||
static_assert_size!(ImplItemKind<'_>, 40);
|
||||
static_assert_size!(Item<'_>, 88);
|
||||
static_assert_size!(ItemKind<'_>, 64);
|
||||
static_assert_size!(LetStmt<'_>, 72);
|
||||
static_assert_size!(LetStmt<'_>, 64);
|
||||
static_assert_size!(Param<'_>, 32);
|
||||
static_assert_size!(Pat<'_>, 80);
|
||||
static_assert_size!(PatKind<'_>, 56);
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@ use rustc_hir::def::{CtorKind, DefKind};
|
|||
use rustc_hir::{LangItem, Node, attrs, find_attr, intravisit};
|
||||
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
|
||||
use rustc_infer::traits::{Obligation, ObligationCauseCode, WellFormedLoc};
|
||||
use rustc_lint_defs::builtin::{
|
||||
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS,
|
||||
};
|
||||
use rustc_lint_defs::builtin::{REPR_TRANSPARENT_NON_ZST_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
|
||||
use rustc_middle::middle::stability::EvalResult;
|
||||
|
|
@ -1509,8 +1507,25 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
|
|||
}
|
||||
|
||||
let typing_env = ty::TypingEnv::non_body_analysis(tcx, adt.did());
|
||||
// For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with
|
||||
// "known" respecting #[non_exhaustive] attributes.
|
||||
// For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST).
|
||||
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
|
||||
// fields or `repr(C)`. We call those fields "unsuited".
|
||||
struct FieldInfo<'tcx> {
|
||||
span: Span,
|
||||
trivial: bool,
|
||||
unsuited: Option<UnsuitedInfo<'tcx>>,
|
||||
}
|
||||
struct UnsuitedInfo<'tcx> {
|
||||
/// The source of the problem, a type that is found somewhere within the field type.
|
||||
ty: Ty<'tcx>,
|
||||
reason: UnsuitedReason,
|
||||
}
|
||||
enum UnsuitedReason {
|
||||
NonExhaustive,
|
||||
PrivateField,
|
||||
ReprC,
|
||||
}
|
||||
|
||||
let field_infos = adt.all_fields().map(|field| {
|
||||
let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did));
|
||||
let layout = tcx.layout_of(typing_env.as_query_input(ty));
|
||||
|
|
@ -1518,22 +1533,20 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
|
|||
let span = tcx.hir_span_if_local(field.did).unwrap();
|
||||
let trivial = layout.is_ok_and(|layout| layout.is_1zst());
|
||||
if !trivial {
|
||||
return (span, trivial, None);
|
||||
// No need to even compute `unsuited`.
|
||||
return FieldInfo { span, trivial, unsuited: None };
|
||||
}
|
||||
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.
|
||||
|
||||
fn check_non_exhaustive<'tcx>(
|
||||
fn check_unsuited<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
t: Ty<'tcx>,
|
||||
) -> ControlFlow<(&'static str, DefId, GenericArgsRef<'tcx>, bool)> {
|
||||
ty: Ty<'tcx>,
|
||||
) -> ControlFlow<UnsuitedInfo<'tcx>> {
|
||||
// We can encounter projections during traversal, so ensure the type is normalized.
|
||||
let t = tcx.try_normalize_erasing_regions(typing_env, t).unwrap_or(t);
|
||||
match t.kind() {
|
||||
ty::Tuple(list) => {
|
||||
list.iter().try_for_each(|t| check_non_exhaustive(tcx, typing_env, t))
|
||||
}
|
||||
ty::Array(ty, _) => check_non_exhaustive(tcx, typing_env, *ty),
|
||||
let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
|
||||
match ty.kind() {
|
||||
ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited(tcx, typing_env, t)),
|
||||
ty::Array(ty, _) => check_unsuited(tcx, typing_env, *ty),
|
||||
ty::Adt(def, args) => {
|
||||
if !def.did().is_local()
|
||||
&& !find_attr!(
|
||||
|
|
@ -1548,28 +1561,36 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
|
|||
.any(ty::VariantDef::is_field_list_non_exhaustive);
|
||||
let has_priv = def.all_fields().any(|f| !f.vis.is_public());
|
||||
if non_exhaustive || has_priv {
|
||||
return ControlFlow::Break((
|
||||
def.descr(),
|
||||
def.did(),
|
||||
args,
|
||||
non_exhaustive,
|
||||
));
|
||||
return ControlFlow::Break(UnsuitedInfo {
|
||||
ty,
|
||||
reason: if non_exhaustive {
|
||||
UnsuitedReason::NonExhaustive
|
||||
} else {
|
||||
UnsuitedReason::PrivateField
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
if def.repr().c() {
|
||||
return ControlFlow::Break(UnsuitedInfo {
|
||||
ty,
|
||||
reason: UnsuitedReason::ReprC,
|
||||
});
|
||||
}
|
||||
def.all_fields()
|
||||
.map(|field| field.ty(tcx, args))
|
||||
.try_for_each(|t| check_non_exhaustive(tcx, typing_env, t))
|
||||
.try_for_each(|t| check_unsuited(tcx, typing_env, t))
|
||||
}
|
||||
_ => ControlFlow::Continue(()),
|
||||
}
|
||||
}
|
||||
|
||||
(span, trivial, check_non_exhaustive(tcx, typing_env, ty).break_value())
|
||||
FieldInfo { span, trivial, unsuited: check_unsuited(tcx, typing_env, ty).break_value() }
|
||||
});
|
||||
|
||||
let non_trivial_fields = field_infos
|
||||
.clone()
|
||||
.filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None });
|
||||
.filter_map(|field| if !field.trivial { Some(field.span) } else { None });
|
||||
let non_trivial_count = non_trivial_fields.clone().count();
|
||||
if non_trivial_count >= 2 {
|
||||
bad_non_zero_sized_fields(
|
||||
|
|
@ -1581,36 +1602,40 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
|
|||
);
|
||||
return;
|
||||
}
|
||||
let mut prev_non_exhaustive_1zst = false;
|
||||
for (span, _trivial, non_exhaustive_1zst) in field_infos {
|
||||
if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive_1zst {
|
||||
|
||||
let mut prev_unsuited_1zst = false;
|
||||
for field in field_infos {
|
||||
if let Some(unsuited) = field.unsuited {
|
||||
assert!(field.trivial);
|
||||
// If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
|
||||
// Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
|
||||
if non_trivial_count > 0 || prev_non_exhaustive_1zst {
|
||||
if non_trivial_count > 0 || prev_unsuited_1zst {
|
||||
tcx.node_span_lint(
|
||||
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
|
||||
REPR_TRANSPARENT_NON_ZST_FIELDS,
|
||||
tcx.local_def_id_to_hir_id(adt.did().expect_local()),
|
||||
span,
|
||||
field.span,
|
||||
|lint| {
|
||||
lint.primary_message(
|
||||
"zero-sized fields in `repr(transparent)` cannot \
|
||||
contain external non-exhaustive types",
|
||||
);
|
||||
let note = if non_exhaustive {
|
||||
"is marked with `#[non_exhaustive]`"
|
||||
} else {
|
||||
"contains private fields"
|
||||
let title = match unsuited.reason {
|
||||
UnsuitedReason::NonExhaustive => "external non-exhaustive types",
|
||||
UnsuitedReason::PrivateField => "external types with private fields",
|
||||
UnsuitedReason::ReprC => "`repr(C)` types",
|
||||
};
|
||||
lint.primary_message(
|
||||
format!("zero-sized fields in `repr(transparent)` cannot contain {title}"),
|
||||
);
|
||||
let note = match unsuited.reason {
|
||||
UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.",
|
||||
UnsuitedReason::PrivateField => "contains private fields, so it could become non-zero-sized in the future.",
|
||||
UnsuitedReason::ReprC => "is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.",
|
||||
};
|
||||
let field_ty = tcx.def_path_str_with_args(def_id, args);
|
||||
lint.note(format!(
|
||||
"this {descr} contains `{field_ty}`, which {note}, \
|
||||
and makes it not a breaking change to become \
|
||||
non-zero-sized in the future."
|
||||
"this field contains `{field_ty}`, which {note}",
|
||||
field_ty = unsuited.ty,
|
||||
));
|
||||
},
|
||||
)
|
||||
);
|
||||
} else {
|
||||
prev_non_exhaustive_1zst = true;
|
||||
prev_unsuited_1zst = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ use tracing::{debug, instrument};
|
|||
|
||||
use super::ItemCtxt;
|
||||
use super::predicates_of::assert_only_contains_predicates_from;
|
||||
use crate::hir_ty_lowering::{HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter};
|
||||
use crate::hir_ty_lowering::{
|
||||
HirTyLowerer, ImpliedBoundsContext, OverlappingAsssocItemConstraints, PredicateFilter,
|
||||
};
|
||||
|
||||
/// For associated types we include both bounds written on the type
|
||||
/// (`type X: Trait`) and predicates from the trait: `where Self::X: Trait`.
|
||||
|
|
@ -52,15 +54,20 @@ fn associated_type_bounds<'tcx>(
|
|||
| PredicateFilter::SelfTraitThatDefines(_)
|
||||
| PredicateFilter::SelfAndAssociatedTypeBounds => {
|
||||
// Implicit bounds are added to associated types unless a `?Trait` bound is found.
|
||||
icx.lowerer().add_sizedness_bounds(
|
||||
icx.lowerer().add_implicit_sizedness_bounds(
|
||||
&mut bounds,
|
||||
item_ty,
|
||||
hir_bounds,
|
||||
None,
|
||||
None,
|
||||
ImpliedBoundsContext::AssociatedTypeOrImplTrait,
|
||||
span,
|
||||
);
|
||||
icx.lowerer().add_default_traits(
|
||||
&mut bounds,
|
||||
item_ty,
|
||||
hir_bounds,
|
||||
ImpliedBoundsContext::AssociatedTypeOrImplTrait,
|
||||
span,
|
||||
);
|
||||
icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
|
||||
|
||||
// Also collect `where Self::Assoc: Trait` from the parent trait's where clauses.
|
||||
let trait_def_id = tcx.local_parent(assoc_item_def_id);
|
||||
|
|
@ -372,15 +379,20 @@ fn opaque_type_bounds<'tcx>(
|
|||
| PredicateFilter::SelfOnly
|
||||
| PredicateFilter::SelfTraitThatDefines(_)
|
||||
| PredicateFilter::SelfAndAssociatedTypeBounds => {
|
||||
icx.lowerer().add_sizedness_bounds(
|
||||
icx.lowerer().add_implicit_sizedness_bounds(
|
||||
&mut bounds,
|
||||
item_ty,
|
||||
hir_bounds,
|
||||
None,
|
||||
None,
|
||||
ImpliedBoundsContext::AssociatedTypeOrImplTrait,
|
||||
span,
|
||||
);
|
||||
icx.lowerer().add_default_traits(
|
||||
&mut bounds,
|
||||
item_ty,
|
||||
hir_bounds,
|
||||
ImpliedBoundsContext::AssociatedTypeOrImplTrait,
|
||||
span,
|
||||
);
|
||||
icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
|
||||
}
|
||||
//`ConstIfConst` is only interested in `[const]` bounds.
|
||||
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ use crate::collect::ItemCtxt;
|
|||
use crate::constrained_generic_params as cgp;
|
||||
use crate::delegation::inherit_predicates_for_delegation_item;
|
||||
use crate::hir_ty_lowering::{
|
||||
HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason,
|
||||
HirTyLowerer, ImpliedBoundsContext, OverlappingAsssocItemConstraints, PredicateFilter,
|
||||
RegionInferReason,
|
||||
};
|
||||
|
||||
/// Returns a list of all type predicates (explicit and implicit) for the definition with
|
||||
|
|
@ -189,19 +190,18 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
|||
PredicateFilter::All,
|
||||
OverlappingAsssocItemConstraints::Allowed,
|
||||
);
|
||||
icx.lowerer().add_sizedness_bounds(
|
||||
icx.lowerer().add_implicit_sizedness_bounds(
|
||||
&mut bounds,
|
||||
tcx.types.self_param,
|
||||
self_bounds,
|
||||
None,
|
||||
Some(def_id),
|
||||
ImpliedBoundsContext::TraitDef(def_id),
|
||||
span,
|
||||
);
|
||||
icx.lowerer().add_default_super_traits(
|
||||
def_id,
|
||||
icx.lowerer().add_default_traits(
|
||||
&mut bounds,
|
||||
tcx.types.self_param,
|
||||
self_bounds,
|
||||
hir_generics,
|
||||
ImpliedBoundsContext::TraitDef(def_id),
|
||||
span,
|
||||
);
|
||||
predicates.extend(bounds);
|
||||
|
|
@ -229,19 +229,18 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
|||
let param_ty = icx.lowerer().lower_ty_param(param.hir_id);
|
||||
let mut bounds = Vec::new();
|
||||
// Implicit bounds are added to type params unless a `?Trait` bound is found
|
||||
icx.lowerer().add_sizedness_bounds(
|
||||
icx.lowerer().add_implicit_sizedness_bounds(
|
||||
&mut bounds,
|
||||
param_ty,
|
||||
&[],
|
||||
Some((param.def_id, hir_generics.predicates)),
|
||||
None,
|
||||
ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates),
|
||||
param.span,
|
||||
);
|
||||
icx.lowerer().add_default_traits(
|
||||
&mut bounds,
|
||||
param_ty,
|
||||
&[],
|
||||
Some((param.def_id, hir_generics.predicates)),
|
||||
ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates),
|
||||
param.span,
|
||||
);
|
||||
trace!(?bounds);
|
||||
|
|
@ -676,11 +675,18 @@ pub(super) fn implied_predicates_with_filter<'tcx>(
|
|||
| PredicateFilter::SelfOnly
|
||||
| PredicateFilter::SelfTraitThatDefines(_)
|
||||
| PredicateFilter::SelfAndAssociatedTypeBounds => {
|
||||
icx.lowerer().add_default_super_traits(
|
||||
trait_def_id,
|
||||
icx.lowerer().add_implicit_sizedness_bounds(
|
||||
&mut bounds,
|
||||
self_param_ty,
|
||||
superbounds,
|
||||
generics,
|
||||
ImpliedBoundsContext::TraitDef(trait_def_id),
|
||||
item.span,
|
||||
);
|
||||
icx.lowerer().add_default_traits(
|
||||
&mut bounds,
|
||||
self_param_ty,
|
||||
superbounds,
|
||||
ImpliedBoundsContext::TraitDef(trait_def_id),
|
||||
item.span,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,6 +242,16 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
|
|||
owner_def_id: LocalDefId,
|
||||
opaque_types_from: DefiningScopeKind,
|
||||
) -> Ty<'tcx> {
|
||||
// When an opaque type is stranded, its hidden type cannot be inferred
|
||||
// so we should not continue.
|
||||
if !tcx.opaque_types_defined_by(owner_def_id).contains(&def_id) {
|
||||
let opaque_type_span = tcx.def_span(def_id);
|
||||
let guar = tcx
|
||||
.dcx()
|
||||
.span_delayed_bug(opaque_type_span, "cannot infer type for stranded opaque type");
|
||||
return Ty::new_error(tcx, guar);
|
||||
}
|
||||
|
||||
match opaque_types_from {
|
||||
DefiningScopeKind::HirTypeck => {
|
||||
let tables = tcx.typeck(owner_def_id);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
|
|
@ -7,7 +6,7 @@ use rustc_errors::struct_span_code_err;
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::PolyTraitRef;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::{
|
||||
self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
|
||||
|
|
@ -18,11 +17,10 @@ use rustc_trait_selection::traits;
|
|||
use smallvec::SmallVec;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::errors::GenericsArgsErrExtend;
|
||||
use crate::errors;
|
||||
use crate::hir_ty_lowering::{
|
||||
AssocItemQSelf, FeedConstTy, HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter,
|
||||
RegionInferReason,
|
||||
AssocItemQSelf, FeedConstTy, GenericsArgsErrExtend, HirTyLowerer, ImpliedBoundsContext,
|
||||
OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -62,7 +60,7 @@ impl CollectedSizednessBounds {
|
|||
|
||||
fn search_bounds_for<'tcx>(
|
||||
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
|
||||
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
|
||||
context: ImpliedBoundsContext<'tcx>,
|
||||
mut f: impl FnMut(&'tcx PolyTraitRef<'tcx>),
|
||||
) {
|
||||
let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| {
|
||||
|
|
@ -76,7 +74,7 @@ fn search_bounds_for<'tcx>(
|
|||
};
|
||||
|
||||
search_bounds(hir_bounds);
|
||||
if let Some((self_ty, where_clause)) = self_ty_where_predicates {
|
||||
if let ImpliedBoundsContext::TyParam(self_ty, where_clause) = context {
|
||||
for clause in where_clause {
|
||||
if let hir::WherePredicateKind::BoundPredicate(pred) = clause.kind
|
||||
&& pred.is_param_bound(self_ty.to_def_id())
|
||||
|
|
@ -89,10 +87,10 @@ fn search_bounds_for<'tcx>(
|
|||
|
||||
fn collect_relaxed_bounds<'tcx>(
|
||||
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
|
||||
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
|
||||
context: ImpliedBoundsContext<'tcx>,
|
||||
) -> SmallVec<[&'tcx PolyTraitRef<'tcx>; 1]> {
|
||||
let mut relaxed_bounds: SmallVec<[_; 1]> = SmallVec::new();
|
||||
search_bounds_for(hir_bounds, self_ty_where_predicates, |ptr| {
|
||||
search_bounds_for(hir_bounds, context, |ptr| {
|
||||
if matches!(ptr.modifiers.polarity, hir::BoundPolarity::Maybe(_)) {
|
||||
relaxed_bounds.push(ptr);
|
||||
}
|
||||
|
|
@ -102,11 +100,11 @@ fn collect_relaxed_bounds<'tcx>(
|
|||
|
||||
fn collect_bounds<'a, 'tcx>(
|
||||
hir_bounds: &'a [hir::GenericBound<'tcx>],
|
||||
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
|
||||
context: ImpliedBoundsContext<'tcx>,
|
||||
target_did: DefId,
|
||||
) -> CollectedBound {
|
||||
let mut collect_into = CollectedBound::default();
|
||||
search_bounds_for(hir_bounds, self_ty_where_predicates, |ptr| {
|
||||
search_bounds_for(hir_bounds, context, |ptr| {
|
||||
if !matches!(ptr.trait_ref.path.res, Res::Def(DefKind::Trait, did) if did == target_did) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -123,17 +121,17 @@ fn collect_bounds<'a, 'tcx>(
|
|||
fn collect_sizedness_bounds<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
|
||||
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
|
||||
context: ImpliedBoundsContext<'tcx>,
|
||||
span: Span,
|
||||
) -> CollectedSizednessBounds {
|
||||
let sized_did = tcx.require_lang_item(hir::LangItem::Sized, span);
|
||||
let sized = collect_bounds(hir_bounds, self_ty_where_predicates, sized_did);
|
||||
let sized = collect_bounds(hir_bounds, context, sized_did);
|
||||
|
||||
let meta_sized_did = tcx.require_lang_item(hir::LangItem::MetaSized, span);
|
||||
let meta_sized = collect_bounds(hir_bounds, self_ty_where_predicates, meta_sized_did);
|
||||
let meta_sized = collect_bounds(hir_bounds, context, meta_sized_did);
|
||||
|
||||
let pointee_sized_did = tcx.require_lang_item(hir::LangItem::PointeeSized, span);
|
||||
let pointee_sized = collect_bounds(hir_bounds, self_ty_where_predicates, pointee_sized_did);
|
||||
let pointee_sized = collect_bounds(hir_bounds, context, pointee_sized_did);
|
||||
|
||||
CollectedSizednessBounds { sized, meta_sized, pointee_sized }
|
||||
}
|
||||
|
|
@ -161,13 +159,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
/// bounds are present.
|
||||
/// - On parameters, opaque type, associated types and trait aliases, add a `MetaSized` bound if
|
||||
/// a `?Sized` bound is present.
|
||||
pub(crate) fn add_sizedness_bounds(
|
||||
pub(crate) fn add_implicit_sizedness_bounds(
|
||||
&self,
|
||||
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
|
||||
self_ty: Ty<'tcx>,
|
||||
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
|
||||
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
|
||||
trait_did: Option<LocalDefId>,
|
||||
context: ImpliedBoundsContext<'tcx>,
|
||||
span: Span,
|
||||
) {
|
||||
let tcx = self.tcx();
|
||||
|
|
@ -181,33 +178,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let pointee_sized_did = tcx.require_lang_item(hir::LangItem::PointeeSized, span);
|
||||
|
||||
// If adding sizedness bounds to a trait, then there are some relevant early exits
|
||||
if let Some(trait_did) = trait_did {
|
||||
let trait_did = trait_did.to_def_id();
|
||||
// Never add a default supertrait to `PointeeSized`.
|
||||
if trait_did == pointee_sized_did {
|
||||
return;
|
||||
match context {
|
||||
ImpliedBoundsContext::TraitDef(trait_did) => {
|
||||
let trait_did = trait_did.to_def_id();
|
||||
// Never add a default supertrait to `PointeeSized`.
|
||||
if trait_did == pointee_sized_did {
|
||||
return;
|
||||
}
|
||||
// Don't add default sizedness supertraits to auto traits because it isn't possible to
|
||||
// relax an automatically added supertrait on the defn itself.
|
||||
if tcx.trait_is_auto(trait_did) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Don't add default sizedness supertraits to auto traits because it isn't possible to
|
||||
// relax an automatically added supertrait on the defn itself.
|
||||
if tcx.trait_is_auto(trait_did) {
|
||||
return;
|
||||
ImpliedBoundsContext::TyParam(..) | ImpliedBoundsContext::AssociatedTypeOrImplTrait => {
|
||||
// Report invalid relaxed bounds.
|
||||
// FIXME: Since we only call this validation function here in this function, we only
|
||||
// fully validate relaxed bounds in contexts where we perform
|
||||
// "sized elaboration". In most cases that doesn't matter because we *usually*
|
||||
// reject such relaxed bounds outright during AST lowering.
|
||||
// However, this can easily get out of sync! Ideally, we would perform this step
|
||||
// where we are guaranteed to catch *all* bounds like in
|
||||
// `Self::lower_poly_trait_ref`. List of concrete issues:
|
||||
// FIXME(more_maybe_bounds): We don't call this for trait object tys, supertrait
|
||||
// bounds, trait alias bounds, assoc type bounds (ATB)!
|
||||
let bounds = collect_relaxed_bounds(hir_bounds, context);
|
||||
self.reject_duplicate_relaxed_bounds(bounds);
|
||||
}
|
||||
} else {
|
||||
// Report invalid relaxed bounds.
|
||||
// FIXME: Since we only call this validation function here in this function, we only
|
||||
// fully validate relaxed bounds in contexts where we perform
|
||||
// "sized elaboration". In most cases that doesn't matter because we *usually*
|
||||
// reject such relaxed bounds outright during AST lowering.
|
||||
// However, this can easily get out of sync! Ideally, we would perform this step
|
||||
// where we are guaranteed to catch *all* bounds like in
|
||||
// `Self::lower_poly_trait_ref`. List of concrete issues:
|
||||
// FIXME(more_maybe_bounds): We don't call this for trait object tys, supertrait
|
||||
// bounds, trait alias bounds, assoc type bounds (ATB)!
|
||||
let bounds = collect_relaxed_bounds(hir_bounds, self_ty_where_predicates);
|
||||
self.reject_duplicate_relaxed_bounds(bounds);
|
||||
}
|
||||
|
||||
let collected = collect_sizedness_bounds(tcx, hir_bounds, self_ty_where_predicates, span);
|
||||
let collected = collect_sizedness_bounds(tcx, hir_bounds, context, span);
|
||||
if (collected.sized.maybe || collected.sized.negative)
|
||||
&& !collected.sized.positive
|
||||
&& !collected.meta_sized.any()
|
||||
|
|
@ -217,62 +217,33 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
// other explicit ones) - this can happen for trait aliases as well as bounds.
|
||||
add_trait_bound(tcx, bounds, self_ty, meta_sized_did, span);
|
||||
} else if !collected.any() {
|
||||
if trait_did.is_some() {
|
||||
// If there are no explicit sizedness bounds on a trait then add a default
|
||||
// `MetaSized` supertrait.
|
||||
add_trait_bound(tcx, bounds, self_ty, meta_sized_did, span);
|
||||
} else {
|
||||
// If there are no explicit sizedness bounds on a parameter then add a default
|
||||
// `Sized` bound.
|
||||
let sized_did = tcx.require_lang_item(hir::LangItem::Sized, span);
|
||||
add_trait_bound(tcx, bounds, self_ty, sized_did, span);
|
||||
match context {
|
||||
ImpliedBoundsContext::TraitDef(..) => {
|
||||
// If there are no explicit sizedness bounds on a trait then add a default
|
||||
// `MetaSized` supertrait.
|
||||
add_trait_bound(tcx, bounds, self_ty, meta_sized_did, span);
|
||||
}
|
||||
ImpliedBoundsContext::TyParam(..)
|
||||
| ImpliedBoundsContext::AssociatedTypeOrImplTrait => {
|
||||
// If there are no explicit sizedness bounds on a parameter then add a default
|
||||
// `Sized` bound.
|
||||
let sized_did = tcx.require_lang_item(hir::LangItem::Sized, span);
|
||||
add_trait_bound(tcx, bounds, self_ty, sized_did, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds `experimental_default_bounds` bounds to the supertrait bounds.
|
||||
pub(crate) fn add_default_super_traits(
|
||||
&self,
|
||||
trait_def_id: LocalDefId,
|
||||
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
|
||||
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
|
||||
hir_generics: &'tcx hir::Generics<'tcx>,
|
||||
span: Span,
|
||||
) {
|
||||
assert_matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias);
|
||||
|
||||
// Supertraits for auto trait are unsound according to the unstable book:
|
||||
// https://doc.rust-lang.org/beta/unstable-book/language-features/auto-traits.html#supertraits
|
||||
if self.tcx().trait_is_auto(trait_def_id.to_def_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.add_default_traits(
|
||||
bounds,
|
||||
self.tcx().types.self_param,
|
||||
hir_bounds,
|
||||
Some((trait_def_id, hir_generics.predicates)),
|
||||
span,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn add_default_traits(
|
||||
&self,
|
||||
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
|
||||
self_ty: Ty<'tcx>,
|
||||
hir_bounds: &[hir::GenericBound<'tcx>],
|
||||
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
|
||||
context: ImpliedBoundsContext<'tcx>,
|
||||
span: Span,
|
||||
) {
|
||||
self.tcx().default_traits().iter().for_each(|default_trait| {
|
||||
self.add_default_trait(
|
||||
*default_trait,
|
||||
bounds,
|
||||
self_ty,
|
||||
hir_bounds,
|
||||
self_ty_where_predicates,
|
||||
span,
|
||||
);
|
||||
self.add_default_trait(*default_trait, bounds, self_ty, hir_bounds, context, span);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -285,15 +256,23 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
|
||||
self_ty: Ty<'tcx>,
|
||||
hir_bounds: &[hir::GenericBound<'tcx>],
|
||||
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
|
||||
context: ImpliedBoundsContext<'tcx>,
|
||||
span: Span,
|
||||
) {
|
||||
let tcx = self.tcx();
|
||||
let trait_id = tcx.lang_items().get(trait_);
|
||||
if let Some(trait_id) = trait_id
|
||||
&& self.should_add_default_traits(trait_id, hir_bounds, self_ty_where_predicates)
|
||||
|
||||
// Supertraits for auto trait are unsound according to the unstable book:
|
||||
// https://doc.rust-lang.org/beta/unstable-book/language-features/auto-traits.html#supertraits
|
||||
if let ImpliedBoundsContext::TraitDef(trait_did) = context
|
||||
&& self.tcx().trait_is_auto(trait_did.into())
|
||||
{
|
||||
add_trait_bound(tcx, bounds, self_ty, trait_id, span);
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(trait_did) = tcx.lang_items().get(trait_)
|
||||
&& self.should_add_default_traits(trait_did, hir_bounds, context)
|
||||
{
|
||||
add_trait_bound(tcx, bounds, self_ty, trait_did, span);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -302,9 +281,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
&self,
|
||||
trait_def_id: DefId,
|
||||
hir_bounds: &'a [hir::GenericBound<'tcx>],
|
||||
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
|
||||
context: ImpliedBoundsContext<'tcx>,
|
||||
) -> bool {
|
||||
let collected = collect_bounds(hir_bounds, self_ty_where_predicates, trait_def_id);
|
||||
let collected = collect_bounds(hir_bounds, context, trait_def_id);
|
||||
!self.tcx().has_attr(CRATE_DEF_ID, sym::rustc_no_implicit_bounds) && !collected.any()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ use rustc_errors::codes::*;
|
|||
use rustc_errors::{
|
||||
Applicability, Diag, EmissionGuarantee, StashKey, Suggestions, struct_span_code_err,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{self as hir, LangItem};
|
||||
use rustc_lint_defs::builtin::{BARE_TRAIT_OBJECTS, UNUSED_ASSOCIATED_TYPE_BOUNDS};
|
||||
use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan;
|
||||
use rustc_middle::ty::{
|
||||
|
|
@ -24,7 +24,8 @@ use tracing::{debug, instrument};
|
|||
use super::HirTyLowerer;
|
||||
use crate::errors::SelfInTypeAlias;
|
||||
use crate::hir_ty_lowering::{
|
||||
GenericArgCountMismatch, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason,
|
||||
GenericArgCountMismatch, ImpliedBoundsContext, OverlappingAsssocItemConstraints,
|
||||
PredicateFilter, RegionInferReason,
|
||||
};
|
||||
|
||||
impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
|
|
@ -76,12 +77,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
.iter()
|
||||
.map(|&trait_ref| hir::GenericBound::Trait(trait_ref))
|
||||
.collect::<Vec<_>>(),
|
||||
None,
|
||||
ImpliedBoundsContext::AssociatedTypeOrImplTrait,
|
||||
span,
|
||||
);
|
||||
|
||||
let (elaborated_trait_bounds, elaborated_projection_bounds) =
|
||||
let (mut elaborated_trait_bounds, elaborated_projection_bounds) =
|
||||
traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied());
|
||||
|
||||
// FIXME(sized-hierarchy): https://github.com/rust-lang/rust/pull/142712#issuecomment-3013231794
|
||||
debug!(?user_written_bounds, ?elaborated_trait_bounds);
|
||||
let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, span);
|
||||
// Don't strip out `MetaSized` when the user wrote it explicitly, only when it was
|
||||
// elaborated
|
||||
if user_written_bounds
|
||||
.iter()
|
||||
.all(|(clause, _)| clause.as_trait_clause().map(|p| p.def_id()) != Some(meta_sized_did))
|
||||
{
|
||||
elaborated_trait_bounds.retain(|(pred, _)| pred.def_id() != meta_sized_did);
|
||||
}
|
||||
debug!(?user_written_bounds, ?elaborated_trait_bounds);
|
||||
|
||||
let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds
|
||||
.into_iter()
|
||||
.partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
|
||||
|
|
|
|||
|
|
@ -56,6 +56,19 @@ use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_
|
|||
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
|
||||
use crate::middle::resolve_bound_vars as rbv;
|
||||
|
||||
/// The context in which an implied bound is being added to a item being lowered (i.e. a sizedness
|
||||
/// trait or a default trait)
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum ImpliedBoundsContext<'tcx> {
|
||||
/// An implied bound is added to a trait definition (i.e. a new supertrait), used when adding
|
||||
/// a default `MetaSized` supertrait
|
||||
TraitDef(LocalDefId),
|
||||
/// An implied bound is added to a type parameter
|
||||
TyParam(LocalDefId, &'tcx [hir::WherePredicate<'tcx>]),
|
||||
/// An implied bound being added in any other context
|
||||
AssociatedTypeOrImplTrait,
|
||||
}
|
||||
|
||||
/// A path segment that is semantically allowed to have generic arguments.
|
||||
#[derive(Debug)]
|
||||
pub struct GenericPathSegment(pub DefId, pub usize);
|
||||
|
|
@ -2513,12 +2526,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
PredicateFilter::All,
|
||||
OverlappingAsssocItemConstraints::Allowed,
|
||||
);
|
||||
self.add_sizedness_bounds(
|
||||
self.add_implicit_sizedness_bounds(
|
||||
&mut bounds,
|
||||
self_ty,
|
||||
hir_bounds,
|
||||
None,
|
||||
None,
|
||||
ImpliedBoundsContext::AssociatedTypeOrImplTrait,
|
||||
hir_ty.span,
|
||||
);
|
||||
self.register_trait_ascription_bounds(bounds, hir_ty.hir_id, hir_ty.span);
|
||||
|
|
|
|||
|
|
@ -35,13 +35,13 @@
|
|||
//! // and are then unable to coerce `&7i32` to `&mut i32`.
|
||||
//! ```
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::ops::{ControlFlow, Deref};
|
||||
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Applicability, Diag, struct_span_code_err};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::attrs::InlineAttr;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self as hir, LangItem};
|
||||
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
|
||||
use rustc_infer::infer::relate::RelateResult;
|
||||
use rustc_infer::infer::{DefineOpaqueTypes, InferOk, InferResult, RegionVariableOrigin};
|
||||
|
|
@ -56,6 +56,8 @@ use rustc_middle::ty::error::TypeError;
|
|||
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span};
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
use rustc_trait_selection::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor};
|
||||
use rustc_trait_selection::solve::{Certainty, Goal, NoSolution};
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{
|
||||
self, ImplSource, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,
|
||||
|
|
@ -639,11 +641,44 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
Adjust::Pointer(PointerCoercion::Unsize),
|
||||
)?;
|
||||
|
||||
let mut selcx = traits::SelectionContext::new(self);
|
||||
|
||||
// Create an obligation for `Source: CoerceUnsized<Target>`.
|
||||
let cause = self.cause(self.cause.span, ObligationCauseCode::Coercion { source, target });
|
||||
let pred = ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]);
|
||||
let obligation = Obligation::new(self.tcx, cause, self.fcx.param_env, pred);
|
||||
|
||||
if self.next_trait_solver() {
|
||||
coercion.obligations.push(obligation);
|
||||
|
||||
if self
|
||||
.infcx
|
||||
.visit_proof_tree(
|
||||
Goal::new(self.tcx, self.param_env, pred),
|
||||
&mut CoerceVisitor { fcx: self.fcx, span: self.cause.span },
|
||||
)
|
||||
.is_break()
|
||||
{
|
||||
return Err(TypeError::Mismatch);
|
||||
}
|
||||
} else {
|
||||
self.coerce_unsized_old_solver(
|
||||
obligation,
|
||||
&mut coercion,
|
||||
coerce_unsized_did,
|
||||
unsize_did,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(coercion)
|
||||
}
|
||||
|
||||
fn coerce_unsized_old_solver(
|
||||
&self,
|
||||
obligation: Obligation<'tcx, ty::Predicate<'tcx>>,
|
||||
coercion: &mut InferOk<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>,
|
||||
coerce_unsized_did: DefId,
|
||||
unsize_did: DefId,
|
||||
) -> Result<(), TypeError<'tcx>> {
|
||||
let mut selcx = traits::SelectionContext::new(self);
|
||||
// Use a FIFO queue for this custom fulfillment procedure.
|
||||
//
|
||||
// A Vec (or SmallVec) is not a natural choice for a queue. However,
|
||||
|
|
@ -651,12 +686,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
// and almost never more than 3. By using a SmallVec we avoid an
|
||||
// allocation, at the (very small) cost of (occasionally) having to
|
||||
// shift subsequent elements down when removing the front element.
|
||||
let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![Obligation::new(
|
||||
self.tcx,
|
||||
cause,
|
||||
self.fcx.param_env,
|
||||
ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target])
|
||||
)];
|
||||
let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![obligation];
|
||||
|
||||
// Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
|
||||
// emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
|
||||
|
|
@ -749,7 +779,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
// with the unsizing - the lack of a coercion should
|
||||
// be silent, as it causes a type mismatch later.
|
||||
}
|
||||
|
||||
Ok(Some(ImplSource::UserDefined(impl_source))) => {
|
||||
queue.extend(impl_source.nested);
|
||||
// Certain incoherent `CoerceUnsized` implementations may cause ICEs,
|
||||
|
|
@ -767,7 +796,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(coercion)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Applies reborrowing for `Pin`
|
||||
|
|
@ -2005,3 +2034,69 @@ impl AsCoercionSite for hir::Arm<'_> {
|
|||
self.body
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively visit goals to decide whether an unsizing is possible.
|
||||
/// `Break`s when it isn't, and an error should be raised.
|
||||
/// `Continue`s when an unsizing ok based on an implementation of the `Unsize` trait / lang item.
|
||||
struct CoerceVisitor<'a, 'tcx> {
|
||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> {
|
||||
type Result = ControlFlow<()>;
|
||||
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
|
||||
let Some(pred) = goal.goal().predicate.as_trait_clause() else {
|
||||
return ControlFlow::Continue(());
|
||||
};
|
||||
|
||||
// Make sure this predicate is referring to either an `Unsize` or `CoerceUnsized` trait,
|
||||
// Otherwise there's nothing to do.
|
||||
if !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize)
|
||||
&& !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::CoerceUnsized)
|
||||
{
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
match goal.result() {
|
||||
// If we prove the `Unsize` or `CoerceUnsized` goal, continue recursing.
|
||||
Ok(Certainty::Yes) => ControlFlow::Continue(()),
|
||||
Err(NoSolution) => {
|
||||
// Even if we find no solution, continue recursing if we find a single candidate
|
||||
// for which we're shallowly certain it holds to get the right error source.
|
||||
if let [only_candidate] = &goal.candidates()[..]
|
||||
&& only_candidate.shallow_certainty() == Certainty::Yes
|
||||
{
|
||||
only_candidate.visit_nested_no_probe(self)
|
||||
} else {
|
||||
ControlFlow::Break(())
|
||||
}
|
||||
}
|
||||
Ok(Certainty::Maybe { .. }) => {
|
||||
// FIXME: structurally normalize?
|
||||
if self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize)
|
||||
&& let ty::Dynamic(..) = pred.skip_binder().trait_ref.args.type_at(1).kind()
|
||||
&& let ty::Infer(ty::TyVar(vid)) = *pred.self_ty().skip_binder().kind()
|
||||
&& self.fcx.type_var_is_sized(vid)
|
||||
{
|
||||
// We get here when trying to unsize a type variable to a `dyn Trait`,
|
||||
// knowing that that variable is sized. Unsizing definitely has to happen in that case.
|
||||
// If the variable weren't sized, we may not need an unsizing coercion.
|
||||
// In general, we don't want to add coercions too eagerly since it makes error messages much worse.
|
||||
ControlFlow::Continue(())
|
||||
} else if let Some(cand) = goal.unique_applicable_candidate()
|
||||
&& cand.shallow_certainty() == Certainty::Yes
|
||||
{
|
||||
cand.visit_nested_no_probe(self)
|
||||
} else {
|
||||
ControlFlow::Break(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2509,11 +2509,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.span_extend_while_whitespace(range_start.span)
|
||||
.span_extend_while_whitespace(range_start.expr.span)
|
||||
.shrink_to_hi()
|
||||
.to(range_end.span);
|
||||
.to(range_end.expr.span);
|
||||
|
||||
err.subdiagnostic(TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr });
|
||||
err.subdiagnostic(TypeMismatchFruTypo {
|
||||
expr_span: range_start.expr.span,
|
||||
fru_span,
|
||||
expr,
|
||||
});
|
||||
|
||||
// Suppress any range expr type mismatches
|
||||
self.dcx().try_steal_replace_and_emit_err(
|
||||
|
|
|
|||
|
|
@ -1112,8 +1112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
hir::Stmt {
|
||||
kind:
|
||||
hir::StmtKind::Let(hir::LetStmt {
|
||||
source:
|
||||
hir::LocalSource::AssignDesugar(_),
|
||||
source: hir::LocalSource::AssignDesugar,
|
||||
..
|
||||
}),
|
||||
..
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
|||
use rustc_span::Span;
|
||||
use rustc_trait_selection::solve::Certainty;
|
||||
use rustc_trait_selection::solve::inspect::{
|
||||
InspectConfig, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor,
|
||||
InferCtxtProofTreeExt, InspectConfig, InspectGoal, ProofTreeVisitor,
|
||||
};
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
|
|
|
|||
|
|
@ -1666,7 +1666,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
match expr.kind {
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => {
|
||||
err.span_suggestion_verbose(
|
||||
start.span.shrink_to_hi().with_hi(end.span.lo()),
|
||||
start.expr.span.shrink_to_hi().with_hi(end.expr.span.lo()),
|
||||
"remove the unnecessary `.` operator for a floating point literal",
|
||||
'.',
|
||||
Applicability::MaybeIncorrect,
|
||||
|
|
@ -1674,8 +1674,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
true
|
||||
}
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => {
|
||||
let range_span = expr.span.parent_callsite().unwrap();
|
||||
err.span_suggestion_verbose(
|
||||
expr.span.with_lo(start.span.hi()),
|
||||
range_span.with_lo(start.expr.span.hi()),
|
||||
"remove the unnecessary `.` operator for a floating point literal",
|
||||
'.',
|
||||
Applicability::MaybeIncorrect,
|
||||
|
|
@ -1683,8 +1684,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
true
|
||||
}
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => {
|
||||
let range_span = expr.span.parent_callsite().unwrap();
|
||||
err.span_suggestion_verbose(
|
||||
expr.span.until(end.span),
|
||||
range_span.until(end.expr.span),
|
||||
"remove the unnecessary `.` operator and add an integer part for a floating point literal",
|
||||
"0.",
|
||||
Applicability::MaybeIncorrect,
|
||||
|
|
@ -2693,7 +2695,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
bool, /* suggest `&` or `&mut` type annotation */
|
||||
)> {
|
||||
let sess = self.sess();
|
||||
let sp = expr.span;
|
||||
let sp = expr.range_span().unwrap_or(expr.span);
|
||||
let sm = sess.source_map();
|
||||
|
||||
// If the span is from an external macro, there's no suggestion we can make.
|
||||
|
|
|
|||
|
|
@ -3046,46 +3046,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
foreign_preds.sort_by_key(|pred: &&ty::TraitPredicate<'_>| pred.trait_ref.to_string());
|
||||
let foreign_def_ids = foreign_preds
|
||||
.iter()
|
||||
.filter_map(|pred| match pred.self_ty().kind() {
|
||||
ty::Adt(def, _) => Some(def.did()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<FxIndexSet<_>>();
|
||||
let mut foreign_spans: MultiSpan = foreign_def_ids
|
||||
.iter()
|
||||
.filter_map(|def_id| {
|
||||
let span = self.tcx.def_span(*def_id);
|
||||
if span.is_dummy() { None } else { Some(span) }
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.into();
|
||||
for pred in &foreign_preds {
|
||||
if let ty::Adt(def, _) = pred.self_ty().kind() {
|
||||
foreign_spans.push_span_label(
|
||||
self.tcx.def_span(def.did()),
|
||||
format!("not implement `{}`", pred.trait_ref.print_trait_sugared()),
|
||||
);
|
||||
|
||||
for pred in foreign_preds {
|
||||
let ty = pred.self_ty();
|
||||
let ty::Adt(def, _) = ty.kind() else { continue };
|
||||
let span = self.tcx.def_span(def.did());
|
||||
if span.is_dummy() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if foreign_spans.primary_span().is_some() {
|
||||
let msg = if let [foreign_pred] = foreign_preds.as_slice() {
|
||||
format!(
|
||||
"the foreign item type `{}` doesn't implement `{}`",
|
||||
foreign_pred.self_ty(),
|
||||
foreign_pred.trait_ref.print_trait_sugared()
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"the foreign item type{} {} implement required trait{} for this \
|
||||
operation to be valid",
|
||||
pluralize!(foreign_def_ids.len()),
|
||||
if foreign_def_ids.len() > 1 { "don't" } else { "doesn't" },
|
||||
pluralize!(foreign_preds.len()),
|
||||
)
|
||||
};
|
||||
err.span_note(foreign_spans, msg);
|
||||
let mut mspan: MultiSpan = span.into();
|
||||
mspan.push_span_label(span, format!("`{ty}` is defined in another crate"));
|
||||
err.span_note(
|
||||
mspan,
|
||||
format!("`{ty}` does not implement `{}`", pred.trait_ref.print_trait_sugared()),
|
||||
);
|
||||
}
|
||||
|
||||
let preds: Vec<_> = errors
|
||||
|
|
|
|||
|
|
@ -798,7 +798,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
} else {
|
||||
let predicate = self.tcx().erase_and_anonymize_regions(predicate);
|
||||
if cause.has_infer() || cause.has_placeholders() {
|
||||
// We can't use the the obligation cause as it references
|
||||
// We can't use the obligation cause as it references
|
||||
// information local to this query.
|
||||
cause = self.fcx.misc(cause.span);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,8 +116,6 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
|
|||
use rustc_data_structures::{base_n, flock};
|
||||
use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize};
|
||||
use rustc_middle::bug;
|
||||
use rustc_session::config::CrateType;
|
||||
use rustc_session::output::collect_crate_types;
|
||||
use rustc_session::{Session, StableCrateId};
|
||||
use rustc_span::Symbol;
|
||||
use tracing::debug;
|
||||
|
|
@ -212,7 +210,11 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu
|
|||
/// The garbage collection will take care of it.
|
||||
///
|
||||
/// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph
|
||||
pub(crate) fn prepare_session_directory(sess: &Session, crate_name: Symbol) {
|
||||
pub(crate) fn prepare_session_directory(
|
||||
sess: &Session,
|
||||
crate_name: Symbol,
|
||||
stable_crate_id: StableCrateId,
|
||||
) {
|
||||
if sess.opts.incremental.is_none() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -222,7 +224,7 @@ pub(crate) fn prepare_session_directory(sess: &Session, crate_name: Symbol) {
|
|||
debug!("prepare_session_directory");
|
||||
|
||||
// {incr-comp-dir}/{crate-name-and-disambiguator}
|
||||
let crate_dir = crate_path(sess, crate_name);
|
||||
let crate_dir = crate_path(sess, crate_name, stable_crate_id);
|
||||
debug!("crate-dir: {}", crate_dir.display());
|
||||
create_dir(sess, &crate_dir, "crate");
|
||||
|
||||
|
|
@ -595,17 +597,9 @@ fn string_to_timestamp(s: &str) -> Result<SystemTime, &'static str> {
|
|||
Ok(UNIX_EPOCH + duration)
|
||||
}
|
||||
|
||||
fn crate_path(sess: &Session, crate_name: Symbol) -> PathBuf {
|
||||
fn crate_path(sess: &Session, crate_name: Symbol, stable_crate_id: StableCrateId) -> PathBuf {
|
||||
let incr_dir = sess.opts.incremental.as_ref().unwrap().clone();
|
||||
|
||||
let crate_types = collect_crate_types(sess, &[]);
|
||||
let stable_crate_id = StableCrateId::new(
|
||||
crate_name,
|
||||
crate_types.contains(&CrateType::Executable),
|
||||
sess.opts.cg.metadata.clone(),
|
||||
sess.cfg_version,
|
||||
);
|
||||
|
||||
let crate_name =
|
||||
format!("{crate_name}-{}", stable_crate_id.as_u64().to_base_fixed_len(CASE_INSENSITIVE));
|
||||
incr_dir.join(crate_name)
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ use rustc_middle::dep_graph::{DepGraph, DepsType, SerializedDepGraph, WorkProduc
|
|||
use rustc_middle::query::on_disk_cache::OnDiskCache;
|
||||
use rustc_serialize::Decodable;
|
||||
use rustc_serialize::opaque::MemDecoder;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::IncrementalStateAssertion;
|
||||
use rustc_session::{Session, StableCrateId};
|
||||
use rustc_span::Symbol;
|
||||
use tracing::{debug, warn};
|
||||
|
||||
|
|
@ -208,9 +208,14 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> {
|
|||
|
||||
/// Setups the dependency graph by loading an existing graph from disk and set up streaming of a
|
||||
/// new graph to an incremental session directory.
|
||||
pub fn setup_dep_graph(sess: &Session, crate_name: Symbol, deps: &DepsType) -> DepGraph {
|
||||
pub fn setup_dep_graph(
|
||||
sess: &Session,
|
||||
crate_name: Symbol,
|
||||
stable_crate_id: StableCrateId,
|
||||
deps: &DepsType,
|
||||
) -> DepGraph {
|
||||
// `load_dep_graph` can only be called after `prepare_session_directory`.
|
||||
prepare_session_directory(sess, crate_name);
|
||||
prepare_session_directory(sess, crate_name, stable_crate_id);
|
||||
|
||||
let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess, deps));
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ impl<I: Idx, T> IntoSliceIdx<I, [T]> for core::range::RangeInclusive<I> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "nightly", not(bootstrap)))]
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<I: Idx, T> IntoSliceIdx<I, [T]> for core::range::RangeToInclusive<I> {
|
||||
type Output = core::range::RangeToInclusive<usize>;
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// tidy-alphabetical-start
|
||||
#![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))]
|
||||
#![cfg_attr(bootstrap, feature(new_zeroed_alloc))]
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
#![cfg_attr(feature = "nightly", feature(extend_one, step_trait, test))]
|
||||
#![cfg_attr(feature = "nightly", feature(new_range_api))]
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use std::{env, fs, iter};
|
|||
use rustc_ast as ast;
|
||||
use rustc_attr_parsing::{AttributeParser, ShouldEmit};
|
||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
use rustc_codegen_ssa::{CodegenResults, CrateInfo};
|
||||
use rustc_data_structures::jobserver::Proxy;
|
||||
use rustc_data_structures::steal::Steal;
|
||||
use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal};
|
||||
|
|
@ -925,7 +926,11 @@ pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>(
|
|||
let pre_configured_attrs = rustc_expand::config::pre_configure_attrs(sess, &krate.attrs);
|
||||
|
||||
let crate_name = get_crate_name(sess, &pre_configured_attrs);
|
||||
let crate_types = collect_crate_types(sess, &pre_configured_attrs);
|
||||
let crate_types = collect_crate_types(
|
||||
sess,
|
||||
&compiler.codegen_backend.supported_crate_types(sess),
|
||||
&pre_configured_attrs,
|
||||
);
|
||||
let stable_crate_id = StableCrateId::new(
|
||||
crate_name,
|
||||
crate_types.contains(&CrateType::Executable),
|
||||
|
|
@ -936,7 +941,7 @@ pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>(
|
|||
let outputs = util::build_output_filenames(&pre_configured_attrs, sess);
|
||||
|
||||
let dep_type = DepsType { dep_names: rustc_query_impl::dep_kind_names() };
|
||||
let dep_graph = setup_dep_graph(sess, crate_name, &dep_type);
|
||||
let dep_graph = setup_dep_graph(sess, crate_name, stable_crate_id, &dep_type);
|
||||
|
||||
let cstore =
|
||||
FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _);
|
||||
|
|
@ -1088,9 +1093,15 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
|
|||
|
||||
sess.time("MIR_borrow_checking", || {
|
||||
tcx.par_hir_body_owners(|def_id| {
|
||||
if !tcx.is_typeck_child(def_id.to_def_id()) {
|
||||
let not_typeck_child = !tcx.is_typeck_child(def_id.to_def_id());
|
||||
if not_typeck_child {
|
||||
// Child unsafety and borrowck happens together with the parent
|
||||
tcx.ensure_ok().check_unsafety(def_id);
|
||||
}
|
||||
if tcx.is_trivial_const(def_id) {
|
||||
return;
|
||||
}
|
||||
if not_typeck_child {
|
||||
tcx.ensure_ok().mir_borrowck(def_id);
|
||||
tcx.ensure_ok().check_transmutes(def_id);
|
||||
}
|
||||
|
|
@ -1198,7 +1209,9 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) {
|
|||
if tcx.sess.opts.unstable_opts.validate_mir {
|
||||
sess.time("ensuring_final_MIR_is_computable", || {
|
||||
tcx.par_hir_body_owners(|def_id| {
|
||||
tcx.instance_mir(ty::InstanceKind::Item(def_id.into()));
|
||||
if !tcx.is_trivial_const(def_id) {
|
||||
tcx.instance_mir(ty::InstanceKind::Item(def_id.into()));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -1236,7 +1249,21 @@ pub(crate) fn start_codegen<'tcx>(
|
|||
|
||||
let metadata = rustc_metadata::fs::encode_and_write_metadata(tcx);
|
||||
|
||||
let codegen = tcx.sess.time("codegen_crate", move || codegen_backend.codegen_crate(tcx));
|
||||
let codegen = tcx.sess.time("codegen_crate", move || {
|
||||
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
|
||||
// Skip crate items and just output metadata in -Z no-codegen mode.
|
||||
tcx.sess.dcx().abort_if_errors();
|
||||
|
||||
// Linker::link will skip join_codegen in case of a CodegenResults Any value.
|
||||
Box::new(CodegenResults {
|
||||
modules: vec![],
|
||||
allocator_module: None,
|
||||
crate_info: CrateInfo::new(tcx, "<dummy cpu>".to_owned()),
|
||||
})
|
||||
} else {
|
||||
codegen_backend.codegen_crate(tcx)
|
||||
}
|
||||
});
|
||||
|
||||
info!("Post-codegen\n{:?}", tcx.debug_stats());
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::sync::Arc;
|
|||
|
||||
use rustc_codegen_ssa::CodegenResults;
|
||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
use rustc_data_structures::indexmap::IndexMap;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_errors::timings::TimingSection;
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
|
|
@ -46,7 +47,14 @@ impl Linker {
|
|||
|
||||
pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) {
|
||||
let (codegen_results, mut work_products) = sess.time("finish_ongoing_codegen", || {
|
||||
codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames)
|
||||
match self.ongoing_codegen.downcast::<CodegenResults>() {
|
||||
// This was a check only build
|
||||
Ok(codegen_results) => (*codegen_results, IndexMap::default()),
|
||||
|
||||
Err(ongoing_codegen) => {
|
||||
codegen_backend.join_codegen(ongoing_codegen, sess, &self.output_filenames)
|
||||
}
|
||||
}
|
||||
});
|
||||
sess.timings.end_section(sess.dcx(), TimingSection::Codegen);
|
||||
|
||||
|
|
|
|||
|
|
@ -354,6 +354,14 @@ impl CodegenBackend for DummyCodegenBackend {
|
|||
"dummy"
|
||||
}
|
||||
|
||||
fn supported_crate_types(&self, _sess: &Session) -> Vec<CrateType> {
|
||||
// This includes bin despite failing on the link step to ensure that you
|
||||
// can still get the frontend handling for binaries. For all library
|
||||
// like crate types cargo will fallback to rlib unless you specifically
|
||||
// say that only a different crate type must be used.
|
||||
vec![CrateType::Rlib, CrateType::Executable]
|
||||
}
|
||||
|
||||
fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box<dyn Any> {
|
||||
Box::new(CodegenResults {
|
||||
modules: vec![],
|
||||
|
|
@ -380,12 +388,16 @@ impl CodegenBackend for DummyCodegenBackend {
|
|||
) {
|
||||
// JUSTIFICATION: TyCtxt no longer available here
|
||||
#[allow(rustc::bad_opt_access)]
|
||||
if sess.opts.crate_types.iter().any(|&crate_type| crate_type != CrateType::Rlib) {
|
||||
if let Some(&crate_type) = codegen_results
|
||||
.crate_info
|
||||
.crate_types
|
||||
.iter()
|
||||
.find(|&&crate_type| crate_type != CrateType::Rlib)
|
||||
{
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
sess.dcx().fatal(format!(
|
||||
"crate type {} not supported by the dummy codegen backend",
|
||||
sess.opts.crate_types[0],
|
||||
"crate type {crate_type} not supported by the dummy codegen backend"
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
|||
// We can't suggest `drop()` when we're on the top level.
|
||||
drop_fn_start_end: can_use_init
|
||||
.map(|init| (local.span.until(init.span), init.span.shrink_to_hi())),
|
||||
is_assign_desugar: matches!(local.source, rustc_hir::LocalSource::AssignDesugar(_)),
|
||||
is_assign_desugar: matches!(local.source, rustc_hir::LocalSource::AssignDesugar),
|
||||
};
|
||||
if is_sync_lock {
|
||||
let span = MultiSpan::from_span(pat.span);
|
||||
|
|
|
|||
|
|
@ -362,6 +362,10 @@ fn register_builtins(store: &mut LintStore) {
|
|||
store.register_renamed("static_mut_ref", "static_mut_refs");
|
||||
store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries");
|
||||
store.register_renamed("elided_named_lifetimes", "mismatched_lifetime_syntaxes");
|
||||
store.register_renamed(
|
||||
"repr_transparent_external_private_fields",
|
||||
"repr_transparent_non_zst_fields",
|
||||
);
|
||||
|
||||
// These were moved to tool lints, but rustc still sees them when compiling normally, before
|
||||
// tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use hir::{ExprKind, Node, is_range_literal};
|
||||
use hir::{ExprKind, Node};
|
||||
use rustc_abi::{Integer, Size};
|
||||
use rustc_hir::{HirId, attrs};
|
||||
use rustc_middle::ty::Ty;
|
||||
|
|
@ -44,7 +44,7 @@ fn lint_overflowing_range_endpoint<'tcx>(
|
|||
let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else {
|
||||
return false;
|
||||
};
|
||||
if !is_range_literal(struct_expr) {
|
||||
let Some(range_span) = struct_expr.range_span() else {
|
||||
return false;
|
||||
};
|
||||
let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else {
|
||||
|
|
@ -71,7 +71,7 @@ fn lint_overflowing_range_endpoint<'tcx>(
|
|||
return false;
|
||||
};
|
||||
UseInclusiveRange::WithoutParen {
|
||||
sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()),
|
||||
sugg: range_span.shrink_to_lo().to(lit_span.shrink_to_hi()),
|
||||
start,
|
||||
literal: lit_val - 1,
|
||||
suffix,
|
||||
|
|
@ -87,7 +87,7 @@ fn lint_overflowing_range_endpoint<'tcx>(
|
|||
|
||||
cx.emit_span_lint(
|
||||
OVERFLOWING_LITERALS,
|
||||
struct_expr.span,
|
||||
range_span,
|
||||
RangeEndpointOutOfRange { ty, sub: sub_sugg },
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ declare_lint_pass! {
|
|||
REFINING_IMPL_TRAIT_INTERNAL,
|
||||
REFINING_IMPL_TRAIT_REACHABLE,
|
||||
RENAMED_AND_REMOVED_LINTS,
|
||||
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
|
||||
REPR_TRANSPARENT_NON_ZST_FIELDS,
|
||||
RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
|
||||
RUST_2021_INCOMPATIBLE_OR_PATTERNS,
|
||||
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
|
||||
|
|
@ -3011,10 +3011,9 @@ declare_lint! {
|
|||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `repr_transparent_external_private_fields` lint
|
||||
/// The `repr_transparent_non_zst_fields` lint
|
||||
/// detects types marked `#[repr(transparent)]` that (transitively)
|
||||
/// contain an external ZST type marked `#[non_exhaustive]` or containing
|
||||
/// private fields
|
||||
/// contain a type that is not guaranteed to remain a ZST type under all configurations.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
|
|
@ -3022,8 +3021,13 @@ declare_lint! {
|
|||
/// #![deny(repr_transparent_external_private_fields)]
|
||||
/// use foo::NonExhaustiveZst;
|
||||
///
|
||||
/// #[repr(C)]
|
||||
/// struct CZst([u8; 0]);
|
||||
///
|
||||
/// #[repr(transparent)]
|
||||
/// struct Bar(u32, ([u32; 0], NonExhaustiveZst));
|
||||
/// #[repr(transparent)]
|
||||
/// struct Baz(u32, CZst);
|
||||
/// ```
|
||||
///
|
||||
/// This will produce:
|
||||
|
|
@ -3042,26 +3046,39 @@ declare_lint! {
|
|||
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
/// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
/// = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
/// = note: this struct contains `NonExhaustiveZst`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
|
||||
/// = note: this field contains `NonExhaustiveZst`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
|
||||
///
|
||||
/// error: zero-sized fields in repr(transparent) cannot contain `#[repr(C)]` types
|
||||
/// --> src/main.rs:5:28
|
||||
/// |
|
||||
/// 5 | struct Baz(u32, CZst);
|
||||
/// | ^^^^
|
||||
/// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
/// = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
/// = note: this field contains `CZst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.
|
||||
/// ```
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Previous, Rust accepted fields that contain external private zero-sized types,
|
||||
/// even though it should not be a breaking change to add a non-zero-sized field to
|
||||
/// that private type.
|
||||
/// Previous, Rust accepted fields that contain external private zero-sized types, even though
|
||||
/// those types could gain a non-zero-sized field in a future, semver-compatible update.
|
||||
///
|
||||
/// Rust also accepted fields that contain `repr(C)` zero-sized types, even though those types
|
||||
/// are not guaranteed to be zero-sized on all targets, and even though those types can
|
||||
/// make a difference for the ABI (and therefore cannot be ignored by `repr(transparent)`).
|
||||
///
|
||||
/// This is a [future-incompatible] lint to transition this
|
||||
/// to a hard error in the future. See [issue #78586] for more details.
|
||||
///
|
||||
/// [issue #78586]: https://github.com/rust-lang/rust/issues/78586
|
||||
/// [future-incompatible]: ../index.md#future-incompatible-lints
|
||||
pub REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
|
||||
Warn,
|
||||
pub REPR_TRANSPARENT_NON_ZST_FIELDS,
|
||||
Deny,
|
||||
"transparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::FutureReleaseError,
|
||||
reference: "issue #78586 <https://github.com/rust-lang/rust/issues/78586>",
|
||||
report_in_deps: true,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -403,7 +403,7 @@ pub enum FutureIncompatibilityReason {
|
|||
///
|
||||
/// After a lint has been in this state for a while and you feel like it is ready to graduate
|
||||
/// to warning everyone, consider setting [`FutureIncompatibleInfo::report_in_deps`] to true.
|
||||
/// (see it's documentation for more guidance)
|
||||
/// (see its documentation for more guidance)
|
||||
///
|
||||
/// After some period of time, lints with this variant can be turned into
|
||||
/// hard errors (and the lint removed). Preferably when there is some
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ libc = "0.2.73"
|
|||
[build-dependencies]
|
||||
# tidy-alphabetical-start
|
||||
# `cc` updates often break things, so we pin it here.
|
||||
cc = "=1.2.16"
|
||||
cc = "=1.2.39"
|
||||
# tidy-alphabetical-end
|
||||
|
||||
[features]
|
||||
|
|
|
|||
|
|
@ -245,6 +245,7 @@ enum class LLVMRustAttributeKind {
|
|||
DeadOnUnwind = 43,
|
||||
DeadOnReturn = 44,
|
||||
CapturesReadOnly = 45,
|
||||
CapturesNone = 46,
|
||||
};
|
||||
|
||||
static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
|
||||
|
|
@ -339,6 +340,7 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
|
|||
#endif
|
||||
case LLVMRustAttributeKind::CapturesAddress:
|
||||
case LLVMRustAttributeKind::CapturesReadOnly:
|
||||
case LLVMRustAttributeKind::CapturesNone:
|
||||
report_fatal_error("Should be handled separately");
|
||||
}
|
||||
report_fatal_error("bad LLVMRustAttributeKind");
|
||||
|
|
@ -390,6 +392,9 @@ extern "C" void LLVMRustEraseInstFromParent(LLVMValueRef Instr) {
|
|||
extern "C" LLVMAttributeRef
|
||||
LLVMRustCreateAttrNoValue(LLVMContextRef C, LLVMRustAttributeKind RustAttr) {
|
||||
#if LLVM_VERSION_GE(21, 0)
|
||||
if (RustAttr == LLVMRustAttributeKind::CapturesNone) {
|
||||
return wrap(Attribute::getWithCaptureInfo(*unwrap(C), CaptureInfo::none()));
|
||||
}
|
||||
if (RustAttr == LLVMRustAttributeKind::CapturesAddress) {
|
||||
return wrap(Attribute::getWithCaptureInfo(
|
||||
*unwrap(C), CaptureInfo(CaptureComponents::Address)));
|
||||
|
|
|
|||
|
|
@ -240,6 +240,7 @@ provide! { tcx, def_id, other, cdata,
|
|||
thir_abstract_const => { table }
|
||||
optimized_mir => { table }
|
||||
mir_for_ctfe => { table }
|
||||
trivial_const => { table }
|
||||
closure_saved_names_of_captured_variables => { table }
|
||||
mir_coroutine_witnesses => { table }
|
||||
promoted_mir => { table }
|
||||
|
|
|
|||
|
|
@ -1791,8 +1791,15 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses);
|
||||
}
|
||||
}
|
||||
let mut is_trivial = false;
|
||||
if encode_const {
|
||||
record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id));
|
||||
if let Some((val, ty)) = tcx.trivial_const(def_id) {
|
||||
is_trivial = true;
|
||||
record!(self.tables.trivial_const[def_id.to_def_id()] <- (val, ty));
|
||||
} else {
|
||||
is_trivial = false;
|
||||
record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id));
|
||||
}
|
||||
|
||||
// FIXME(generic_const_exprs): this feels wrong to have in `encode_mir`
|
||||
let abstract_const = tcx.thir_abstract_const(def_id);
|
||||
|
|
@ -1810,7 +1817,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id));
|
||||
if !is_trivial {
|
||||
record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id));
|
||||
}
|
||||
|
||||
if self.tcx.is_coroutine(def_id.to_def_id())
|
||||
&& let Some(witnesses) = tcx.mir_coroutine_witnesses(def_id)
|
||||
|
|
@ -2234,6 +2243,9 @@ fn prefetch_mir(tcx: TyCtxt<'_>) {
|
|||
|
||||
let reachable_set = tcx.reachable_set(());
|
||||
par_for_each_in(tcx.mir_keys(()), |&&def_id| {
|
||||
if tcx.is_trivial_const(def_id) {
|
||||
return;
|
||||
}
|
||||
let (encode_const, encode_opt) = should_encode_mir(tcx, reachable_set, def_id);
|
||||
|
||||
if encode_const {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
|
|||
use rustc_middle::middle::lib_features::FeatureStability;
|
||||
use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::ConstValue;
|
||||
use rustc_middle::ty::fast_reject::SimplifiedType;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, UnusedGenericParams};
|
||||
use rustc_middle::util::Providers;
|
||||
|
|
@ -426,6 +427,7 @@ define_tables! {
|
|||
object_lifetime_default: Table<DefIndex, LazyValue<ObjectLifetimeDefault>>,
|
||||
optimized_mir: Table<DefIndex, LazyValue<mir::Body<'static>>>,
|
||||
mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>,
|
||||
trivial_const: Table<DefIndex, LazyValue<(ConstValue, Ty<'static>)>>,
|
||||
closure_saved_names_of_captured_variables: Table<DefIndex, LazyValue<IndexVec<FieldIdx, Symbol>>>,
|
||||
mir_coroutine_witnesses: Table<DefIndex, LazyValue<mir::CoroutineLayout<'static>>>,
|
||||
promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>,
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ trivially_parameterized_over_tcx! {
|
|||
rustc_middle::middle::lib_features::FeatureStability,
|
||||
rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault,
|
||||
rustc_middle::mir::ConstQualifs,
|
||||
rustc_middle::mir::ConstValue,
|
||||
rustc_middle::ty::AnonConstKind,
|
||||
rustc_middle::ty::AssocContainer,
|
||||
rustc_middle::ty::AsyncDestructor,
|
||||
|
|
|
|||
|
|
@ -2,19 +2,21 @@ use rustc_macros::{Decodable, Encodable, HashStable};
|
|||
|
||||
use crate::ty::{Ty, TyCtxt, TypingEnv};
|
||||
|
||||
/// Flags that dictate how a parameter is mutated. If the flags are empty, the param is
|
||||
/// read-only. If non-empty, it is read-only if *all* flags' conditions are met.
|
||||
/// Summarizes how a parameter (a return place or an argument) is used inside a MIR body.
|
||||
#[derive(Clone, Copy, PartialEq, Debug, Decodable, Encodable, HashStable)]
|
||||
pub struct DeducedReadOnlyParam(u8);
|
||||
pub struct UsageSummary(u8);
|
||||
|
||||
bitflags::bitflags! {
|
||||
impl DeducedReadOnlyParam: u8 {
|
||||
/// This parameter is dropped. It is read-only if `!needs_drop`.
|
||||
const IF_NO_DROP = 1 << 0;
|
||||
/// This parameter is borrowed. It is read-only if `Freeze`.
|
||||
const IF_FREEZE = 1 << 1;
|
||||
/// This parameter is mutated. It is never read-only.
|
||||
const MUTATED = 1 << 2;
|
||||
impl UsageSummary: u8 {
|
||||
/// This parameter is dropped when it `needs_drop`.
|
||||
const DROP = 1 << 0;
|
||||
/// There is a shared borrow to this parameter.
|
||||
/// It allows for mutation unless parameter is `Freeze`.
|
||||
const SHARED_BORROW = 1 << 1;
|
||||
/// This parameter is mutated (excluding through a drop or a shared borrow).
|
||||
const MUTATE = 1 << 2;
|
||||
/// This parameter is captured (excluding through a drop).
|
||||
const CAPTURE = 1 << 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -24,43 +26,53 @@ bitflags::bitflags! {
|
|||
/// These can be useful for optimization purposes when a function is directly called. We compute
|
||||
/// them and store them into the crate metadata so that downstream crates can make use of them.
|
||||
///
|
||||
/// Right now, we only have `read_only`, but `no_capture` and `no_alias` might be useful in the
|
||||
/// Right now, we have `readonly` and `captures(none)`, but `no_alias` might be useful in the
|
||||
/// future.
|
||||
#[derive(Clone, Copy, PartialEq, Debug, Decodable, Encodable, HashStable)]
|
||||
pub struct DeducedParamAttrs {
|
||||
/// The parameter is marked immutable in the function.
|
||||
pub read_only: DeducedReadOnlyParam,
|
||||
}
|
||||
|
||||
// By default, consider the parameters to be mutated.
|
||||
impl Default for DeducedParamAttrs {
|
||||
#[inline]
|
||||
fn default() -> DeducedParamAttrs {
|
||||
DeducedParamAttrs { read_only: DeducedReadOnlyParam::MUTATED }
|
||||
}
|
||||
pub usage: UsageSummary,
|
||||
}
|
||||
|
||||
impl DeducedParamAttrs {
|
||||
/// Returns true if no attributes have been deduced.
|
||||
#[inline]
|
||||
pub fn is_default(self) -> bool {
|
||||
self.read_only.contains(DeducedReadOnlyParam::MUTATED)
|
||||
self.usage.contains(UsageSummary::MUTATE | UsageSummary::CAPTURE)
|
||||
}
|
||||
|
||||
/// For parameters passed indirectly, returns true if pointer is never written through.
|
||||
pub fn read_only<'tcx>(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: TypingEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let read_only = self.read_only;
|
||||
// We have to check *all* set bits; only if all checks pass is this truly read-only.
|
||||
if read_only.contains(DeducedReadOnlyParam::MUTATED) {
|
||||
// Only if all checks pass is this truly read-only.
|
||||
if self.usage.contains(UsageSummary::MUTATE) {
|
||||
return false;
|
||||
}
|
||||
if read_only.contains(DeducedReadOnlyParam::IF_NO_DROP) && ty.needs_drop(tcx, typing_env) {
|
||||
if self.usage.contains(UsageSummary::DROP) && ty.needs_drop(tcx, typing_env) {
|
||||
return false;
|
||||
}
|
||||
if read_only.contains(DeducedReadOnlyParam::IF_FREEZE) && !ty.is_freeze(tcx, typing_env) {
|
||||
if self.usage.contains(UsageSummary::SHARED_BORROW) && !ty.is_freeze(tcx, typing_env) {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// For parameters passed indirectly, returns true if pointer is not captured, i.e., its
|
||||
/// address is not captured, and pointer is used neither for reads nor writes after function
|
||||
/// returns.
|
||||
pub fn captures_none<'tcx>(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: TypingEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
if self.usage.contains(UsageSummary::CAPTURE) {
|
||||
return false;
|
||||
}
|
||||
if self.usage.contains(UsageSummary::DROP) && ty.needs_drop(tcx, typing_env) {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ where
|
|||
|
||||
let mirs = def_ids
|
||||
.iter()
|
||||
.filter(|def_id| !tcx.is_trivial_const(*def_id))
|
||||
.flat_map(|def_id| {
|
||||
if tcx.is_const_fn(*def_id) {
|
||||
vec![tcx.optimized_mir(*def_id), tcx.mir_for_ctfe(*def_id)]
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
// If we don't *only* FCW anon consts we can wind up incorrectly FCW'ing uses of assoc
|
||||
// consts in pattern positions. #140447
|
||||
&& self.def_kind(cid.instance.def_id()) == DefKind::AnonConst
|
||||
&& !self.is_trivial_const(cid.instance.def_id())
|
||||
{
|
||||
let mir_body = self.mir_for_ctfe(cid.instance.def_id());
|
||||
if mir_body.is_polymorphic {
|
||||
|
|
|
|||
|
|
@ -353,8 +353,16 @@ pub fn write_mir_pretty<'tcx>(
|
|||
// are shared between mir_for_ctfe and optimized_mir
|
||||
writer.write_mir_fn(tcx.mir_for_ctfe(def_id), w)?;
|
||||
} else {
|
||||
let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id));
|
||||
render_body(w, instance_mir)?;
|
||||
if let Some((val, ty)) = tcx.trivial_const(def_id) {
|
||||
ty::print::with_forced_impl_filename_line! {
|
||||
// see notes on #41697 elsewhere
|
||||
write!(w, "const {}", tcx.def_path_str(def_id))?
|
||||
}
|
||||
writeln!(w, ": {} = const {};", ty, Const::Val(val, ty))?;
|
||||
} else {
|
||||
let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id));
|
||||
render_body(w, instance_mir)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -160,6 +160,10 @@ impl EraseType for Result<mir::ConstValue, mir::interpret::ErrorHandled> {
|
|||
type Result = [u8; size_of::<Result<mir::ConstValue, mir::interpret::ErrorHandled>>()];
|
||||
}
|
||||
|
||||
impl EraseType for Option<(mir::ConstValue, Ty<'_>)> {
|
||||
type Result = [u8; size_of::<Option<(mir::ConstValue, Ty<'_>)>>()];
|
||||
}
|
||||
|
||||
impl EraseType for EvalToValTreeResult<'_> {
|
||||
type Result = [u8; size_of::<EvalToValTreeResult<'static>>()];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2719,6 +2719,12 @@ rustc_queries! {
|
|||
separate_provide_extern
|
||||
}
|
||||
|
||||
query trivial_const(def_id: DefId) -> Option<(mir::ConstValue, Ty<'tcx>)> {
|
||||
desc { |tcx| "checking if `{}` is a trivial const", tcx.def_path_str(def_id) }
|
||||
cache_on_disk_if { def_id.is_local() }
|
||||
separate_provide_extern
|
||||
}
|
||||
|
||||
/// Checks for the nearest `#[sanitize(xyz = "off")]` or
|
||||
/// `#[sanitize(xyz = "on")]` on this def and any enclosing defs, up to the
|
||||
/// crate root.
|
||||
|
|
|
|||
|
|
@ -3545,6 +3545,13 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self.get_diagnostic_attr(def_id, sym::do_not_recommend).is_some()
|
||||
}
|
||||
|
||||
pub fn is_trivial_const<P>(self, def_id: P) -> bool
|
||||
where
|
||||
P: IntoQueryParam<DefId>,
|
||||
{
|
||||
self.trivial_const(def_id).is_some()
|
||||
}
|
||||
|
||||
/// Whether this def is one of the special bin crate entrypoint functions that must have a
|
||||
/// monomorphization and also not be internalized in the bin crate.
|
||||
pub fn is_entrypoint(self, def_id: DefId) -> bool {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer};
|
|||
use rustc_hir::attrs::{AttributeKind, StrippedCfgItem};
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap};
|
||||
use rustc_hir::definitions::DisambiguatorState;
|
||||
use rustc_hir::{LangItem, attrs as attr, find_attr};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_index::bit_set::BitMatrix;
|
||||
|
|
@ -211,8 +210,6 @@ pub struct ResolverAstLowering {
|
|||
|
||||
pub node_id_to_def_id: NodeMap<LocalDefId>,
|
||||
|
||||
pub disambiguator: DisambiguatorState,
|
||||
|
||||
pub trait_map: NodeMap<Vec<hir::TraitCandidate>>,
|
||||
/// List functions and methods for which lifetime elision was successful.
|
||||
pub lifetime_elision_allowed: FxHashSet<ast::NodeId>,
|
||||
|
|
|
|||
|
|
@ -2156,6 +2156,7 @@ fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace {
|
|||
|
||||
DefPathData::ValueNs(..)
|
||||
| DefPathData::AnonConst
|
||||
| DefPathData::LateAnonConst
|
||||
| DefPathData::Closure
|
||||
| DefPathData::Ctor => Namespace::ValueNS,
|
||||
|
||||
|
|
|
|||
|
|
@ -454,7 +454,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
index,
|
||||
mutability,
|
||||
fake_borrow_temps,
|
||||
expr.temp_lifetime,
|
||||
expr_span,
|
||||
source_info,
|
||||
),
|
||||
|
|
@ -625,7 +624,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
index: ExprId,
|
||||
mutability: Mutability,
|
||||
fake_borrow_temps: Option<&mut Vec<Local>>,
|
||||
temp_lifetime: TempLifetime,
|
||||
expr_span: Span,
|
||||
source_info: SourceInfo,
|
||||
) -> BlockAnd<PlaceBuilder<'tcx>> {
|
||||
|
|
@ -639,7 +637,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// Making this a *fresh* temporary means we do not have to worry about
|
||||
// the index changing later: Nothing will ever change this temporary.
|
||||
// The "retagging" transformation (for Stacked Borrows) relies on this.
|
||||
let idx = unpack!(block = self.as_temp(block, temp_lifetime, index, Mutability::Not));
|
||||
let index_lifetime = self.thir[index].temp_lifetime;
|
||||
let idx = unpack!(block = self.as_temp(block, index_lifetime, index, Mutability::Not));
|
||||
|
||||
block = self.bounds_check(block, &base_place, idx, expr_span, source_info);
|
||||
|
||||
|
|
|
|||
|
|
@ -2897,8 +2897,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
for (_, node) in tcx.hir_parent_iter(var_id.0) {
|
||||
// FIXME(khuey) at what point is it safe to bail on the iterator?
|
||||
// Can we stop at the first non-Pat node?
|
||||
if matches!(node, Node::LetStmt(&LetStmt { source: LocalSource::AssignDesugar(_), .. }))
|
||||
{
|
||||
if matches!(node, Node::LetStmt(&LetStmt { source: LocalSource::AssignDesugar, .. })) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,8 +132,8 @@ struct SelfArgVisitor<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> SelfArgVisitor<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>, elem: ProjectionElem<Local, Ty<'tcx>>) -> Self {
|
||||
Self { tcx, new_base: Place { local: SELF_ARG, projection: tcx.mk_place_elems(&[elem]) } }
|
||||
fn new(tcx: TyCtxt<'tcx>, new_base: Place<'tcx>) -> Self {
|
||||
Self { tcx, new_base }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -146,16 +146,14 @@ impl<'tcx> MutVisitor<'tcx> for SelfArgVisitor<'tcx> {
|
|||
assert_ne!(*local, SELF_ARG);
|
||||
}
|
||||
|
||||
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
|
||||
fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, _: Location) {
|
||||
if place.local == SELF_ARG {
|
||||
replace_base(place, self.new_base, 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);
|
||||
}
|
||||
for elem in place.projection.iter() {
|
||||
if let PlaceElem::Index(local) = elem {
|
||||
assert_ne!(local, SELF_ARG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -515,20 +513,22 @@ fn make_aggregate_adt<'tcx>(
|
|||
|
||||
#[tracing::instrument(level = "trace", skip(tcx, body))]
|
||||
fn make_coroutine_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
let coroutine_ty = body.local_decls.raw[1].ty;
|
||||
let coroutine_ty = body.local_decls[SELF_ARG].ty;
|
||||
|
||||
let ref_coroutine_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty);
|
||||
|
||||
// Replace the by value coroutine argument
|
||||
body.local_decls.raw[1].ty = ref_coroutine_ty;
|
||||
body.local_decls[SELF_ARG].ty = ref_coroutine_ty;
|
||||
|
||||
// Add a deref to accesses of the coroutine state
|
||||
SelfArgVisitor::new(tcx, ProjectionElem::Deref).visit_body(body);
|
||||
SelfArgVisitor::new(tcx, tcx.mk_place_deref(SELF_ARG.into())).visit_body(body);
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(tcx, body))]
|
||||
fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
let ref_coroutine_ty = body.local_decls.raw[1].ty;
|
||||
let coroutine_ty = body.local_decls[SELF_ARG].ty;
|
||||
|
||||
let ref_coroutine_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty);
|
||||
|
||||
let pin_did = tcx.require_lang_item(LangItem::Pin, body.span);
|
||||
let pin_adt_ref = tcx.adt_def(pin_did);
|
||||
|
|
@ -536,11 +536,33 @@ fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body
|
|||
let pin_ref_coroutine_ty = Ty::new_adt(tcx, pin_adt_ref, args);
|
||||
|
||||
// Replace the by ref coroutine argument
|
||||
body.local_decls.raw[1].ty = pin_ref_coroutine_ty;
|
||||
body.local_decls[SELF_ARG].ty = pin_ref_coroutine_ty;
|
||||
|
||||
let unpinned_local = body.local_decls.push(LocalDecl::new(ref_coroutine_ty, body.span));
|
||||
|
||||
// Add the Pin field access to accesses of the coroutine state
|
||||
SelfArgVisitor::new(tcx, ProjectionElem::Field(FieldIdx::ZERO, ref_coroutine_ty))
|
||||
.visit_body(body);
|
||||
SelfArgVisitor::new(tcx, tcx.mk_place_deref(unpinned_local.into())).visit_body(body);
|
||||
|
||||
let source_info = SourceInfo::outermost(body.span);
|
||||
let pin_field = tcx.mk_place_field(SELF_ARG.into(), FieldIdx::ZERO, ref_coroutine_ty);
|
||||
|
||||
let statements = &mut body.basic_blocks.as_mut_preserves_cfg()[START_BLOCK].statements;
|
||||
// Miri requires retags to be the very first thing in the body.
|
||||
// We insert this assignment just after.
|
||||
let insert_point = statements
|
||||
.iter()
|
||||
.position(|stmt| !matches!(stmt.kind, StatementKind::Retag(..)))
|
||||
.unwrap_or(statements.len());
|
||||
statements.insert(
|
||||
insert_point,
|
||||
Statement::new(
|
||||
source_info,
|
||||
StatementKind::Assign(Box::new((
|
||||
unpinned_local.into(),
|
||||
Rvalue::Use(Operand::Copy(pin_field)),
|
||||
))),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Transforms the `body` of the coroutine applying the following transforms:
|
||||
|
|
@ -1292,8 +1314,6 @@ fn create_coroutine_resume_function<'tcx>(
|
|||
let default_block = insert_term_block(body, TerminatorKind::Unreachable);
|
||||
insert_switch(body, cases, &transform, default_block);
|
||||
|
||||
make_coroutine_state_argument_indirect(tcx, body);
|
||||
|
||||
match transform.coroutine_kind {
|
||||
CoroutineKind::Coroutine(_)
|
||||
| CoroutineKind::Desugared(CoroutineDesugaring::Async | CoroutineDesugaring::AsyncGen, _) =>
|
||||
|
|
@ -1302,7 +1322,9 @@ fn create_coroutine_resume_function<'tcx>(
|
|||
}
|
||||
// Iterator::next doesn't accept a pinned argument,
|
||||
// unlike for all other coroutine kinds.
|
||||
CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {}
|
||||
CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
|
||||
make_coroutine_state_argument_indirect(tcx, body);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we remove dead blocks to remove
|
||||
|
|
|
|||
|
|
@ -684,12 +684,13 @@ pub(super) fn create_coroutine_drop_shim_async<'tcx>(
|
|||
let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()]));
|
||||
body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info);
|
||||
|
||||
make_coroutine_state_argument_indirect(tcx, &mut body);
|
||||
|
||||
match transform.coroutine_kind {
|
||||
// Iterator::next doesn't accept a pinned argument,
|
||||
// unlike for all other coroutine kinds.
|
||||
CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {}
|
||||
CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
|
||||
make_coroutine_state_argument_indirect(tcx, &mut body);
|
||||
}
|
||||
|
||||
_ => {
|
||||
make_coroutine_state_argument_pinned(tcx, &mut body);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,65 +11,91 @@
|
|||
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::middle::deduced_param_attrs::{DeducedParamAttrs, DeducedReadOnlyParam};
|
||||
use rustc_middle::middle::deduced_param_attrs::{DeducedParamAttrs, UsageSummary};
|
||||
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_session::config::OptLevel;
|
||||
|
||||
/// A visitor that determines which arguments have been mutated. We can't use the mutability field
|
||||
/// on LocalDecl for this because it has no meaning post-optimization.
|
||||
struct DeduceReadOnly {
|
||||
/// Each bit is indexed by argument number, starting at zero (so 0 corresponds to local decl
|
||||
/// 1). The bit is false if the argument may have been mutated or true if we know it hasn't
|
||||
/// been up to the point we're at.
|
||||
read_only: IndexVec<usize, DeducedReadOnlyParam>,
|
||||
/// A visitor that determines how a return place and arguments are used inside MIR body.
|
||||
/// To determine whether a local is mutated we can't use the mutability field on LocalDecl
|
||||
/// because it has no meaning post-optimization.
|
||||
struct DeduceParamAttrs {
|
||||
/// Summarizes how a return place and arguments are used inside MIR body.
|
||||
usage: IndexVec<Local, UsageSummary>,
|
||||
}
|
||||
|
||||
impl DeduceReadOnly {
|
||||
/// Returns a new DeduceReadOnly instance.
|
||||
fn new(arg_count: usize) -> Self {
|
||||
Self { read_only: IndexVec::from_elem_n(DeducedReadOnlyParam::empty(), arg_count) }
|
||||
impl DeduceParamAttrs {
|
||||
/// Returns a new DeduceParamAttrs instance.
|
||||
fn new(body: &Body<'_>) -> Self {
|
||||
let mut this =
|
||||
Self { usage: IndexVec::from_elem_n(UsageSummary::empty(), body.arg_count + 1) };
|
||||
// Code generation indicates that a return place is writable. To avoid setting both
|
||||
// `readonly` and `writable` attributes, when return place is never written to, mark it as
|
||||
// mutated.
|
||||
this.usage[RETURN_PLACE] |= UsageSummary::MUTATE;
|
||||
this
|
||||
}
|
||||
|
||||
/// Returns whether the given local is a parameter and its index.
|
||||
fn as_param(&self, local: Local) -> Option<usize> {
|
||||
// Locals and parameters are shifted by `RETURN_PLACE`.
|
||||
let param_index = local.as_usize().checked_sub(1)?;
|
||||
if param_index < self.read_only.len() { Some(param_index) } else { None }
|
||||
/// Returns whether a local is the return place or an argument and returns its index.
|
||||
fn as_param(&self, local: Local) -> Option<Local> {
|
||||
if local.index() < self.usage.len() { Some(local) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for DeduceReadOnly {
|
||||
impl<'tcx> Visitor<'tcx> for DeduceParamAttrs {
|
||||
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
|
||||
// We're only interested in arguments.
|
||||
let Some(param_index) = self.as_param(place.local) else { return };
|
||||
// We're only interested in the return place or an argument.
|
||||
let Some(i) = self.as_param(place.local) else { return };
|
||||
|
||||
match context {
|
||||
// Not mutating, so it's fine.
|
||||
// Not actually using the local.
|
||||
PlaceContext::NonUse(..) => {}
|
||||
// Dereference is not a mutation.
|
||||
// Neither mutated nor captured.
|
||||
_ if place.is_indirect_first_projection() => {}
|
||||
// This is a `Drop`. It could disappear at monomorphization, so mark it specially.
|
||||
PlaceContext::MutatingUse(MutatingUseContext::Drop)
|
||||
// Projection changes the place's type, so `needs_drop(local.ty)` is not
|
||||
// `needs_drop(place.ty)`.
|
||||
if place.projection.is_empty() => {
|
||||
self.read_only[param_index] |= DeducedReadOnlyParam::IF_NO_DROP;
|
||||
self.usage[i] |= UsageSummary::DROP;
|
||||
}
|
||||
PlaceContext::MutatingUse(
|
||||
MutatingUseContext::Call
|
||||
| MutatingUseContext::Yield
|
||||
| MutatingUseContext::Drop
|
||||
| MutatingUseContext::Borrow
|
||||
| MutatingUseContext::RawBorrow) => {
|
||||
self.usage[i] |= UsageSummary::MUTATE;
|
||||
self.usage[i] |= UsageSummary::CAPTURE;
|
||||
}
|
||||
PlaceContext::MutatingUse(
|
||||
MutatingUseContext::Store
|
||||
| MutatingUseContext::SetDiscriminant
|
||||
| MutatingUseContext::AsmOutput
|
||||
| MutatingUseContext::Projection
|
||||
| MutatingUseContext::Retag) => {
|
||||
self.usage[i] |= UsageSummary::MUTATE;
|
||||
}
|
||||
// This is a mutation, so mark it as such.
|
||||
PlaceContext::MutatingUse(..)
|
||||
// Whether mutating though a `&raw const` is allowed is still undecided, so we
|
||||
// disable any sketchy `readonly` optimizations for now.
|
||||
| PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow) => {
|
||||
self.read_only[param_index] |= DeducedReadOnlyParam::MUTATED;
|
||||
// Whether mutating though a `&raw const` is allowed is still undecided, so we
|
||||
// disable any sketchy `readonly` optimizations for now.
|
||||
self.usage[i] |= UsageSummary::MUTATE;
|
||||
self.usage[i] |= UsageSummary::CAPTURE;
|
||||
}
|
||||
// Not mutating if the parameter is `Freeze`.
|
||||
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => {
|
||||
self.read_only[param_index] |= DeducedReadOnlyParam::IF_FREEZE;
|
||||
// Not mutating if the parameter is `Freeze`.
|
||||
self.usage[i] |= UsageSummary::SHARED_BORROW;
|
||||
self.usage[i] |= UsageSummary::CAPTURE;
|
||||
}
|
||||
// Not mutating, so it's fine.
|
||||
PlaceContext::NonMutatingUse(..) => {}
|
||||
PlaceContext::NonMutatingUse(
|
||||
NonMutatingUseContext::Inspect
|
||||
| NonMutatingUseContext::Copy
|
||||
| NonMutatingUseContext::Move
|
||||
| NonMutatingUseContext::FakeBorrow
|
||||
| NonMutatingUseContext::PlaceMention
|
||||
| NonMutatingUseContext::Projection) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,11 +124,11 @@ impl<'tcx> Visitor<'tcx> for DeduceReadOnly {
|
|||
if let TerminatorKind::Call { ref args, .. } = terminator.kind {
|
||||
for arg in args {
|
||||
if let Operand::Move(place) = arg.node
|
||||
// We're only interested in arguments.
|
||||
&& let Some(param_index) = self.as_param(place.local)
|
||||
&& !place.is_indirect_first_projection()
|
||||
&& let Some(i) = self.as_param(place.local)
|
||||
{
|
||||
self.read_only[param_index] |= DeducedReadOnlyParam::MUTATED;
|
||||
self.usage[i] |= UsageSummary::MUTATE;
|
||||
self.usage[i] |= UsageSummary::CAPTURE;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -154,10 +180,9 @@ pub(super) fn deduced_param_attrs<'tcx>(
|
|||
if matches!(fn_ty.kind(), ty::FnDef(..))
|
||||
&& fn_ty
|
||||
.fn_sig(tcx)
|
||||
.inputs()
|
||||
.inputs_and_output()
|
||||
.skip_binder()
|
||||
.iter()
|
||||
.cloned()
|
||||
.all(type_will_always_be_passed_directly)
|
||||
{
|
||||
return &[];
|
||||
|
|
@ -170,13 +195,18 @@ pub(super) fn deduced_param_attrs<'tcx>(
|
|||
|
||||
// Grab the optimized MIR. Analyze it to determine which arguments have been mutated.
|
||||
let body: &Body<'tcx> = tcx.optimized_mir(def_id);
|
||||
let mut deduce_read_only = DeduceReadOnly::new(body.arg_count);
|
||||
deduce_read_only.visit_body(body);
|
||||
tracing::trace!(?deduce_read_only.read_only);
|
||||
// Arguments spread at ABI level are currently unsupported.
|
||||
if body.spread_arg.is_some() {
|
||||
return &[];
|
||||
}
|
||||
|
||||
let mut deduced_param_attrs: &[_] = tcx.arena.alloc_from_iter(
|
||||
deduce_read_only.read_only.into_iter().map(|read_only| DeducedParamAttrs { read_only }),
|
||||
);
|
||||
let mut deduce = DeduceParamAttrs::new(body);
|
||||
deduce.visit_body(body);
|
||||
tracing::trace!(?deduce.usage);
|
||||
|
||||
let mut deduced_param_attrs: &[_] = tcx
|
||||
.arena
|
||||
.alloc_from_iter(deduce.usage.into_iter().map(|usage| DeducedParamAttrs { usage }));
|
||||
|
||||
// Trailing parameters past the size of the `deduced_param_attrs` array are assumed to have the
|
||||
// default set of attributes, so we don't have to store them explicitly. Pop them off to save a
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ mod liveness;
|
|||
mod patch;
|
||||
mod shim;
|
||||
mod ssa;
|
||||
mod trivial_const;
|
||||
|
||||
/// We import passes via this macro so that we can have a static list of pass names
|
||||
/// (used to verify CLI arguments). It takes a list of modules, followed by the passes
|
||||
|
|
@ -226,6 +227,7 @@ pub fn provide(providers: &mut Providers) {
|
|||
promoted_mir,
|
||||
deduced_param_attrs: deduce_param_attrs::deduced_param_attrs,
|
||||
coroutine_by_move_body_def_id: coroutine::coroutine_by_move_body_def_id,
|
||||
trivial_const: trivial_const::trivial_const_provider,
|
||||
..providers.queries
|
||||
};
|
||||
}
|
||||
|
|
@ -379,6 +381,16 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs {
|
|||
fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
|
||||
let mut body = build_mir(tcx, def);
|
||||
|
||||
// Identifying trivial consts based on their mir_built is easy, but a little wasteful.
|
||||
// Trying to push this logic earlier in the compiler and never even produce the Body would
|
||||
// probably improve compile time.
|
||||
if trivial_const::trivial_const(tcx, def, || &body).is_some() {
|
||||
// Skip all the passes below for trivial consts.
|
||||
let body = tcx.alloc_steal_mir(body);
|
||||
pass_manager::dump_mir_for_phase_change(tcx, &body.borrow());
|
||||
return body;
|
||||
}
|
||||
|
||||
pass_manager::dump_mir_for_phase_change(tcx, &body);
|
||||
|
||||
pm::run_passes(
|
||||
|
|
@ -409,6 +421,8 @@ fn mir_promoted(
|
|||
tcx: TyCtxt<'_>,
|
||||
def: LocalDefId,
|
||||
) -> (&Steal<Body<'_>>, &Steal<IndexVec<Promoted, Body<'_>>>) {
|
||||
debug_assert!(!tcx.is_trivial_const(def), "Tried to get mir_promoted of a trivial const");
|
||||
|
||||
// Ensure that we compute the `mir_const_qualif` for constants at
|
||||
// this point, before we steal the mir-const result.
|
||||
// Also this means promotion can rely on all const checks having been done.
|
||||
|
|
@ -436,6 +450,9 @@ fn mir_promoted(
|
|||
tcx.ensure_done().coroutine_by_move_body_def_id(def);
|
||||
}
|
||||
|
||||
// the `trivial_const` query uses mir_built, so make sure it is run.
|
||||
tcx.ensure_done().trivial_const(def);
|
||||
|
||||
let mut body = tcx.mir_built(def).steal();
|
||||
if let Some(error_reported) = const_qualifs.tainted_by_errors {
|
||||
body.tainted_by_errors = Some(error_reported);
|
||||
|
|
@ -463,6 +480,7 @@ fn mir_promoted(
|
|||
|
||||
/// Compute the MIR that is used during CTFE (and thus has no optimizations run on it)
|
||||
fn mir_for_ctfe(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &Body<'_> {
|
||||
debug_assert!(!tcx.is_trivial_const(def_id), "Tried to get mir_for_ctfe of a trivial const");
|
||||
tcx.arena.alloc(inner_mir_for_ctfe(tcx, def_id))
|
||||
}
|
||||
|
||||
|
|
|
|||
110
compiler/rustc_mir_transform/src/trivial_const.rs
Normal file
110
compiler/rustc_mir_transform/src/trivial_const.rs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::mir::{
|
||||
Body, Const, ConstValue, Operand, Place, RETURN_PLACE, Rvalue, START_BLOCK, StatementKind,
|
||||
TerminatorKind, UnevaluatedConst,
|
||||
};
|
||||
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitableExt};
|
||||
|
||||
/// If the given def is a trivial const, returns the value and type the const evaluates to.
|
||||
///
|
||||
/// A "trivial const" is a const which can be easily proven to evaluate successfully, and the value
|
||||
/// that it evaluates to can be easily found without going through the usual MIR phases for a const.
|
||||
///
|
||||
/// Currently, we support two forms of trivial const.
|
||||
///
|
||||
/// The base case is this:
|
||||
/// ```
|
||||
/// const A: usize = 0;
|
||||
/// ```
|
||||
/// which has this MIR:
|
||||
/// ```text
|
||||
/// const A: usize = {
|
||||
/// let mut _0: usize;
|
||||
///
|
||||
/// bb0: {
|
||||
/// _0 = const 0_usize;
|
||||
/// return;
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Which we recognize by looking for a Body which has a single basic block with a return
|
||||
/// terminator and a single statement which assigns an `Operand::Constant(Const::Val)` to the
|
||||
/// return place.
|
||||
/// This scenario meets the required criteria because:
|
||||
/// * Control flow cannot panic, we don't have any calls or assert terminators
|
||||
/// * The value of the const is already computed, so it cannot fail
|
||||
///
|
||||
/// In addition to assignment of literals, assignments of trivial consts are also considered
|
||||
/// trivial consts. In this case, both `A` and `B` are trivial:
|
||||
/// ```
|
||||
/// const A: usize = 0;
|
||||
/// const B: usize = A;
|
||||
/// ```
|
||||
pub(crate) fn trivial_const<'a, 'tcx: 'a, F, B>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def: LocalDefId,
|
||||
body_provider: F,
|
||||
) -> Option<(ConstValue, Ty<'tcx>)>
|
||||
where
|
||||
F: FnOnce() -> B,
|
||||
B: Deref<Target = Body<'tcx>>,
|
||||
{
|
||||
if !matches!(tcx.def_kind(def), DefKind::AssocConst | DefKind::Const | DefKind::AnonConst) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let body = body_provider();
|
||||
|
||||
if body.has_opaque_types() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if body.basic_blocks.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let block = &body.basic_blocks[START_BLOCK];
|
||||
if block.statements.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if block.terminator().kind != TerminatorKind::Return {
|
||||
return None;
|
||||
}
|
||||
|
||||
let StatementKind::Assign(box (place, rvalue)) = &block.statements[0].kind else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if *place != Place::from(RETURN_PLACE) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Rvalue::Use(Operand::Constant(c)) = rvalue else {
|
||||
return None;
|
||||
};
|
||||
match c.const_ {
|
||||
Const::Ty(..) => None,
|
||||
Const::Unevaluated(UnevaluatedConst { def, args, .. }, _ty) => {
|
||||
if !args.is_empty() {
|
||||
return None;
|
||||
}
|
||||
tcx.trivial_const(def)
|
||||
}
|
||||
Const::Val(v, ty) => Some((v, ty)),
|
||||
}
|
||||
}
|
||||
|
||||
// The query provider is based on calling the free function trivial_const, which calls mir_built,
|
||||
// which internally has a fast-path for trivial consts so it too calls trivial_const. This isn't
|
||||
// recursive, but we are checking if the const is trivial twice. A better design might detect
|
||||
// trivial consts before getting to MIR, which would hopefully straighten this out.
|
||||
pub(crate) fn trivial_const_provider<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def: LocalDefId,
|
||||
) -> Option<(ConstValue, Ty<'tcx>)> {
|
||||
trivial_const(tcx, def, || tcx.mir_built(def).borrow())
|
||||
}
|
||||
|
|
@ -211,10 +211,32 @@ where
|
|||
}
|
||||
|
||||
fn consider_builtin_copy_clone_candidate(
|
||||
_ecx: &mut EvalCtxt<'_, D>,
|
||||
_goal: Goal<I, Self>,
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
Err(NoSolution)
|
||||
let cx = ecx.cx();
|
||||
|
||||
let self_ty = goal.predicate.self_ty();
|
||||
let constituent_tys =
|
||||
structural_traits::instantiate_constituent_tys_for_copy_clone_trait(ecx, self_ty)?;
|
||||
|
||||
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
|
||||
ecx.enter_forall(constituent_tys, |ecx, tys| {
|
||||
ecx.add_goals(
|
||||
GoalSource::ImplWhereBound,
|
||||
tys.into_iter().map(|ty| {
|
||||
goal.with(
|
||||
cx,
|
||||
ty::ClauseKind::HostEffect(
|
||||
goal.predicate.with_replaced_self_ty(cx, ty),
|
||||
),
|
||||
)
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
})
|
||||
}
|
||||
|
||||
fn consider_builtin_fn_ptr_trait_candidate(
|
||||
|
|
|
|||
|
|
@ -1308,36 +1308,6 @@ where
|
|||
},
|
||||
);
|
||||
|
||||
// HACK: We bail with overflow if the response would have too many non-region
|
||||
// inference variables. This tends to only happen if we encounter a lot of
|
||||
// ambiguous alias types which get replaced with fresh inference variables
|
||||
// during generalization. This prevents hangs caused by an exponential blowup,
|
||||
// see tests/ui/traits/next-solver/coherence-alias-hang.rs.
|
||||
match self.current_goal_kind {
|
||||
// We don't do so for `NormalizesTo` goals as we erased the expected term and
|
||||
// bailing with overflow here would prevent us from detecting a type-mismatch,
|
||||
// causing a coherence error in diesel, see #131969. We still bail with overflow
|
||||
// when later returning from the parent AliasRelate goal.
|
||||
CurrentGoalKind::NormalizesTo => {}
|
||||
CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => {
|
||||
let num_non_region_vars = canonical
|
||||
.variables
|
||||
.iter()
|
||||
.filter(|c| !c.is_region() && c.is_existential())
|
||||
.count();
|
||||
if num_non_region_vars > self.cx().recursion_limit() {
|
||||
debug!(?num_non_region_vars, "too many inference variables -> overflow");
|
||||
return Ok(self.make_ambiguous_response_no_constraints(
|
||||
MaybeCause::Overflow {
|
||||
suggest_increasing_limit: true,
|
||||
keep_constraints: false,
|
||||
},
|
||||
OpaqueTypesJank::AllGood,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(canonical)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4036,6 +4036,30 @@ impl<'a> Parser<'a> {
|
|||
self.mk_expr(span, ExprKind::Err(guar))
|
||||
}
|
||||
|
||||
pub(crate) fn mk_unit_expr(&self, span: Span) -> Box<Expr> {
|
||||
self.mk_expr(span, ExprKind::Tup(Default::default()))
|
||||
}
|
||||
|
||||
pub(crate) fn mk_closure_expr(&self, span: Span, body: Box<Expr>) -> Box<Expr> {
|
||||
self.mk_expr(
|
||||
span,
|
||||
ast::ExprKind::Closure(Box::new(ast::Closure {
|
||||
binder: rustc_ast::ClosureBinder::NotPresent,
|
||||
constness: rustc_ast::Const::No,
|
||||
movability: rustc_ast::Movability::Movable,
|
||||
capture_clause: rustc_ast::CaptureBy::Ref,
|
||||
coroutine_kind: None,
|
||||
fn_decl: Box::new(rustc_ast::FnDecl {
|
||||
inputs: Default::default(),
|
||||
output: rustc_ast::FnRetTy::Default(span),
|
||||
}),
|
||||
fn_arg_span: span,
|
||||
fn_decl_span: span,
|
||||
body,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create expression span ensuring the span of the parent node
|
||||
/// is larger than the span of lhs and rhs, including the attributes.
|
||||
fn mk_expr_sp(&self, lhs: &Box<Expr>, lhs_span: Span, op_span: Span, rhs_span: Span) -> Span {
|
||||
|
|
|
|||
|
|
@ -312,25 +312,48 @@ impl<'a> Parser<'a> {
|
|||
/// Parses an experimental fn contract
|
||||
/// (`contract_requires(WWW) contract_ensures(ZZZ)`)
|
||||
pub(super) fn parse_contract(&mut self) -> PResult<'a, Option<Box<ast::FnContract>>> {
|
||||
let requires = if self.eat_keyword_noexpect(exp!(ContractRequires).kw) {
|
||||
self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span);
|
||||
let precond = self.parse_expr()?;
|
||||
Some(precond)
|
||||
let (declarations, requires) = self.parse_contract_requires()?;
|
||||
let ensures = self.parse_contract_ensures()?;
|
||||
|
||||
if requires.is_none() && ensures.is_none() {
|
||||
Ok(None)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let ensures = if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) {
|
||||
Ok(Some(Box::new(ast::FnContract { declarations, requires, ensures })))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_contract_requires(
|
||||
&mut self,
|
||||
) -> PResult<'a, (ThinVec<rustc_ast::Stmt>, Option<Box<rustc_ast::Expr>>)> {
|
||||
Ok(if self.eat_keyword_noexpect(exp!(ContractRequires).kw) {
|
||||
self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span);
|
||||
let mut decls_and_precond = self.parse_block()?;
|
||||
|
||||
let precond = match decls_and_precond.stmts.pop() {
|
||||
Some(precond) => match precond.kind {
|
||||
rustc_ast::StmtKind::Expr(expr) => expr,
|
||||
// Insert dummy node that will be rejected by typechecker to
|
||||
// avoid reinventing an error
|
||||
_ => self.mk_unit_expr(decls_and_precond.span),
|
||||
},
|
||||
None => self.mk_unit_expr(decls_and_precond.span),
|
||||
};
|
||||
let precond = self.mk_closure_expr(precond.span, precond);
|
||||
let decls = decls_and_precond.stmts;
|
||||
(decls, Some(precond))
|
||||
} else {
|
||||
(Default::default(), None)
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_contract_ensures(&mut self) -> PResult<'a, Option<Box<rustc_ast::Expr>>> {
|
||||
Ok(if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) {
|
||||
self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span);
|
||||
let postcond = self.parse_expr()?;
|
||||
Some(postcond)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if requires.is_none() && ensures.is_none() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(Box::new(ast::FnContract { requires, ensures })))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses an optional where-clause.
|
||||
|
|
|
|||
|
|
@ -92,7 +92,8 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> {
|
|||
} else {
|
||||
false
|
||||
};
|
||||
!must_override && self.tcx.is_mir_available(def_id)
|
||||
// FIXME: A good reason to make is_mir_available or mir_keys change behavior
|
||||
!must_override && self.tcx.is_mir_available(def_id) && !self.tcx.is_trivial_const(def_id)
|
||||
}
|
||||
|
||||
fn filter_fn_def(&self, def_id: DefId) -> Option<DefId> {
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
let mut expected = source.descr_expected();
|
||||
let path_str = Segment::names_to_string(path);
|
||||
let item_str = path.last().unwrap().ident;
|
||||
|
||||
if let Some(res) = res {
|
||||
BaseError {
|
||||
msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
|
||||
|
|
@ -821,12 +822,18 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
args_snippet = snippet;
|
||||
}
|
||||
|
||||
err.span_suggestion(
|
||||
call_span,
|
||||
format!("try calling `{ident}` as a method"),
|
||||
format!("self.{path_str}({args_snippet})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
if let Some(Res::Def(DefKind::Struct, def_id)) = res {
|
||||
self.update_err_for_private_tuple_struct_fields(err, &source, def_id);
|
||||
err.note("constructor is not visible here due to private fields");
|
||||
} else {
|
||||
err.span_suggestion(
|
||||
call_span,
|
||||
format!("try calling `{ident}` as a method"),
|
||||
format!("self.{path_str}({args_snippet})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
return (true, suggested_candidates, candidates);
|
||||
}
|
||||
}
|
||||
|
|
@ -1611,6 +1618,47 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn update_err_for_private_tuple_struct_fields(
|
||||
&mut self,
|
||||
err: &mut Diag<'_>,
|
||||
source: &PathSource<'_, '_, '_>,
|
||||
def_id: DefId,
|
||||
) -> Option<Vec<Span>> {
|
||||
match source {
|
||||
// e.g. `if let Enum::TupleVariant(field1, field2) = _`
|
||||
PathSource::TupleStruct(_, pattern_spans) => {
|
||||
err.primary_message(
|
||||
"cannot match against a tuple struct which contains private fields",
|
||||
);
|
||||
|
||||
// Use spans of the tuple struct pattern.
|
||||
Some(Vec::from(*pattern_spans))
|
||||
}
|
||||
// e.g. `let _ = Enum::TupleVariant(field1, field2);`
|
||||
PathSource::Expr(Some(Expr {
|
||||
kind: ExprKind::Call(path, args),
|
||||
span: call_span,
|
||||
..
|
||||
})) => {
|
||||
err.primary_message(
|
||||
"cannot initialize a tuple struct which contains private fields",
|
||||
);
|
||||
self.suggest_alternative_construction_methods(
|
||||
def_id,
|
||||
err,
|
||||
path.span,
|
||||
*call_span,
|
||||
&args[..],
|
||||
);
|
||||
// Use spans of the tuple struct definition.
|
||||
self.r
|
||||
.field_idents(def_id)
|
||||
.map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides context-dependent help for errors reported by the `smart_resolve_path_fragment`
|
||||
/// function.
|
||||
/// Returns `true` if able to provide context-dependent help.
|
||||
|
|
@ -1942,42 +1990,6 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
return true;
|
||||
};
|
||||
|
||||
let update_message =
|
||||
|this: &mut Self, err: &mut Diag<'_>, source: &PathSource<'_, '_, '_>| {
|
||||
match source {
|
||||
// e.g. `if let Enum::TupleVariant(field1, field2) = _`
|
||||
PathSource::TupleStruct(_, pattern_spans) => {
|
||||
err.primary_message(
|
||||
"cannot match against a tuple struct which contains private fields",
|
||||
);
|
||||
|
||||
// Use spans of the tuple struct pattern.
|
||||
Some(Vec::from(*pattern_spans))
|
||||
}
|
||||
// e.g. `let _ = Enum::TupleVariant(field1, field2);`
|
||||
PathSource::Expr(Some(Expr {
|
||||
kind: ExprKind::Call(path, args),
|
||||
span: call_span,
|
||||
..
|
||||
})) => {
|
||||
err.primary_message(
|
||||
"cannot initialize a tuple struct which contains private fields",
|
||||
);
|
||||
this.suggest_alternative_construction_methods(
|
||||
def_id,
|
||||
err,
|
||||
path.span,
|
||||
*call_span,
|
||||
&args[..],
|
||||
);
|
||||
// Use spans of the tuple struct definition.
|
||||
this.r
|
||||
.field_idents(def_id)
|
||||
.map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module);
|
||||
if let Some(use_span) = self.r.inaccessible_ctor_reexport.get(&span)
|
||||
&& is_accessible
|
||||
|
|
@ -2006,13 +2018,14 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
update_message(self, err, &source);
|
||||
self.update_err_for_private_tuple_struct_fields(err, &source, def_id);
|
||||
}
|
||||
if !is_expected(ctor_def) || is_accessible {
|
||||
return true;
|
||||
}
|
||||
|
||||
let field_spans = update_message(self, err, &source);
|
||||
let field_spans =
|
||||
self.update_err_for_private_tuple_struct_fields(err, &source, def_id);
|
||||
|
||||
if let Some(spans) =
|
||||
field_spans.filter(|spans| spans.len() > 0 && fields.len() == spans.len())
|
||||
|
|
|
|||
|
|
@ -1791,7 +1791,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
.into_items()
|
||||
.map(|(k, f)| (k, f.key()))
|
||||
.collect(),
|
||||
disambiguator: self.disambiguator,
|
||||
trait_map: self.trait_map,
|
||||
lifetime_elision_allowed: self.lifetime_elision_allowed,
|
||||
lint_buffer: Steal::new(self.lint_buffer),
|
||||
|
|
|
|||
|
|
@ -1107,8 +1107,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
// If such resolution is successful and gives the same result
|
||||
// (e.g. if the macro is re-imported), then silence the lint.
|
||||
let no_macro_rules = self.arenas.alloc_macro_rules_scope(MacroRulesScope::Empty);
|
||||
let ident = path.segments[0].ident;
|
||||
let fallback_binding = self.reborrow().resolve_ident_in_scope_set(
|
||||
path.segments[0].ident,
|
||||
ident,
|
||||
ScopeSet::Macro(MacroKind::Bang),
|
||||
&ParentScope { macro_rules: no_macro_rules, ..*parent_scope },
|
||||
None,
|
||||
|
|
@ -1116,7 +1117,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
None,
|
||||
None,
|
||||
);
|
||||
if fallback_binding.ok().and_then(|b| b.res().opt_def_id()) != Some(def_id) {
|
||||
if let Ok(fallback_binding) = fallback_binding
|
||||
&& fallback_binding.res().opt_def_id() == Some(def_id)
|
||||
{
|
||||
// Silence `unused_imports` on the fallback import as well.
|
||||
self.get_mut().record_use(ident, fallback_binding, Used::Other);
|
||||
} else {
|
||||
let location = match parent_scope.module.kind {
|
||||
ModuleKind::Def(kind, def_id, name) => {
|
||||
if let Some(name) = name {
|
||||
|
|
|
|||
|
|
@ -712,7 +712,8 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
|
|||
hir::definitions::DefPathData::ValueNs(..) => "v",
|
||||
hir::definitions::DefPathData::Closure => "C",
|
||||
hir::definitions::DefPathData::Ctor => "c",
|
||||
hir::definitions::DefPathData::AnonConst => "k",
|
||||
hir::definitions::DefPathData::AnonConst => "K",
|
||||
hir::definitions::DefPathData::LateAnonConst => "k",
|
||||
hir::definitions::DefPathData::OpaqueTy => "i",
|
||||
hir::definitions::DefPathData::SyntheticCoroutineBody => "s",
|
||||
hir::definitions::DefPathData::NestedStatic => "n",
|
||||
|
|
@ -722,6 +723,7 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
|
|||
| hir::definitions::DefPathData::MacroNs(..)
|
||||
| hir::definitions::DefPathData::OpaqueLifetime(..)
|
||||
| hir::definitions::DefPathData::LifetimeNs(..)
|
||||
| hir::definitions::DefPathData::DesugaredAnonymousLifetime
|
||||
| hir::definitions::DefPathData::AnonAssocTy(..) => {
|
||||
bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1386,10 +1386,12 @@ bitflags::bitflags! {
|
|||
const DIAGNOSTICS = 1 << 1;
|
||||
/// Apply remappings to debug information
|
||||
const DEBUGINFO = 1 << 3;
|
||||
/// Apply remappings to coverage information
|
||||
const COVERAGE = 1 << 4;
|
||||
|
||||
/// An alias for `macro` and `debuginfo`. This ensures all paths in compiled
|
||||
/// executables or libraries are remapped but not elsewhere.
|
||||
const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits();
|
||||
/// An alias for `macro`, `debuginfo` and `coverage`. This ensures all paths in compiled
|
||||
/// executables, libraries and objects are remapped but not elsewhere.
|
||||
const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits() | Self::COVERAGE.bits();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -869,8 +869,7 @@ mod desc {
|
|||
pub(crate) const parse_branch_protection: &str = "a `,` separated combination of `bti`, `gcs`, `pac-ret`, (optionally with `pc`, `b-key`, `leaf` if `pac-ret` is set)";
|
||||
pub(crate) const parse_proc_macro_execution_strategy: &str =
|
||||
"one of supported execution strategies (`same-thread`, or `cross-thread`)";
|
||||
pub(crate) const parse_remap_path_scope: &str =
|
||||
"comma separated list of scopes: `macro`, `diagnostics`, `debuginfo`, `object`, `all`";
|
||||
pub(crate) const parse_remap_path_scope: &str = "comma separated list of scopes: `macro`, `diagnostics`, `debuginfo`, `coverage`, `object`, `all`";
|
||||
pub(crate) const parse_inlining_threshold: &str =
|
||||
"either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number";
|
||||
pub(crate) const parse_llvm_module_flag: &str = "<key>:<type>:<value>:<behavior>. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)";
|
||||
|
|
@ -1705,6 +1704,7 @@ pub mod parse {
|
|||
"macro" => RemapPathScopeComponents::MACRO,
|
||||
"diagnostics" => RemapPathScopeComponents::DIAGNOSTICS,
|
||||
"debuginfo" => RemapPathScopeComponents::DEBUGINFO,
|
||||
"coverage" => RemapPathScopeComponents::COVERAGE,
|
||||
"object" => RemapPathScopeComponents::OBJECT,
|
||||
"all" => RemapPathScopeComponents::all(),
|
||||
_ => return false,
|
||||
|
|
|
|||
|
|
@ -174,7 +174,11 @@ pub fn categorize_crate_type(s: Symbol) -> Option<CrateType> {
|
|||
Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1)
|
||||
}
|
||||
|
||||
pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<CrateType> {
|
||||
pub fn collect_crate_types(
|
||||
session: &Session,
|
||||
backend_crate_types: &[CrateType],
|
||||
attrs: &[ast::Attribute],
|
||||
) -> Vec<CrateType> {
|
||||
// If we're generating a test executable, then ignore all other output
|
||||
// styles at all other locations
|
||||
if session.opts.test {
|
||||
|
|
@ -219,7 +223,12 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C
|
|||
}
|
||||
|
||||
base.retain(|crate_type| {
|
||||
if invalid_output_for_target(session, *crate_type) {
|
||||
if invalid_output_for_target(session, *crate_type)
|
||||
|| !backend_crate_types.contains(crate_type)
|
||||
{
|
||||
// FIXME provide a better warning for the case where the codegen
|
||||
// backend doesn't support it once cargo doesn't hard code this
|
||||
// warning message.
|
||||
session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget {
|
||||
crate_type: *crate_type,
|
||||
target_triple: &session.opts.target_triple,
|
||||
|
|
|
|||
|
|
@ -955,7 +955,24 @@ fn default_emitter(
|
|||
|
||||
if let HumanReadableErrorType::AnnotateSnippet = kind {
|
||||
let emitter =
|
||||
AnnotateSnippetEmitter::new(source_map, translator, short, macro_backtrace);
|
||||
AnnotateSnippetEmitter::new(stderr_destination(color_config), translator)
|
||||
.sm(source_map)
|
||||
.short_message(short)
|
||||
.diagnostic_width(sopts.diagnostic_width)
|
||||
.macro_backtrace(macro_backtrace)
|
||||
.track_diagnostics(track_diagnostics)
|
||||
.terminal_url(terminal_url)
|
||||
.theme(if let HumanReadableErrorType::Unicode = kind {
|
||||
OutputTheme::Unicode
|
||||
} else {
|
||||
OutputTheme::Ascii
|
||||
})
|
||||
.ignored_directories_in_source_blocks(
|
||||
sopts
|
||||
.unstable_opts
|
||||
.ignore_directory_in_diagnostics_source_blocks
|
||||
.clone(),
|
||||
);
|
||||
Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
|
||||
} else {
|
||||
let emitter = HumanEmitter::new(stderr_destination(color_config), translator)
|
||||
|
|
|
|||
|
|
@ -1247,6 +1247,7 @@ pub enum DesugaringKind {
|
|||
/// rewriting it.
|
||||
source: bool,
|
||||
},
|
||||
RangeExpr,
|
||||
}
|
||||
|
||||
impl DesugaringKind {
|
||||
|
|
@ -1268,6 +1269,7 @@ impl DesugaringKind {
|
|||
DesugaringKind::FormatLiteral { source: false } => {
|
||||
"expression that expanded into a format string literal"
|
||||
}
|
||||
DesugaringKind::RangeExpr => "range expression",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1287,6 +1289,7 @@ impl DesugaringKind {
|
|||
DesugaringKind::Contract => value == "Contract",
|
||||
DesugaringKind::PatTyRange => value == "PatTyRange",
|
||||
DesugaringKind::FormatLiteral { .. } => value == "FormatLiteral",
|
||||
DesugaringKind::RangeExpr => value == "RangeExpr",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue