Merge pull request #4656 from rust-lang/rustup-2025-10-30

Automatic Rustup
This commit is contained in:
Ralf Jung 2025-10-30 09:16:59 +00:00 committed by GitHub
commit 9cb215ada4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
664 changed files with 15920 additions and 17722 deletions

View file

@ -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>

View file

@ -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",
]

View file

@ -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)
===========================

View file

@ -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>>,
}

View file

@ -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>>) {

View file

@ -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,

View file

@ -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,
)

View file

@ -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 {

View file

@ -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(

View file

@ -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();

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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(

View file

@ -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(

View file

@ -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;

View file

@ -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> {

View file

@ -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(),

View file

@ -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,
);

View file

@ -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(

View file

@ -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 {

View file

@ -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");

View file

@ -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.

View file

@ -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());

View file

@ -289,6 +289,7 @@ pub(crate) enum AttributeKind {
DeadOnUnwind = 43,
DeadOnReturn = 44,
CapturesReadOnly = 45,
CapturesNone = 46,
}
/// LLVMIntPredicate

View file

@ -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 wont 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 {

View file

@ -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);

View file

@ -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."
);
}
}
}

View file

@ -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) {}

View file

@ -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)]

View file

@ -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

View file

@ -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,

View file

@ -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"

View file

@ -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))
}

View file

@ -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();

View file

@ -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),

View file

@ -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 },

View file

@ -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);

View file

@ -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;
}
}
}

View file

@ -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 => {}

View file

@ -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,
);
}

View file

@ -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);

View file

@ -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()
}

View file

@ -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()));

View file

@ -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);

View file

@ -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(())
}
}
}
}
}

View file

@ -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(

View file

@ -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,
..
}),
..

View file

@ -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};

View file

@ -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.

View file

@ -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

View file

@ -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);
}

View file

@ -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)

View file

@ -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));

View file

@ -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]

View file

@ -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))]

View file

@ -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());

View file

@ -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);

View file

@ -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"
));
}

View file

@ -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);

View file

@ -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

View file

@ -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 },
);

View file

@ -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,
};
}

View file

@ -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

View file

@ -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]

View file

@ -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)));

View file

@ -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 }

View file

@ -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 {

View file

@ -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>>>>,

View file

@ -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,

View file

@ -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

View file

@ -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)]

View file

@ -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 {

View file

@ -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(())

View file

@ -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>>()];
}

View file

@ -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.

View file

@ -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 {

View file

@ -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>,

View file

@ -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,

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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))
}

View 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())
}

View file

@ -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(

View file

@ -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)
}

View file

@ -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 {

View file

@ -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.

View file

@ -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> {

View file

@ -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())

View file

@ -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),

View file

@ -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 {

View file

@ -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);
}

View file

@ -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();
}
}

View file

@ -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,

View file

@ -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,

View file

@ -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)

View file

@ -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