diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2b5699dcd098..3f052eca48e6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ the Zulip stream is the best place to *ask* for help. Documentation for contributing to the compiler or tooling is located in the [Guide to Rustc Development][rustc-dev-guide], commonly known as the [rustc-dev-guide]. Documentation for the -standard library in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide]. +standard library is in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide]. ## Making changes to subtrees and submodules diff --git a/Cargo.lock b/Cargo.lock index 23abc6626aab..4f98a3facf9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3539,6 +3539,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", + "rustc_data_structures", "rustc_errors", "rustc_feature", "rustc_hir", @@ -3571,7 +3572,6 @@ dependencies = [ "rustc_abi", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_graphviz", "rustc_hir", "rustc_index", @@ -3687,7 +3687,6 @@ dependencies = [ "serde_json", "smallvec", "tempfile", - "thin-vec", "thorin-dwp", "tracing", "wasm-encoder 0.219.2", @@ -3704,7 +3703,6 @@ dependencies = [ "rustc_ast", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", "rustc_index", "rustc_infer", @@ -3771,7 +3769,6 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", - "rustc_borrowck", "rustc_codegen_ssa", "rustc_const_eval", "rustc_data_structures", @@ -3791,13 +3788,11 @@ dependencies = [ "rustc_mir_build", "rustc_mir_transform", "rustc_parse", - "rustc_passes", "rustc_public", "rustc_resolve", "rustc_session", "rustc_span", "rustc_target", - "rustc_trait_selection", "serde_json", "shlex", "tracing", @@ -3892,19 +3887,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "rustc_fluent_macro" -version = "0.0.0" -dependencies = [ - "annotate-snippets 0.11.5", - "fluent-bundle", - "fluent-syntax", - "proc-macro2", - "quote", - "syn 2.0.110", - "unic-langid", -] - [[package]] name = "rustc_fs_util" version = "0.0.0" @@ -3959,7 +3941,6 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_feature", - "rustc_fluent_macro", "rustc_hir", "rustc_index", "rustc_infer", @@ -4148,7 +4129,6 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_feature", - "rustc_fluent_macro", "rustc_hir", "rustc_index", "rustc_infer", @@ -4286,7 +4266,6 @@ dependencies = [ "rustc_ast", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", "rustc_index", "rustc_infer", @@ -4388,7 +4367,6 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_feature", - "rustc_fluent_macro", "rustc_index", "rustc_lexer", "rustc_macros", @@ -4421,7 +4399,6 @@ dependencies = [ "rustc_errors", "rustc_expand", "rustc_feature", - "rustc_fluent_macro", "rustc_hir", "rustc_index", "rustc_macros", @@ -4515,6 +4492,7 @@ name = "rustc_query_impl" version = "0.0.0" dependencies = [ "measureme", + "rustc_abi", "rustc_data_structures", "rustc_errors", "rustc_hashes", @@ -4524,7 +4502,9 @@ dependencies = [ "rustc_middle", "rustc_query_system", "rustc_serialize", + "rustc_session", "rustc_span", + "rustc_thread_pool", "tracing", ] @@ -4570,7 +4550,6 @@ dependencies = [ "rustc_macros", "rustc_metadata", "rustc_middle", - "rustc_query_system", "rustc_session", "rustc_span", "smallvec", @@ -4724,7 +4703,6 @@ dependencies = [ "rustc_ast", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", "rustc_infer", "rustc_macros", @@ -5586,7 +5564,6 @@ dependencies = [ "build_helper", "cargo_metadata 0.21.0", "clap", - "fluent-syntax", "globset", "ignore", "miropt-test-tools", diff --git a/INSTALL.md b/INSTALL.md index dd6c64b3df5f..0e998101fcd1 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -233,7 +233,7 @@ itself back on after some time). ### MSVC -MSVC builds of Rust additionally requires an installation of: +MSVC builds of Rust additionally require an installation of: - Visual Studio 2022 (or later) build tools so `rustc` can use its linker. Older Visual Studio versions such as 2019 *may* work but aren't actively tested. diff --git a/RELEASES.md b/RELEASES.md index 424e12ceec05..29c787f4e14c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,12 @@ +Version 1.93.1 (2026-02-12) +=========================== + + + +- [Don't try to recover keyword as non-keyword identifier](https://github.com/rust-lang/rust/pull/150590), fixing an ICE that especially [affected rustfmt](https://github.com/rust-lang/rustfmt/issues/6739). +- [Fix `clippy::panicking_unwrap` false-positive on field access with implicit deref](https://github.com/rust-lang/rust-clippy/pull/16196). +- [Revert "Update wasm-related dependencies in CI"](https://github.com/rust-lang/rust/pull/152259), fixing file descriptor leaks on the `wasm32-wasip2` target. + Version 1.93.0 (2026-01-22) ========================== @@ -1546,7 +1555,7 @@ Compatibility Notes - [Check well-formedness of the source type's signature in fn pointer casts.](https://github.com/rust-lang/rust/pull/129021) This partly closes a soundness hole that comes when casting a function item to function pointer - [Use equality instead of subtyping when resolving type dependent paths.](https://github.com/rust-lang/rust/pull/129073) - Linking on macOS now correctly includes Rust's default deployment target. Due to a linker bug, you might have to pass `MACOSX_DEPLOYMENT_TARGET` or fix your `#[link]` attributes to point to the correct frameworks. See . -- [Rust will now correctly raise an error for `repr(Rust)` written on non-`struct`/`enum`/`union` items, since it previous did not have any effect.](https://github.com/rust-lang/rust/pull/129422) +- [Rust will now correctly raise an error for `repr(Rust)` written on non-`struct`/`enum`/`union` items, since it previously did not have any effect.](https://github.com/rust-lang/rust/pull/129422) - The future incompatibility lint `deprecated_cfg_attr_crate_type_name` [has been made into a hard error](https://github.com/rust-lang/rust/pull/129670). It was used to deny usage of `#![crate_type]` and `#![crate_name]` attributes in `#![cfg_attr]`, which required a hack in the compiler to be able to change the used crate type and crate name after cfg expansion. Users can use `--crate-type` instead of `#![cfg_attr(..., crate_type = "...")]` and `--crate-name` instead of `#![cfg_attr(..., crate_name = "...")]` when running `rustc`/`cargo rustc` on the command line. Use of those two attributes outside of `#![cfg_attr]` continue to be fully supported. @@ -1722,7 +1731,7 @@ Cargo Compatibility Notes ------------------- - We now [disallow setting some built-in cfgs via the command-line](https://github.com/rust-lang/rust/pull/126158) with the newly added [`explicit_builtin_cfgs_in_flags`](https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#explicit-builtin-cfgs-in-flags) lint in order to prevent incoherent state, eg. `windows` cfg active but target is Linux based. The appropriate [`rustc` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html) should be used instead. -- The standard library has a new implementation of `binary_search` which is significantly improves performance ([#128254](https://github.com/rust-lang/rust/pull/128254)). However when a sorted slice has multiple values which compare equal, the new implementation may select a different value among the equal ones than the old implementation. +- The standard library has a new implementation of `binary_search` which significantly improves performance ([#128254](https://github.com/rust-lang/rust/pull/128254)). However when a sorted slice has multiple values which compare equal, the new implementation may select a different value among the equal ones than the old implementation. - [illumos/Solaris now sets `MSG_NOSIGNAL` when writing to sockets](https://github.com/rust-lang/rust/pull/128259). This avoids killing the process with SIGPIPE when writing to a closed socket, which matches the existing behavior on other UNIX targets. - [Removes a problematic hack that always passed the --whole-archive linker flag for tests, which may cause linker errors for code accidentally relying on it.](https://github.com/rust-lang/rust/pull/128400) - The WebAssembly target features `multivalue` and `reference-types` are now @@ -1872,7 +1881,7 @@ These changes do not affect any public interfaces of Rust, but they represent significant improvements to the performance or internals of rustc and related tools. -- [Add a Rust-for Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/) +- [Add a Rust-for-Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/) Version 1.80.1 (2024-08-08) =========================== @@ -4510,7 +4519,7 @@ Compatibility Notes saturating to `0` instead][89926]. In the real world the panic happened mostly on platforms with buggy monotonic clock implementations rather than catching programming errors like reversing the start and end times. Such programming - errors will now results in `0` rather than a panic. + errors will now result in `0` rather than a panic. - In a future release we're planning to increase the baseline requirements for the Linux kernel to version 3.2, and for glibc to version 2.17. We'd love your feedback in [PR #95026][95026]. diff --git a/compiler/rustc_abi/src/callconv/reg.rs b/compiler/rustc_abi/src/callconv/reg.rs index 66c8056d0c2a..66d4dca00726 100644 --- a/compiler/rustc_abi/src/callconv/reg.rs +++ b/compiler/rustc_abi/src/callconv/reg.rs @@ -35,6 +35,7 @@ impl Reg { reg_ctor!(f32, Float, 32); reg_ctor!(f64, Float, 64); + reg_ctor!(f128, Float, 128); } impl Reg { diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 41ad14f550ab..aafb124986e1 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -290,7 +290,19 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { /// function call isn't allowed (a.k.a. `va_list`). /// /// This function handles transparent types automatically. - pub fn pass_indirectly_in_non_rustic_abis(mut self, cx: &C) -> bool + pub fn pass_indirectly_in_non_rustic_abis(self, cx: &C) -> bool + where + Ty: TyAbiInterface<'a, C> + Copy, + { + let base = self.peel_transparent_wrappers(cx); + Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(base) + } + + /// Recursively peel away transparent wrappers, returning the inner value. + /// + /// The return value is not `repr(transparent)` and/or does + /// not have a non-1zst field. + pub fn peel_transparent_wrappers(mut self, cx: &C) -> Self where Ty: TyAbiInterface<'a, C> + Copy, { @@ -300,7 +312,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { self = field; } - Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(self) + self } /// Finds the one field that is not a 1-ZST. diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 061ad8617893..85f31b75e240 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1,6 +1,6 @@ // tidy-alphabetical-start +#![cfg_attr(all(feature = "nightly", bootstrap), feature(assert_matches))] #![cfg_attr(feature = "nightly", allow(internal_features))] -#![cfg_attr(feature = "nightly", feature(assert_matches))] #![cfg_attr(feature = "nightly", feature(rustc_attrs))] #![cfg_attr(feature = "nightly", feature(step_trait))] // tidy-alphabetical-end diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 6217bf46a942..97dd21db07e7 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -13,7 +13,6 @@ #![cfg_attr(test, feature(test))] #![deny(unsafe_op_in_unsafe_fn)] #![doc(test(no_crate_inject, attr(deny(warnings), allow(internal_features))))] -#![feature(core_intrinsics)] #![feature(decl_macro)] #![feature(dropck_eyepatch)] #![feature(never_type)] @@ -26,7 +25,7 @@ use std::cell::{Cell, RefCell}; use std::marker::PhantomData; use std::mem::{self, MaybeUninit}; use std::ptr::{self, NonNull}; -use std::{cmp, intrinsics, slice}; +use std::{cmp, hint, slice}; use smallvec::SmallVec; @@ -452,7 +451,7 @@ impl DroplessArena { let bytes = align_up(layout.size(), DROPLESS_ALIGNMENT); // Tell LLVM that `end` is aligned to DROPLESS_ALIGNMENT. - unsafe { intrinsics::assume(end == align_down(end, DROPLESS_ALIGNMENT)) }; + unsafe { hint::assert_unchecked(end == align_down(end, DROPLESS_ALIGNMENT)) }; if let Some(sub) = end.checked_sub(bytes) { let new_end = align_down(sub, layout.align()); diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 591abdaa2683..fa323b7cf581 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -656,11 +656,7 @@ impl Pat { // A tuple pattern `(P0, .., Pn)` can be reparsed as `(T0, .., Tn)` // assuming `T0` to `Tn` are all syntactically valid as types. PatKind::Tuple(pats) => { - let mut tys = ThinVec::with_capacity(pats.len()); - // FIXME(#48994) - could just be collected into an Option - for pat in pats { - tys.push(pat.to_ty()?); - } + let tys = pats.iter().map(|pat| pat.to_ty()).collect::>>()?; TyKind::Tup(tys) } _ => return None, @@ -3873,27 +3869,44 @@ pub struct ConstItem { pub ident: Ident, pub generics: Generics, pub ty: Box, - pub rhs: Option, + pub rhs_kind: ConstItemRhsKind, pub define_opaque: Option>, } #[derive(Clone, Encodable, Decodable, Debug, Walkable)] -pub enum ConstItemRhs { - TypeConst(AnonConst), - Body(Box), +pub enum ConstItemRhsKind { + Body { rhs: Option> }, + TypeConst { rhs: Option }, } -impl ConstItemRhs { - pub fn span(&self) -> Span { - self.expr().span +impl ConstItemRhsKind { + pub fn new_body(rhs: Box) -> Self { + Self::Body { rhs: Some(rhs) } } - pub fn expr(&self) -> &Expr { + pub fn span(&self) -> Option { + Some(self.expr()?.span) + } + + pub fn expr(&self) -> Option<&Expr> { match self { - ConstItemRhs::TypeConst(anon_const) => &anon_const.value, - ConstItemRhs::Body(expr) => expr, + Self::Body { rhs: Some(body) } => Some(&body), + Self::TypeConst { rhs: Some(anon) } => Some(&anon.value), + _ => None, } } + + pub fn has_expr(&self) -> bool { + match self { + Self::Body { rhs: Some(_) } => true, + Self::TypeConst { rhs: Some(_) } => true, + _ => false, + } + } + + pub fn is_type_const(&self) -> bool { + matches!(self, &Self::TypeConst { .. }) + } } #[derive(Clone, Encodable, Decodable, Debug, Walkable)] diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 51614460d3c4..8556e8288670 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -427,7 +427,7 @@ macro_rules! common_visitor_and_walkers { Const, ConstBlockItem, ConstItem, - ConstItemRhs, + ConstItemRhsKind, Defaultness, Delegation, DelegationMac, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 051bebfbec00..d9bf6b52f31f 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -966,14 +966,14 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr)); this.arena.alloc(this.expr(gen_future_span, expr_break)) }); - self.arm(ready_pat, break_x) + self.arm(ready_pat, break_x, span) }; // `::std::task::Poll::Pending => {}` let pending_arm = { let pending_pat = self.pat_lang_item_variant(span, hir::LangItem::PollPending, &[]); let empty_block = self.expr_block_empty(span); - self.arm(pending_pat, empty_block) + self.arm(pending_pat, empty_block, span) }; let inner_match_stmt = { @@ -1027,7 +1027,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }); // mut __awaitee => loop { ... } - let awaitee_arm = self.arm(awaitee_pat, loop_expr); + let awaitee_arm = self.arm(awaitee_pat, loop_expr, span); // `match ::std::future::IntoFuture::into_future() { ... }` let into_future_expr = match await_kind { @@ -1817,7 +1817,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let break_expr = self.with_loop_scope(loop_hir_id, |this| this.expr_break_alloc(for_span)); let pat = self.pat_none(for_span); - self.arm(pat, break_expr) + self.arm(pat, break_expr, for_span) }; // Some() => , @@ -1826,7 +1826,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let body_block = self.with_loop_scope(loop_hir_id, |this| this.lower_block(body, false)); let body_expr = self.arena.alloc(self.expr_block(body_block)); - self.arm(some_pat, body_expr) + self.arm(some_pat, body_expr, for_span) }; // `mut iter` @@ -1885,7 +1885,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let loop_expr = self.arena.alloc(hir::Expr { hir_id: loop_hir_id, kind, span: for_span }); // `mut iter => { ... }` - let iter_arm = self.arm(iter_pat, loop_expr); + let iter_arm = self.arm(iter_pat, loop_expr, for_span); let match_expr = match loop_kind { ForLoopKind::For => { @@ -1930,7 +1930,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::LangItem::IntoAsyncIterIntoIter, arena_vec![self; head], ); - let iter_arm = self.arm(async_iter_pat, inner_match_expr); + let iter_arm = self.arm(async_iter_pat, inner_match_expr, for_span); self.arena.alloc(self.expr_match( for_span, iter, @@ -1997,7 +1997,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let val_expr = self.expr_ident(span, val_ident, val_pat_nid); self.lower_attrs(val_expr.hir_id, &attrs, span, Target::Expression); let continue_pat = self.pat_cf_continue(unstable_span, val_pat); - self.arm(continue_pat, val_expr) + self.arm(continue_pat, val_expr, try_span) }; // `ControlFlow::Break(residual) => @@ -2040,7 +2040,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_attrs(ret_expr.hir_id, &attrs, span, Target::Expression); let break_pat = self.pat_cf_break(try_span, residual_local); - self.arm(break_pat, ret_expr) + self.arm(break_pat, ret_expr, try_span) }; hir::ExprKind::Match( @@ -2368,12 +2368,13 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, pat: &'hir hir::Pat<'hir>, expr: &'hir hir::Expr<'hir>, + span: Span, ) -> hir::Arm<'hir> { hir::Arm { hir_id: self.next_id(), pat, guard: None, - span: self.lower_span(expr.span), + span: self.lower_span(span), body: expr, } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index b497c6beeb98..9922ed8a5c58 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -288,7 +288,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ident, generics, ty, - rhs, + rhs_kind, define_opaque, }) => { let ident = self.lower_ident(*ident); @@ -301,7 +301,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy), ); - let rhs = this.lower_const_item_rhs(attrs, rhs.as_ref(), span); + let rhs = this.lower_const_item_rhs(rhs_kind, span); (ty, rhs) }, ); @@ -827,7 +827,10 @@ impl<'hir> LoweringContext<'_, 'hir> { hir_id, def_id: self.local_def_id(v.id), data: self.lower_variant_data(hir_id, item_kind, &v.data), - disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const_to_anon_const(e)), + disr_expr: v + .disr_expr + .as_ref() + .map(|e| self.lower_anon_const_to_anon_const(e, e.value.span)), ident: self.lower_ident(v.ident), span: self.lower_span(v.span), } @@ -917,7 +920,10 @@ impl<'hir> LoweringContext<'_, 'hir> { None => Ident::new(sym::integer(index), self.lower_span(f.span)), }, vis_span: self.lower_span(f.vis.span), - default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)), + default: f + .default + .as_ref() + .map(|v| self.lower_anon_const_to_anon_const(v, v.value.span)), ty, safety: self.lower_safety(f.safety, hir::Safety::Safe), } @@ -935,7 +941,12 @@ impl<'hir> LoweringContext<'_, 'hir> { let (ident, generics, kind, has_default) = match &i.kind { AssocItemKind::Const(box ConstItem { - ident, generics, ty, rhs, define_opaque, .. + ident, + generics, + ty, + rhs_kind, + define_opaque, + .. }) => { let (generics, kind) = self.lower_generics( generics, @@ -946,15 +957,18 @@ impl<'hir> LoweringContext<'_, 'hir> { ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy), ); - let rhs = rhs - .as_ref() - .map(|rhs| this.lower_const_item_rhs(attrs, Some(rhs), i.span)); - hir::TraitItemKind::Const(ty, rhs) + // Trait associated consts don't need an expression/body. + let rhs = if rhs_kind.has_expr() { + Some(this.lower_const_item_rhs(rhs_kind, i.span)) + } else { + None + }; + hir::TraitItemKind::Const(ty, rhs, rhs_kind.is_type_const().into()) }, ); if define_opaque.is_some() { - if rhs.is_some() { + if rhs_kind.has_expr() { self.lower_define_opaque(hir_id, &define_opaque); } else { self.dcx().span_err( @@ -964,7 +978,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - (*ident, generics, kind, rhs.is_some()) + (*ident, generics, kind, rhs_kind.has_expr()) } AssocItemKind::Fn(box Fn { sig, ident, generics, body: None, define_opaque, .. @@ -1148,7 +1162,12 @@ impl<'hir> LoweringContext<'_, 'hir> { let (ident, (generics, kind)) = match &i.kind { AssocItemKind::Const(box ConstItem { - ident, generics, ty, rhs, define_opaque, .. + ident, + generics, + ty, + rhs_kind, + define_opaque, + .. }) => ( *ident, self.lower_generics( @@ -1161,7 +1180,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy), ); this.lower_define_opaque(hir_id, &define_opaque); - let rhs = this.lower_const_item_rhs(attrs, rhs.as_ref(), i.span); + let rhs = this.lower_const_item_rhs(rhs_kind, i.span); hir::ImplItemKind::Const(ty, rhs) }, ), @@ -1391,7 +1410,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // create a fake body so that the entire rest of the compiler doesn't have to deal with // this as a special case. return self.lower_fn_body(decl, contract, |this| { - if attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)) + if find_attr!(attrs, AttributeKind::RustcIntrinsic) || this.tcx.is_sdylib_interface_build() { let span = this.lower_span(span); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index e4f431fd2431..ff5edfc79943 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2374,15 +2374,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_const_item_rhs( &mut self, - attrs: &[hir::Attribute], - rhs: Option<&ConstItemRhs>, + rhs_kind: &ConstItemRhsKind, span: Span, ) -> hir::ConstItemRhs<'hir> { - match rhs { - Some(ConstItemRhs::TypeConst(anon)) => { + match rhs_kind { + ConstItemRhsKind::Body { rhs: Some(body) } => { + hir::ConstItemRhs::Body(self.lower_const_body(span, Some(body))) + } + ConstItemRhsKind::Body { rhs: None } => { + hir::ConstItemRhs::Body(self.lower_const_body(span, None)) + } + ConstItemRhsKind::TypeConst { rhs: Some(anon) } => { hir::ConstItemRhs::TypeConst(self.lower_anon_const_to_const_arg_and_alloc(anon)) } - None if find_attr!(attrs, AttributeKind::TypeConst(_)) => { + ConstItemRhsKind::TypeConst { rhs: None } => { let const_arg = ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Error( @@ -2392,10 +2397,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; hir::ConstItemRhs::TypeConst(self.arena.alloc(const_arg)) } - Some(ConstItemRhs::Body(body)) => { - hir::ConstItemRhs::Body(self.lower_const_body(span, Some(body))) - } - None => hir::ConstItemRhs::Body(self.lower_const_body(span, None)), } } @@ -2425,15 +2426,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ); let lowered_args = self.arena.alloc_from_iter(args.iter().map(|arg| { - let const_arg = if let ExprKind::ConstBlock(anon_const) = &arg.kind { - let def_id = self.local_def_id(anon_const.id); - let def_kind = self.tcx.def_kind(def_id); - assert_eq!(DefKind::AnonConst, def_kind); - self.lower_anon_const_to_const_arg(anon_const) - } else { - self.lower_expr_to_const_arg_direct(arg) - }; - + let const_arg = self.lower_expr_to_const_arg_direct(arg); &*self.arena.alloc(const_arg) })); @@ -2445,16 +2438,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } ExprKind::Tup(exprs) => { let exprs = self.arena.alloc_from_iter(exprs.iter().map(|expr| { - let expr = if let ExprKind::ConstBlock(anon_const) = &expr.kind { - let def_id = self.local_def_id(anon_const.id); - let def_kind = self.tcx.def_kind(def_id); - assert_eq!(DefKind::AnonConst, def_kind); - - self.lower_anon_const_to_const_arg(anon_const) - } else { - self.lower_expr_to_const_arg_direct(&expr) - }; - + let expr = self.lower_expr_to_const_arg_direct(&expr); &*self.arena.alloc(expr) })); @@ -2494,16 +2478,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // then go unused as the `Target::ExprField` is not actually // corresponding to `Node::ExprField`. self.lower_attrs(hir_id, &f.attrs, f.span, Target::ExprField); - - let expr = if let ExprKind::ConstBlock(anon_const) = &f.expr.kind { - let def_id = self.local_def_id(anon_const.id); - let def_kind = self.tcx.def_kind(def_id); - assert_eq!(DefKind::AnonConst, def_kind); - - self.lower_anon_const_to_const_arg(anon_const) - } else { - self.lower_expr_to_const_arg_direct(&f.expr) - }; + let expr = self.lower_expr_to_const_arg_direct(&f.expr); &*self.arena.alloc(hir::ConstArgExprField { hir_id, @@ -2521,13 +2496,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } ExprKind::Array(elements) => { let lowered_elems = self.arena.alloc_from_iter(elements.iter().map(|element| { - let const_arg = if let ExprKind::ConstBlock(anon_const) = &element.kind { - let def_id = self.local_def_id(anon_const.id); - assert_eq!(DefKind::AnonConst, self.tcx.def_kind(def_id)); - self.lower_anon_const_to_const_arg(anon_const) - } else { - self.lower_expr_to_const_arg_direct(element) - }; + let const_arg = self.lower_expr_to_const_arg_direct(element); &*self.arena.alloc(const_arg) })); let array_expr = self.arena.alloc(hir::ConstArgArrayExpr { @@ -2557,6 +2526,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { | ExprKind::Call(..) | ExprKind::Tup(..) | ExprKind::Array(..) + | ExprKind::ConstBlock(..) ) { return self.lower_expr_to_const_arg_direct(expr); @@ -2570,10 +2540,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ConstArg { hir_id: self.lower_node_id(expr.id), - kind: hir::ConstArgKind::Literal(literal.node), + kind: hir::ConstArgKind::Literal { lit: literal.node, negated: false }, span, } } + ExprKind::Unary(UnOp::Neg, inner_expr) + if let ExprKind::Lit(literal) = &inner_expr.kind => + { + let span = expr.span; + let literal = self.lower_lit(literal, span); + + ConstArg { + hir_id: self.lower_node_id(expr.id), + kind: hir::ConstArgKind::Literal { lit: literal.node, negated: true }, + span, + } + } + ExprKind::ConstBlock(anon_const) => { + let def_id = self.local_def_id(anon_const.id); + assert_eq!(DefKind::AnonConst, self.tcx.def_kind(def_id)); + self.lower_anon_const_to_const_arg(anon_const, span) + } _ => overly_complex_const(self), } } @@ -2584,11 +2571,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, anon: &AnonConst, ) -> &'hir hir::ConstArg<'hir> { - self.arena.alloc(self.lower_anon_const_to_const_arg(anon)) + self.arena.alloc(self.lower_anon_const_to_const_arg(anon, anon.value.span)) } #[instrument(level = "debug", skip(self))] - fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> hir::ConstArg<'hir> { + fn lower_anon_const_to_const_arg( + &mut self, + anon: &AnonConst, + span: Span, + ) -> hir::ConstArg<'hir> { let tcx = self.tcx; // We cannot change parsing depending on feature gates available, @@ -2599,7 +2590,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { if tcx.features().min_generic_const_args() { return match anon.mgca_disambiguation { MgcaDisambiguation::AnonConst => { - let lowered_anon = self.lower_anon_const_to_anon_const(anon); + let lowered_anon = self.lower_anon_const_to_anon_const(anon, span); ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Anon(lowered_anon), @@ -2645,7 +2636,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; } - let lowered_anon = self.lower_anon_const_to_anon_const(anon); + let lowered_anon = self.lower_anon_const_to_anon_const(anon, anon.value.span); ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Anon(lowered_anon), @@ -2655,7 +2646,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// See [`hir::ConstArg`] for when to use this function vs /// [`Self::lower_anon_const_to_const_arg`]. - fn lower_anon_const_to_anon_const(&mut self, c: &AnonConst) -> &'hir hir::AnonConst { + fn lower_anon_const_to_anon_const( + &mut self, + c: &AnonConst, + span: Span, + ) -> &'hir hir::AnonConst { self.arena.alloc(self.with_new_scopes(c.value.span, |this| { let def_id = this.local_def_id(c.id); let hir_id = this.lower_node_id(c.id); @@ -2663,7 +2658,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { def_id, hir_id, body: this.lower_const_body(c.value.span, Some(&c.value)), - span: this.lower_span(c.value.span), + span: this.lower_span(span), } })) } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 2457e0a777e4..b9fb20b68971 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1361,9 +1361,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } }); } - ItemKind::Const(box ConstItem { defaultness, ident, rhs, .. }) => { + ItemKind::Const(box ConstItem { defaultness, ident, rhs_kind, .. }) => { self.check_defaultness(item.span, *defaultness); - if rhs.is_none() { + if !rhs_kind.has_expr() { self.dcx().emit_err(errors::ConstWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), @@ -1715,11 +1715,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if let AssocCtxt::Impl { .. } = ctxt { match &item.kind { - AssocItemKind::Const(box ConstItem { rhs: None, .. }) => { - self.dcx().emit_err(errors::AssocConstWithoutBody { - span: item.span, - replace_span: self.ending_semi_or_hi(item.span), - }); + AssocItemKind::Const(box ConstItem { rhs_kind, .. }) => { + if !rhs_kind.has_expr() { + self.dcx().emit_err(errors::AssocConstWithoutBody { + span: item.span, + replace_span: self.ending_semi_or_hi(item.span), + }); + } } AssocItemKind::Fn(box Fn { body, .. }) => { if body.is_none() && !self.is_sdylib_interface { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index baf6f6beaeed..dd260aede489 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -3,7 +3,7 @@ use rustc_abi::ExternAbi; use rustc_ast::ParamKindOrd; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic, inline_fluent}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; @@ -503,7 +503,7 @@ pub(crate) struct AutoTraitItems { #[primary_span] pub spans: Vec, #[suggestion( - "remove the super traits or lifetime bounds", + "remove the associated items", code = "", applicability = "machine-applicable", style = "tool-only" @@ -927,19 +927,15 @@ pub(crate) struct FeatureOnNonNightly { pub sugg: Option, } +#[derive(Subdiagnostic)] +#[help( + "the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable" +)] pub(crate) struct StableFeature { pub name: Symbol, pub since: Symbol, } -impl Subdiagnostic for StableFeature { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { - diag.arg("name", self.name); - diag.arg("since", self.since); - diag.help(inline_fluent!("the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable")); - } -} - #[derive(Diagnostic)] #[diag("`{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed")] #[help("remove one of these features")] @@ -950,6 +946,16 @@ pub(crate) struct IncompatibleFeatures { pub f2: Symbol, } +#[derive(Diagnostic)] +#[diag("`{$parent}` requires {$missing} to be enabled")] +#[help("enable all of these features")] +pub(crate) struct MissingDependentFeatures { + #[primary_span] + pub parent_span: Span, + pub parent: Symbol, + pub missing: String, +} + #[derive(Diagnostic)] #[diag("negative bounds are not supported")] pub(crate) struct NegativeBoundUnsupported { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 1b1bbb1564c4..34c73b545df8 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -249,6 +249,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ty), .. }) => { self.check_impl_trait(ty, false) } + ast::ItemKind::Const(box ast::ConstItem { + rhs_kind: ast::ConstItemRhsKind::TypeConst { .. }, + .. + }) => { + // Make sure this is only allowed if the feature gate is enabled. + // #![feature(min_generic_const_args)] + gate!(&self, min_generic_const_args, i.span, "top-level `type const` are unstable"); + } _ => {} } @@ -330,15 +338,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn visit_expr(&mut self, e: &'a ast::Expr) { match e.kind { ast::ExprKind::TryBlock(_, None) => { + // `try { ... }` is old and is only gated post-expansion here. gate!(&self, try_blocks, e.span, "`try` expression is experimental"); } ast::ExprKind::TryBlock(_, Some(_)) => { - gate!( - &self, - try_blocks_heterogeneous, - e.span, - "`try bikeshed` expression is experimental" - ); + // `try_blocks_heterogeneous` is new, and gated pre-expansion instead. } ast::ExprKind::Lit(token::Lit { kind: token::LitKind::Float | token::LitKind::Integer, @@ -422,6 +426,20 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } false } + ast::AssocItemKind::Const(box ast::ConstItem { + rhs_kind: ast::ConstItemRhsKind::TypeConst { .. }, + .. + }) => { + // Make sure this is only allowed if the feature gate is enabled. + // #![feature(min_generic_const_args)] + gate!( + &self, + min_generic_const_args, + i.span, + "associated `type const` are unstable" + ); + false + } _ => false, }; if let ast::Defaultness::Default(_) = i.kind.defaultness() { @@ -441,6 +459,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { maybe_stage_features(sess, features, krate); check_incompatible_features(sess, features); + check_dependent_features(sess, features); check_new_solver_banned_features(sess, features); let mut visitor = PostExpansionVisitor { sess, features }; @@ -499,6 +518,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { half_open_range_patterns_in_slices, "half-open range patterns in slices are unstable" ); + gate_all!(try_blocks_heterogeneous, "`try bikeshed` expression is experimental"); gate_all!(yeet_expr, "`do yeet` expression is experimental"); gate_all!(const_closures, "const closures are experimental"); gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); @@ -527,6 +547,27 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { } } } + // `mgca_type_const_syntax` is part of `min_generic_const_args` so either + // or both are enabled we don't need to emit a feature error. + if let Some(spans) = spans.get(&sym::mgca_type_const_syntax) { + for span in spans { + if visitor.features.min_generic_const_args() + || visitor.features.mgca_type_const_syntax() + || span.allows_unstable(sym::min_generic_const_args) + || span.allows_unstable(sym::mgca_type_const_syntax) + { + continue; + } + feature_err( + &visitor.sess, + sym::min_generic_const_args, + *span, + "`type const` syntax is experimental", + ) + .emit(); + } + } + gate_all!(global_registration, "global registration is experimental"); gate_all!(return_type_notation, "return type notation is experimental"); gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); @@ -649,6 +690,27 @@ fn check_incompatible_features(sess: &Session, features: &Features) { } } +fn check_dependent_features(sess: &Session, features: &Features) { + for &(parent, children) in + rustc_feature::DEPENDENT_FEATURES.iter().filter(|(parent, _)| features.enabled(*parent)) + { + if children.iter().any(|f| !features.enabled(*f)) { + let parent_span = features + .enabled_features_iter_stable_order() + .find_map(|(name, span)| (name == parent).then_some(span)) + .unwrap(); + // FIXME: should probably format this in fluent instead of here + let missing = children + .iter() + .filter(|f| !features.enabled(**f)) + .map(|s| format!("`{}`", s.as_str())) + .intersperse(String::from(", ")) + .collect(); + sess.dcx().emit_err(errors::MissingDependentFeatures { parent_span, parent, missing }); + } + } +} + fn check_new_solver_banned_features(sess: &Session, features: &Features) { if !sess.opts.unstable_opts.next_solver.globally { return; diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 2630348c49c1..7793f786cefe 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -5,6 +5,7 @@ // tidy-alphabetical-start #![feature(box_patterns)] #![feature(if_let_guard)] +#![feature(iter_intersperse)] #![feature(iter_is_partitioned)] // tidy-alphabetical-end diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 68054b06e39f..c7f110a2e003 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -221,7 +221,7 @@ impl<'a> State<'a> { ident, generics, ty, - rhs, + rhs_kind, define_opaque, }) => { self.print_item_const( @@ -229,7 +229,7 @@ impl<'a> State<'a> { None, generics, ty, - rhs.as_ref().map(|ct| ct.expr()), + rhs_kind.expr(), &item.vis, ast::Safety::Default, *defaultness, @@ -573,7 +573,7 @@ impl<'a> State<'a> { ident, generics, ty, - rhs, + rhs_kind, define_opaque, }) => { self.print_item_const( @@ -581,7 +581,7 @@ impl<'a> State<'a> { None, generics, ty, - rhs.as_ref().map(|ct| ct.expr()), + rhs_kind.expr(), vis, ast::Safety::Default, *defaultness, diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 411f3f5ccbd1..0a11a2da0dcf 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index d825d770fa35..4f1a8cd8b403 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -29,7 +29,7 @@ impl CombineAttributeParser for AllowInternalUnstableParser { pub(crate) struct UnstableFeatureBoundParser; impl CombineAttributeParser for UnstableFeatureBoundParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound]; + const PATH: &[rustc_span::Symbol] = &[sym::unstable_feature_bound]; type Item = (Symbol, Span); const CONVERT: ConvertFn = |items, _| AttributeKind::UnstableFeatureBound(items); const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index 4005ad2cba11..b6cb5b4504ee 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -1,22 +1,35 @@ use rustc_ast::token::Token; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AttrStyle, NodeId, token}; +use rustc_data_structures::fx::FxHashMap; use rustc_feature::{AttributeTemplate, Features}; use rustc_hir::attrs::CfgEntry; use rustc_hir::{AttrPath, Target}; use rustc_parse::exp; use rustc_parse::parser::{Parser, Recovery}; use rustc_session::Session; -use rustc_span::{ErrorGuaranteed, Span, sym}; +use rustc_session::lint::BuiltinLintDiag; +use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES; +use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use crate::parser::MetaItemOrLitParser; use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry}; +#[derive(Clone)] pub enum CfgSelectPredicate { Cfg(CfgEntry), Wildcard(Token), } +impl CfgSelectPredicate { + fn span(&self) -> Span { + match self { + CfgSelectPredicate::Cfg(cfg_entry) => cfg_entry.span(), + CfgSelectPredicate::Wildcard(token) => token.span, + } + } +} + #[derive(Default)] pub struct CfgSelectBranches { /// All the conditional branches. @@ -115,5 +128,102 @@ pub fn parse_cfg_select( } } + if let Some(features) = features + && features.enabled(sym::cfg_select) + { + let it = branches + .reachable + .iter() + .map(|(entry, _, _)| CfgSelectPredicate::Cfg(entry.clone())) + .chain(branches.wildcard.as_ref().map(|(t, _, _)| CfgSelectPredicate::Wildcard(*t))) + .chain( + branches.unreachable.iter().map(|(entry, _, _)| CfgSelectPredicate::clone(entry)), + ); + + lint_unreachable(p, it, lint_node_id); + } + Ok(branches) } + +fn lint_unreachable( + p: &mut Parser<'_>, + predicates: impl Iterator, + lint_node_id: NodeId, +) { + // Symbols that have a known value. + let mut known = FxHashMap::::default(); + let mut wildcard_span = None; + let mut it = predicates; + + let branch_is_unreachable = |predicate: CfgSelectPredicate, wildcard_span| { + let span = predicate.span(); + p.psess.buffer_lint( + UNREACHABLE_CFG_SELECT_PREDICATES, + span, + lint_node_id, + BuiltinLintDiag::UnreachableCfg { span, wildcard_span }, + ); + }; + + for predicate in &mut it { + let CfgSelectPredicate::Cfg(ref cfg_entry) = predicate else { + wildcard_span = Some(predicate.span()); + break; + }; + + match cfg_entry { + CfgEntry::Bool(true, _) => { + wildcard_span = Some(predicate.span()); + break; + } + CfgEntry::Bool(false, _) => continue, + CfgEntry::NameValue { name, value, .. } => match value { + None => { + // `name` will be false in all subsequent branches. + let current = known.insert(*name, false); + + match current { + None => continue, + Some(false) => { + branch_is_unreachable(predicate, None); + break; + } + Some(true) => { + // this branch will be taken, so all subsequent branches are unreachable. + break; + } + } + } + Some(_) => { /* for now we don't bother solving these */ } + }, + CfgEntry::Not(inner, _) => match &**inner { + CfgEntry::NameValue { name, value: None, .. } => { + // `name` will be true in all subsequent branches. + let current = known.insert(*name, true); + + match current { + None => continue, + Some(true) => { + branch_is_unreachable(predicate, None); + break; + } + Some(false) => { + // this branch will be taken, so all subsequent branches are unreachable. + break; + } + } + } + _ => { /* for now we don't bother solving these */ } + }, + CfgEntry::All(_, _) | CfgEntry::Any(_, _) => { + /* for now we don't bother solving these */ + } + CfgEntry::Version(..) => { /* don't bother solving these */ } + } + } + + for predicate in it { + branch_is_unreachable(predicate, wildcard_span) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 557dfe09853b..bdfe7bfb8f1f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -283,3 +283,12 @@ impl NoArgsAttributeParser for RustcPreserveUbChecksParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPreserveUbChecks; } + +pub(crate) struct RustcNoImplicitBoundsParser; + +impl NoArgsAttributeParser for RustcNoImplicitBoundsParser { + const PATH: &[Symbol] = &[sym::rustc_no_implicit_bounds]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitBounds; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index e01377d247bb..c055c2936e95 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -1,4 +1,5 @@ use rustc_hir::attrs::{DeprecatedSince, Deprecation}; +use rustc_hir::{RustcVersion, VERSION_PLACEHOLDER}; use super::prelude::*; use super::util::parse_version; @@ -143,6 +144,8 @@ impl SingleAttributeParser for DeprecationParser { DeprecatedSince::Future } else if !is_rustc { DeprecatedSince::NonStandard(since) + } else if since.as_str() == VERSION_PLACEHOLDER { + DeprecatedSince::RustcVersion(RustcVersion::CURRENT) } else if let Some(version) = parse_version(since) { DeprecatedSince::RustcVersion(version) } else { diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index f6aab9ea0ee2..767200bfa9bf 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -10,7 +10,7 @@ use super::prelude::*; pub(crate) struct InlineParser; impl SingleAttributeParser for InlineParser { - const PATH: &'static [Symbol] = &[sym::inline]; + const PATH: &[Symbol] = &[sym::inline]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ @@ -67,7 +67,7 @@ impl SingleAttributeParser for InlineParser { pub(crate) struct RustcForceInlineParser; impl SingleAttributeParser for RustcForceInlineParser { - const PATH: &'static [Symbol] = &[sym::rustc_force_inline]; + const PATH: &[Symbol] = &[sym::rustc_force_inline]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs index 3674aa7124ab..f9ace7e25d1b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs @@ -1,3 +1,6 @@ +use rustc_hir::lints::AttributeLintKind; +use rustc_session::lint::builtin::AMBIGUOUS_DERIVE_HELPERS; + use super::prelude::*; const PROC_MACRO_ALLOWED_TARGETS: AllowedTargets = @@ -126,6 +129,13 @@ fn parse_derive_like( cx.expected_identifier(ident.span); return None; } + if rustc_feature::is_builtin_attr_name(ident.name) { + cx.emit_lint( + AMBIGUOUS_DERIVE_HELPERS, + AttributeLintKind::AmbiguousDeriveHelpers, + ident.span, + ); + } attributes.push(ident.name); } } diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 34d4957edc70..fb0b8df65284 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -272,7 +272,7 @@ fn parse_alignment(node: &LitKind) -> Result { pub(crate) struct AlignParser(Option<(Align, Span)>); impl AlignParser { - const PATH: &'static [Symbol] = &[sym::rustc_align]; + const PATH: &[Symbol] = &[sym::rustc_align]; const TEMPLATE: AttributeTemplate = template!(List: &[""]); fn parse(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) { @@ -329,7 +329,7 @@ impl AttributeParser for AlignParser { pub(crate) struct AlignStaticParser(AlignParser); impl AlignStaticParser { - const PATH: &'static [Symbol] = &[sym::rustc_align_static]; + const PATH: &[Symbol] = &[sym::rustc_align_static]; const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE; fn parse(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) { diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 348882dc59fc..f1b31365013e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -2,7 +2,8 @@ use std::path::PathBuf; use rustc_ast::{LitIntType, LitKind, MetaItemLit}; use rustc_hir::attrs::{ - BorrowckGraphvizFormatKind, RustcCleanAttribute, RustcCleanQueries, RustcLayoutType, + BorrowckGraphvizFormatKind, CguFields, CguKind, DivergingBlockBehavior, + DivergingFallbackBehavior, RustcCleanAttribute, RustcCleanQueries, RustcLayoutType, RustcMirKind, }; use rustc_session::errors; @@ -10,12 +11,14 @@ use rustc_span::Symbol; use super::prelude::*; use super::util::parse_single_integer; -use crate::session_diagnostics::{AttributeRequiresOpt, RustcScalableVectorCountOutOfRange}; +use crate::session_diagnostics::{ + AttributeRequiresOpt, CguFieldsMissing, RustcScalableVectorCountOutOfRange, +}; pub(crate) struct RustcMainParser; impl NoArgsAttributeParser for RustcMainParser { - const PATH: &'static [Symbol] = &[sym::rustc_main]; + const PATH: &[Symbol] = &[sym::rustc_main]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcMain; @@ -100,7 +103,7 @@ impl NoArgsAttributeParser for RustcNoImplicitAutorefsParser { pub(crate) struct RustcLayoutScalarValidRangeStartParser; impl SingleAttributeParser for RustcLayoutScalarValidRangeStartParser { - const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_start]; + const PATH: &[Symbol] = &[sym::rustc_layout_scalar_valid_range_start]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); @@ -115,7 +118,7 @@ impl SingleAttributeParser for RustcLayoutScalarValidRangeStartPars pub(crate) struct RustcLayoutScalarValidRangeEndParser; impl SingleAttributeParser for RustcLayoutScalarValidRangeEndParser { - const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_end]; + const PATH: &[Symbol] = &[sym::rustc_layout_scalar_valid_range_end]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); @@ -204,6 +207,325 @@ impl NoArgsAttributeParser for RustcLintOptTyParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintOptTy; } +fn parse_cgu_fields( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + accepts_kind: bool, +) -> Option<(Symbol, Symbol, Option)> { + let Some(args) = args.list() else { + cx.expected_list(cx.attr_span, args); + return None; + }; + + let mut cfg = None::<(Symbol, Span)>; + let mut module = None::<(Symbol, Span)>; + let mut kind = None::<(Symbol, Span)>; + + for arg in args.mixed() { + let Some(arg) = arg.meta_item() else { + cx.expected_name_value(args.span, None); + continue; + }; + + let res = match arg.ident().map(|i| i.name) { + Some(sym::cfg) => &mut cfg, + Some(sym::module) => &mut module, + Some(sym::kind) if accepts_kind => &mut kind, + _ => { + cx.expected_specific_argument( + arg.path().span(), + if accepts_kind { + &[sym::cfg, sym::module, sym::kind] + } else { + &[sym::cfg, sym::module] + }, + ); + continue; + } + }; + + let Some(i) = arg.args().name_value() else { + cx.expected_name_value(arg.span(), None); + continue; + }; + + let Some(str) = i.value_as_str() else { + cx.expected_string_literal(i.value_span, Some(i.value_as_lit())); + continue; + }; + + if res.is_some() { + cx.duplicate_key(arg.span(), arg.ident().unwrap().name); + continue; + } + + *res = Some((str, i.value_span)); + } + + let Some((cfg, _)) = cfg else { + cx.emit_err(CguFieldsMissing { span: args.span, name: &cx.attr_path, field: sym::cfg }); + return None; + }; + let Some((module, _)) = module else { + cx.emit_err(CguFieldsMissing { span: args.span, name: &cx.attr_path, field: sym::module }); + return None; + }; + let kind = if let Some((kind, span)) = kind { + Some(match kind { + sym::no => CguKind::No, + sym::pre_dash_lto => CguKind::PreDashLto, + sym::post_dash_lto => CguKind::PostDashLto, + sym::any => CguKind::Any, + _ => { + cx.expected_specific_argument_strings( + span, + &[sym::no, sym::pre_dash_lto, sym::post_dash_lto, sym::any], + ); + return None; + } + }) + } else { + // return None so that an unwrap for the attributes that need it is ok. + if accepts_kind { + cx.emit_err(CguFieldsMissing { + span: args.span, + name: &cx.attr_path, + field: sym::kind, + }); + return None; + }; + + None + }; + + Some((cfg, module, kind)) +} + +#[derive(Default)] +pub(crate) struct RustcCguTestAttributeParser { + items: ThinVec<(Span, CguFields)>, +} + +impl AttributeParser for RustcCguTestAttributeParser { + const ATTRIBUTES: AcceptMapping = &[ + ( + &[sym::rustc_partition_reused], + template!(List: &[r#"cfg = "...", module = "...""#]), + |this, cx, args| { + this.items.extend(parse_cgu_fields(cx, args, false).map(|(cfg, module, _)| { + (cx.attr_span, CguFields::PartitionReused { cfg, module }) + })); + }, + ), + ( + &[sym::rustc_partition_codegened], + template!(List: &[r#"cfg = "...", module = "...""#]), + |this, cx, args| { + this.items.extend(parse_cgu_fields(cx, args, false).map(|(cfg, module, _)| { + (cx.attr_span, CguFields::PartitionCodegened { cfg, module }) + })); + }, + ), + ( + &[sym::rustc_expected_cgu_reuse], + template!(List: &[r#"cfg = "...", module = "...", kind = "...""#]), + |this, cx, args| { + this.items.extend(parse_cgu_fields(cx, args, true).map(|(cfg, module, kind)| { + // unwrap ok because if not given, we return None in `parse_cgu_fields`. + (cx.attr_span, CguFields::ExpectedCguReuse { cfg, module, kind: kind.unwrap() }) + })); + }, + ), + ]; + + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::Mod), Allow(Target::Crate)]); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + Some(AttributeKind::RustcCguTestAttr(self.items)) + } +} + +pub(crate) struct RustcDeprecatedSafe2024Parser; + +impl SingleAttributeParser for RustcDeprecatedSafe2024Parser { + const PATH: &[Symbol] = &[sym::rustc_deprecated_safe_2024]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const TEMPLATE: AttributeTemplate = template!(List: &[r#"audit_that = "...""#]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(args) = args.list() else { + cx.expected_list(cx.attr_span, args); + return None; + }; + + let Some(single) = args.single() else { + cx.expected_single_argument(args.span); + return None; + }; + + let Some(arg) = single.meta_item() else { + cx.expected_name_value(args.span, None); + return None; + }; + + let Some(args) = arg.word_is(sym::audit_that) else { + cx.expected_specific_argument(arg.span(), &[sym::audit_that]); + return None; + }; + + let Some(nv) = args.name_value() else { + cx.expected_name_value(arg.span(), Some(sym::audit_that)); + return None; + }; + + let Some(suggestion) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return None; + }; + + Some(AttributeKind::RustcDeprecatedSafe2024 { suggestion }) + } +} + +pub(crate) struct RustcConversionSuggestionParser; + +impl NoArgsAttributeParser for RustcConversionSuggestionParser { + const PATH: &[Symbol] = &[sym::rustc_conversion_suggestion]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConversionSuggestion; +} + +pub(crate) struct RustcCaptureAnalysisParser; + +impl NoArgsAttributeParser for RustcCaptureAnalysisParser { + const PATH: &[Symbol] = &[sym::rustc_capture_analysis]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Closure)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcCaptureAnalysis; +} + +pub(crate) struct RustcNeverTypeOptionsParser; + +impl SingleAttributeParser for RustcNeverTypeOptionsParser { + const PATH: &[Symbol] = &[sym::rustc_never_type_options]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const TEMPLATE: AttributeTemplate = template!(List: &[ + r#"fallback = "unit", "never", "no""#, + r#"diverging_block_default = "unit", "never""#, + ]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span, args); + return None; + }; + + let mut fallback = None::; + let mut diverging_block_default = None::; + + for arg in list.mixed() { + let Some(meta) = arg.meta_item() else { + cx.expected_name_value(arg.span(), None); + continue; + }; + + let res = match meta.ident().map(|i| i.name) { + Some(sym::fallback) => &mut fallback, + Some(sym::diverging_block_default) => &mut diverging_block_default, + _ => { + cx.expected_specific_argument( + meta.path().span(), + &[sym::fallback, sym::diverging_block_default], + ); + continue; + } + }; + + let Some(nv) = meta.args().name_value() else { + cx.expected_name_value(meta.span(), None); + continue; + }; + + let Some(field) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + continue; + }; + + if res.is_some() { + cx.duplicate_key(meta.span(), meta.ident().unwrap().name); + continue; + } + + *res = Some(Ident { name: field, span: nv.value_span }); + } + + let fallback = match fallback { + None => None, + Some(Ident { name: sym::unit, .. }) => Some(DivergingFallbackBehavior::ToUnit), + Some(Ident { name: sym::never, .. }) => Some(DivergingFallbackBehavior::ToNever), + Some(Ident { name: sym::no, .. }) => Some(DivergingFallbackBehavior::NoFallback), + Some(Ident { span, .. }) => { + cx.expected_specific_argument_strings(span, &[sym::unit, sym::never, sym::no]); + return None; + } + }; + + let diverging_block_default = match diverging_block_default { + None => None, + Some(Ident { name: sym::unit, .. }) => Some(DivergingBlockBehavior::Unit), + Some(Ident { name: sym::never, .. }) => Some(DivergingBlockBehavior::Never), + Some(Ident { span, .. }) => { + cx.expected_specific_argument_strings(span, &[sym::unit, sym::no]); + return None; + } + }; + + Some(AttributeKind::RustcNeverTypeOptions { fallback, diverging_block_default }) + } +} + +pub(crate) struct RustcTrivialFieldReadsParser; + +impl NoArgsAttributeParser for RustcTrivialFieldReadsParser { + const PATH: &[Symbol] = &[sym::rustc_trivial_field_reads]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTrivialFieldReads; +} + +pub(crate) struct RustcNoMirInlineParser; + +impl NoArgsAttributeParser for RustcNoMirInlineParser { + const PATH: &[Symbol] = &[sym::rustc_no_mir_inline]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoMirInline; +} + pub(crate) struct RustcLintQueryInstabilityParser; impl NoArgsAttributeParser for RustcLintQueryInstabilityParser { @@ -219,6 +541,22 @@ impl NoArgsAttributeParser for RustcLintQueryInstabilityParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintQueryInstability; } +pub(crate) struct RustcRegionsParser; + +impl NoArgsAttributeParser for RustcRegionsParser { + const PATH: &[Symbol] = &[sym::rustc_regions]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcRegions; +} + pub(crate) struct RustcLintUntrackedQueryInformationParser; impl NoArgsAttributeParser for RustcLintUntrackedQueryInformationParser { @@ -237,21 +575,11 @@ impl NoArgsAttributeParser for RustcLintUntrackedQueryInformationPa pub(crate) struct RustcObjectLifetimeDefaultParser; -impl SingleAttributeParser for RustcObjectLifetimeDefaultParser { +impl NoArgsAttributeParser for RustcObjectLifetimeDefaultParser { const PATH: &[Symbol] = &[sym::rustc_object_lifetime_default]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); - const TEMPLATE: AttributeTemplate = template!(Word); - - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - if let Err(span) = args.no_args() { - cx.expected_no_args(span); - return None; - } - - Some(AttributeKind::RustcObjectLifetimeDefault) - } + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcObjectLifetimeDefault; } pub(crate) struct RustcSimdMonomorphizeLaneLimitParser; @@ -490,10 +818,11 @@ impl CombineAttributeParser for RustcMirParser { .collect() } } + pub(crate) struct RustcNonConstTraitMethodParser; impl NoArgsAttributeParser for RustcNonConstTraitMethodParser { - const PATH: &'static [Symbol] = &[sym::rustc_non_const_trait_method]; + const PATH: &[Symbol] = &[sym::rustc_non_const_trait_method]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::Method(MethodKind::Trait { body: true })), @@ -721,3 +1050,173 @@ impl CombineAttributeParser for RustcThenThisWouldNeedParser { Some(ident) } } + +pub(crate) struct RustcInsignificantDtorParser; + +impl NoArgsAttributeParser for RustcInsignificantDtorParser { + const PATH: &[Symbol] = &[sym::rustc_insignificant_dtor]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Enum), + Allow(Target::Struct), + Allow(Target::ForeignTy), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcInsignificantDtor; +} + +pub(crate) struct RustcEffectiveVisibilityParser; + +impl NoArgsAttributeParser for RustcEffectiveVisibilityParser { + const PATH: &[Symbol] = &[sym::rustc_effective_visibility]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Use), + Allow(Target::Static), + Allow(Target::Const), + Allow(Target::Fn), + Allow(Target::Closure), + Allow(Target::Mod), + Allow(Target::ForeignMod), + Allow(Target::TyAlias), + Allow(Target::Enum), + Allow(Target::Variant), + Allow(Target::Struct), + Allow(Target::Field), + Allow(Target::Union), + Allow(Target::Trait), + Allow(Target::TraitAlias), + Allow(Target::Impl { of_trait: false }), + Allow(Target::Impl { of_trait: true }), + Allow(Target::AssocConst), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::AssocTy), + Allow(Target::ForeignFn), + Allow(Target::ForeignStatic), + Allow(Target::ForeignTy), + Allow(Target::MacroDef), + Allow(Target::PatField), + Allow(Target::Crate), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEffectiveVisibility; +} + +pub(crate) struct RustcSymbolName; + +impl SingleAttributeParser for RustcSymbolName { + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::ForeignFn), + Allow(Target::ForeignStatic), + Allow(Target::Impl { of_trait: false }), + ]); + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const PATH: &[Symbol] = &[sym::rustc_symbol_name]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const TEMPLATE: AttributeTemplate = template!(Word); + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + if let Err(span) = args.no_args() { + cx.expected_no_args(span); + return None; + } + Some(AttributeKind::RustcSymbolName(cx.attr_span)) + } +} + +pub(crate) struct RustcDefPath; + +impl SingleAttributeParser for RustcDefPath { + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::ForeignFn), + Allow(Target::ForeignStatic), + Allow(Target::Impl { of_trait: false }), + ]); + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const PATH: &[Symbol] = &[sym::rustc_def_path]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const TEMPLATE: AttributeTemplate = template!(Word); + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + if let Err(span) = args.no_args() { + cx.expected_no_args(span); + return None; + } + Some(AttributeKind::RustcDefPath(cx.attr_span)) + } +} + +pub(crate) struct RustcIntrinsicParser; + +impl NoArgsAttributeParser for RustcIntrinsicParser { + const PATH: &[Symbol] = &[sym::rustc_intrinsic]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsic; +} + +pub(crate) struct RustcIntrinsicConstStableIndirectParser; + +impl NoArgsAttributeParser for RustcIntrinsicConstStableIndirectParser { + const PATH: &'static [Symbol] = &[sym::rustc_intrinsic_const_stable_indirect]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsicConstStableIndirect; +} + +pub(crate) struct RustcStrictCoherenceParser; + +impl NoArgsAttributeParser for RustcStrictCoherenceParser { + const PATH: &[Symbol] = &[sym::rustc_strict_coherence]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Trait), + Allow(Target::Struct), + Allow(Target::Enum), + Allow(Target::Union), + Allow(Target::ForeignTy), + ]); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcStrictCoherence; +} + +pub(crate) struct RustcReservationImplParser; + +impl SingleAttributeParser for RustcReservationImplParser { + const PATH: &[Symbol] = &[sym::rustc_reservation_impl]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::Impl { of_trait: true })]); + + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "reservation message"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(nv) = args.name_value() else { + cx.expected_name_value(args.span().unwrap_or(cx.attr_span), None); + return None; + }; + + let Some(value_str) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return None; + }; + + Some(AttributeKind::RustcReservationImpl(cx.attr_span, value_str)) + } +} + +pub(crate) struct PreludeImportParser; + +impl NoArgsAttributeParser for PreludeImportParser { + const PATH: &[Symbol] = &[sym::prelude_import]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Use)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::PreludeImport; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 1f01cadcb43e..a2be2d42b3e1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -244,7 +244,20 @@ impl AttributeParser for ConstStabilityParser { this.promotable = true; }), ]; - const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Impl { of_trait: false }), + Allow(Target::Impl { of_trait: true }), + Allow(Target::Use), // FIXME I don't think this does anything? + Allow(Target::Const), + Allow(Target::AssocConst), + Allow(Target::Trait), + Allow(Target::Static), + Allow(Target::Crate), + ]); fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option { if self.promotable { diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index 236d10d77b92..41b1836588de 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -1,3 +1,4 @@ +use rustc_hir::attrs::RustcAbiAttrKind; use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; use super::prelude::*; @@ -140,3 +141,119 @@ impl SingleAttributeParser for ReexportTestHarnessMainParser { Some(AttributeKind::ReexportTestHarnessMain(name)) } } + +pub(crate) struct RustcAbiParser; + +impl SingleAttributeParser for RustcAbiParser { + const PATH: &[Symbol] = &[sym::rustc_abi]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::debug, sym::assert_eq]); + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::TyAlias), + Allow(Target::Fn), + Allow(Target::ForeignFn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(args) = args.list() else { + cx.expected_specific_argument_and_list(cx.attr_span, &[sym::assert_eq, sym::debug]); + return None; + }; + + let Some(arg) = args.single() else { + cx.expected_single_argument(cx.attr_span); + return None; + }; + + let fail_incorrect_argument = + |span| cx.expected_specific_argument(span, &[sym::assert_eq, sym::debug]); + + let Some(arg) = arg.meta_item() else { + fail_incorrect_argument(args.span); + return None; + }; + + let kind: RustcAbiAttrKind = match arg.path().word_sym() { + Some(sym::assert_eq) => RustcAbiAttrKind::AssertEq, + Some(sym::debug) => RustcAbiAttrKind::Debug, + None | Some(_) => { + fail_incorrect_argument(arg.span()); + return None; + } + }; + + Some(AttributeKind::RustcAbi { attr_span: cx.attr_span, kind }) + } +} + +pub(crate) struct RustcDelayedBugFromInsideQueryParser; + +impl NoArgsAttributeParser for RustcDelayedBugFromInsideQueryParser { + const PATH: &[Symbol] = &[sym::rustc_delayed_bug_from_inside_query]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDelayedBugFromInsideQuery; +} + +pub(crate) struct RustcEvaluateWhereClausesParser; + +impl NoArgsAttributeParser for RustcEvaluateWhereClausesParser { + const PATH: &[Symbol] = &[sym::rustc_evaluate_where_clauses]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Trait { body: false })), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEvaluateWhereClauses; +} + +pub(crate) struct RustcOutlivesParser; + +impl NoArgsAttributeParser for RustcOutlivesParser { + const PATH: &[Symbol] = &[sym::rustc_outlives]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Struct), + Allow(Target::Enum), + Allow(Target::Union), + Allow(Target::TyAlias), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcOutlives; +} + +pub(crate) struct TestRunnerParser; + +impl SingleAttributeParser for TestRunnerParser { + const PATH: &[Symbol] = &[sym::test_runner]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const TEMPLATE: AttributeTemplate = template!(List: &["path"]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span, args); + return None; + }; + + let Some(single) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + + let Some(meta) = single.meta_item() else { + cx.unexpected_literal(single.span()); + return None; + }; + + Some(AttributeKind::TestRunner(meta.path().0.clone())) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index bfea02e789a0..ceaa43948d67 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -66,15 +66,6 @@ impl NoArgsAttributeParser for ParenSugarParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcParenSugar; } -pub(crate) struct TypeConstParser; -impl NoArgsAttributeParser for TypeConstParser { - const PATH: &[Symbol] = &[sym::type_const]; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = - AllowedTargets::AllowList(&[Allow(Target::Const), Allow(Target::AssocConst)]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::TypeConst; -} - // Markers pub(crate) struct MarkerParser; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 5abf299ec618..c6f0914bfbda 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::collections::BTreeMap; +use std::collections::btree_map::Entry; use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; @@ -61,7 +62,7 @@ use crate::target_checking::AllowedTargets; type GroupType = LazyLock>; pub(super) struct GroupTypeInner { - pub(super) accepters: BTreeMap<&'static [Symbol], Vec>>, + pub(super) accepters: BTreeMap<&'static [Symbol], GroupTypeInnerAccept>, } pub(super) struct GroupTypeInnerAccept { @@ -101,7 +102,7 @@ macro_rules! attribute_parsers { @[$stage: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?]; ) => { pub(crate) static $name: GroupType<$stage> = LazyLock::new(|| { - let mut accepters = BTreeMap::<_, Vec>>::new(); + let mut accepters = BTreeMap::<_, GroupTypeInnerAccept<$stage>>::new(); $( { thread_local! { @@ -109,19 +110,24 @@ macro_rules! attribute_parsers { }; for (path, template, accept_fn) in <$names>::ATTRIBUTES { - accepters.entry(*path).or_default().push(GroupTypeInnerAccept { - template: *template, - accept_fn: Box::new(|cx, args| { - STATE_OBJECT.with_borrow_mut(|s| { - accept_fn(s, cx, args) - }) - }), - allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS, - finalizer: Box::new(|cx| { - let state = STATE_OBJECT.take(); - state.finalize(cx) - }), - }); + match accepters.entry(*path) { + Entry::Vacant(e) => { + e.insert(GroupTypeInnerAccept { + template: *template, + accept_fn: Box::new(|cx, args| { + STATE_OBJECT.with_borrow_mut(|s| { + accept_fn(s, cx, args) + }) + }), + allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS, + finalizer: Box::new(|cx| { + let state = STATE_OBJECT.take(); + state.finalize(cx) + }) + }); + } + Entry::Occupied(_) => panic!("Attribute {path:?} has multiple accepters"), + } } } )* @@ -141,6 +147,7 @@ attribute_parsers!( DocParser, MacroUseParser, NakedParser, + RustcCguTestAttributeParser, StabilityParser, UsedParser, // tidy-alphabetical-end @@ -191,8 +198,11 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, + Single, + Single, Single, Single, Single, @@ -200,12 +210,15 @@ attribute_parsers!( Single, Single, Single, - Single, + Single, + Single, Single, Single, + Single, Single, Single, Single, + Single, Single, Single, Single, @@ -245,41 +258,56 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, Single>, Single>, Single>, + Single>, Single>, + Single>, Single>, + Single>, Single>, Single>, Single>, Single>, Single>, + Single>, + Single>, Single>, Single>, + Single>, + Single>, + Single>, Single>, Single>, Single>, Single>, Single>, Single>, + Single>, + Single>, Single>, Single>, + Single>, Single>, + Single>, Single>, Single>, Single>, + Single>, Single>, + Single>, + Single>, Single>, Single>, Single>, Single>, Single>, Single>, - Single>, Single>, // tidy-alphabetical-end ]; diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index bac4936c20d2..f75f63a0e811 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -327,7 +327,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { let parts = n.item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); - if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) { + if let Some(accept) = S::parsers().accepters.get(parts.as_slice()) { let Some(args) = ArgParser::from_attr_args( args, &parts, @@ -368,28 +368,26 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { continue; } - for accept in accepts { - let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext { - shared: SharedContext { - cx: self, - target_span, - target, - emit_lint: &mut emit_lint, - }, - attr_span, - inner_span: lower_span(n.item.span()), - attr_style: attr.style, - parsed_description: ParsedDescription::Attribute, - template: &accept.template, - attr_path: attr_path.clone(), - }; + let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext { + shared: SharedContext { + cx: self, + target_span, + target, + emit_lint: &mut emit_lint, + }, + attr_span, + inner_span: lower_span(n.item.span()), + attr_style: attr.style, + parsed_description: ParsedDescription::Attribute, + template: &accept.template, + attr_path: attr_path.clone(), + }; - (accept.accept_fn)(&mut cx, &args); - finalizers.push(&accept.finalizer); + (accept.accept_fn)(&mut cx, &args); + finalizers.push(&accept.finalizer); - if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { - Self::check_target(&accept.allowed_targets, target, &mut cx); - } + if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { + Self::check_target(&accept.allowed_targets, target, &mut cx); } } else { // If we're here, we must be compiling a tool attribute... Or someone diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 6ca47e8d698b..2eb585671fff 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -45,6 +45,15 @@ pub(crate) struct DocAliasStartEnd<'a> { pub attr_str: &'a str, } +#[derive(Diagnostic)] +#[diag("`#[{$name})]` is missing a `{$field}` argument")] +pub(crate) struct CguFieldsMissing<'a> { + #[primary_span] + pub span: Span, + pub name: &'a AttrPath, + pub field: Symbol, +} + #[derive(Diagnostic)] #[diag("`#![doc({$attr_name} = \"...\")]` isn't allowed as a crate-level attribute")] pub(crate) struct DocAttrNotCrateLevel { diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml index 9e7d55180a23..55a09dbf1d72 100644 --- a/compiler/rustc_borrowck/Cargo.toml +++ b/compiler/rustc_borrowck/Cargo.toml @@ -11,7 +11,6 @@ polonius-engine = "0.13.0" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_graphviz = { path = "../rustc_graphviz" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl deleted file mode 100644 index f59e106c7ac3..000000000000 --- a/compiler/rustc_borrowck/messages.ftl +++ /dev/null @@ -1,296 +0,0 @@ -borrowck_assign_due_to_use_closure = - assignment occurs due to use in closure - -borrowck_assign_due_to_use_coroutine = - assign occurs due to use in coroutine - -borrowck_assign_part_due_to_use_closure = - assignment to part occurs due to use in closure - -borrowck_assign_part_due_to_use_coroutine = - assign to part occurs due to use in coroutine - -borrowck_borrow_due_to_use_closure = - borrow occurs due to use in closure - -borrowck_borrow_due_to_use_coroutine = - borrow occurs due to use in coroutine - -borrowck_calling_operator_moves = - calling this operator moves the value - -borrowck_calling_operator_moves_lhs = - calling this operator moves the left-hand side - -borrowck_cannot_move_when_borrowed = - cannot move out of {$place -> - [value] value - *[other] {$place} - } because it is borrowed - .label = borrow of {$borrow_place -> - [value] value - *[other] {$borrow_place} - } occurs here - .move_label = move out of {$value_place -> - [value] value - *[other] {$value_place} - } occurs here - -borrowck_capture_immute = - capture is immutable because of use here - -borrowck_capture_move = - capture is moved because of use here - -borrowck_capture_mut = - capture is mutable because of use here - -borrowck_closure_inferred_mut = inferred to be a `FnMut` closure - -borrowck_closure_invoked_twice = - closure cannot be invoked more than once because it moves the variable `{$place_name}` out of its environment - -borrowck_closure_moved_twice = - closure cannot be moved more than once as it is not `Copy` due to moving the variable `{$place_name}` out of its environment - -borrowck_consider_borrow_type_contents = - help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents - -borrowck_could_not_normalize = - could not normalize `{$value}` - -borrowck_could_not_prove = - could not prove `{$predicate}` - -borrowck_dereference_suggestion = - dereference the return value - -borrowck_func_take_self_moved_place = - `{$func}` takes ownership of the receiver `self`, which moves {$place_name} - -borrowck_generic_does_not_live_long_enough = - `{$kind}` does not live long enough - -borrowck_higher_ranked_lifetime_error = - higher-ranked lifetime error - -borrowck_higher_ranked_subtype_error = - higher-ranked subtype error - -borrowck_implicit_static = - this has an implicit `'static` lifetime requirement - -borrowck_implicit_static_introduced = - calling this method introduces the `impl`'s `'static` requirement - -borrowck_implicit_static_relax = - consider relaxing the implicit `'static` requirement - -borrowck_lifetime_constraints_error = - lifetime may not live long enough - -borrowck_limitations_implies_static = - due to a current limitation of the type system, this implies a `'static` lifetime - -borrowck_move_closure_suggestion = - consider adding 'move' keyword before the nested closure - -borrowck_move_out_place_here = - {$place} is moved here - -borrowck_move_unsized = - cannot move a value of type `{$ty}` - .label = the size of `{$ty}` cannot be statically determined - -borrowck_moved_a_fn_once_in_call = - this value implements `FnOnce`, which causes it to be moved when called - -borrowck_moved_a_fn_once_in_call_call = - `FnOnce` closures can only be called once - -borrowck_moved_a_fn_once_in_call_def = - `{$ty}` is made to be an `FnOnce` closure here - -borrowck_moved_due_to_await = - {$place_name} {$is_partial -> - [true] partially moved - *[false] moved - } due to this {$is_loop_message -> - [true] await, in previous iteration of loop - *[false] await - } - -borrowck_moved_due_to_call = - {$place_name} {$is_partial -> - [true] partially moved - *[false] moved - } due to this {$is_loop_message -> - [true] call, in previous iteration of loop - *[false] call - } - -borrowck_moved_due_to_implicit_into_iter_call = - {$place_name} {$is_partial -> - [true] partially moved - *[false] moved - } due to this implicit call to {$is_loop_message -> - [true] `.into_iter()`, in previous iteration of loop - *[false] `.into_iter()` - } - -borrowck_moved_due_to_method_call = - {$place_name} {$is_partial -> - [true] partially moved - *[false] moved - } due to this method {$is_loop_message -> - [true] call, in previous iteration of loop - *[false] call - } - -borrowck_moved_due_to_usage_in_operator = - {$place_name} {$is_partial -> - [true] partially moved - *[false] moved - } due to usage in {$is_loop_message -> - [true] operator, in previous iteration of loop - *[false] operator - } - -borrowck_opaque_type_lifetime_mismatch = - opaque type used twice with different lifetimes - .label = lifetime `{$arg}` used here - .prev_lifetime_label = lifetime `{$prev}` previously used here - .note = if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types - -borrowck_partial_var_move_by_use_in_closure = - variable {$is_partial -> - [true] partially moved - *[false] moved - } due to use in closure - -borrowck_partial_var_move_by_use_in_coroutine = - variable {$is_partial -> - [true] partially moved - *[false] moved - } due to use in coroutine - -borrowck_restrict_to_static = - consider restricting the type parameter to the `'static` lifetime - -borrowck_returned_async_block_escaped = - returns an `async` block that contains a reference to a captured variable, which then escapes the closure body - -borrowck_returned_closure_escaped = - returns a closure that contains a reference to a captured variable, which then escapes the closure body - -borrowck_returned_lifetime_short = - {$category_desc}requires that `{$free_region_name}` must outlive `{$outlived_fr_name}` - -borrowck_returned_lifetime_wrong = - {$mir_def_name} was supposed to return data with lifetime `{$outlived_fr_name}` but it is returning data with lifetime `{$fr_name}` - -borrowck_returned_ref_escaped = - returns a reference to a captured variable which escapes the closure body - -borrowck_simd_intrinsic_arg_const = - {$arg -> - [1] 1st - [2] 2nd - [3] 3rd - *[other] {$arg}th - } argument of `{$intrinsic}` is required to be a `const` item - -borrowck_suggest_create_fresh_reborrow = - consider reborrowing the `Pin` instead of moving it - -borrowck_suggest_iterate_over_slice = - consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop - -borrowck_tail_expr_drop_order = relative drop order changing in Rust 2024 - .label = this temporary value will be dropped at the end of the block - .note = consider using a `let` binding to ensure the value will live long enough - -borrowck_ty_no_impl_copy = - {$is_partial_move -> - [true] partial move - *[false] move - } occurs because {$place} has type `{$ty}`, which does not implement the `Copy` trait - -borrowck_use_due_to_use_closure = - use occurs due to use in closure - -borrowck_use_due_to_use_coroutine = - use occurs due to use in coroutine - -borrowck_used_impl_require_static = - the used `impl` has a `'static` requirement - -borrowck_value_capture_here = - value captured {$is_within -> - [true] here by coroutine - *[false] here - } - -borrowck_value_moved_here = - value {$is_partial -> - [true] partially moved - *[false] moved - } {$is_move_msg -> - [true] into closure here - *[false] here - }{$is_loop_message -> - [true] , in previous iteration of loop - *[false] {""} - } - -borrowck_var_borrow_by_use_in_closure = - borrow occurs due to use in closure - -borrowck_var_borrow_by_use_in_coroutine = - borrow occurs due to use in coroutine - -borrowck_var_borrow_by_use_place_in_closure = - {$is_single_var -> - *[true] borrow occurs - [false] borrows occur - } due to use of {$place} in closure - -borrowck_var_borrow_by_use_place_in_coroutine = - {$is_single_var -> - *[true] borrow occurs - [false] borrows occur - } due to use of {$place} in coroutine - -borrowck_var_cannot_escape_closure = - captured variable cannot escape `FnMut` closure body - .note = `FnMut` closures only have access to their captured variables while they are executing... - .cannot_escape = ...therefore, they cannot allow references to captured variables to escape - -borrowck_var_does_not_need_mut = - variable does not need to be mutable - .suggestion = remove this `mut` - -borrowck_var_first_borrow_by_use_place_in_closure = - first borrow occurs due to use of {$place} in closure - -borrowck_var_first_borrow_by_use_place_in_coroutine = - first borrow occurs due to use of {$place} in coroutine - -borrowck_var_here_captured = variable captured here - -borrowck_var_here_defined = variable defined here - -borrowck_var_move_by_use_in_closure = - move occurs due to use in closure - -borrowck_var_move_by_use_in_coroutine = - move occurs due to use in coroutine - -borrowck_var_mutable_borrow_by_use_place_in_closure = - mutable borrow occurs due to use of {$place} in closure - -borrowck_var_second_borrow_by_use_place_in_closure = - second borrow occurs due to use of {$place} in closure - -borrowck_var_second_borrow_by_use_place_in_coroutine = - second borrow occurs due to use of {$place} in coroutine diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 6ed07cf9b1c8..a927c30fae32 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -24,7 +24,6 @@ use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_ use tracing::{debug, instrument}; use crate::MirBorrowckCtxt; -use crate::region_infer::values::RegionElement; use crate::session_diagnostics::{ HigherRankedErrorCause, HigherRankedLifetimeError, HigherRankedSubtypeError, }; @@ -49,11 +48,12 @@ impl<'tcx> UniverseInfo<'tcx> { UniverseInfo::RelateTys { expected, found } } + /// Report an error where an element erroneously made its way into `placeholder`. pub(crate) fn report_erroneous_element( &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion<'tcx>, - error_element: RegionElement<'tcx>, + error_element: Option>, cause: ObligationCause<'tcx>, ) { match *self { @@ -146,14 +146,14 @@ pub(crate) trait TypeOpInfo<'tcx> { ) -> Option>; /// Constraints require that `error_element` appear in the - /// values of `placeholder`, but this cannot be proven to + /// values of `placeholder`, but this cannot be proven to /// hold. Report an error. #[instrument(level = "debug", skip(self, mbcx))] fn report_erroneous_element( &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion<'tcx>, - error_element: RegionElement<'tcx>, + error_element: Option>, cause: ObligationCause<'tcx>, ) { let tcx = mbcx.infcx.tcx; @@ -172,19 +172,17 @@ pub(crate) trait TypeOpInfo<'tcx> { ty::PlaceholderRegion::new(adjusted_universe.into(), placeholder.bound), ); - let error_region = - if let RegionElement::PlaceholderRegion(error_placeholder) = error_element { - let adjusted_universe = - error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32()); - adjusted_universe.map(|adjusted| { - ty::Region::new_placeholder( - tcx, - ty::PlaceholderRegion::new(adjusted.into(), error_placeholder.bound), - ) - }) - } else { - None - }; + // FIXME: one day this should just be error_element, + // and this method shouldn't do anything. + let error_region = error_element.and_then(|e| { + let adjusted_universe = e.universe.as_u32().checked_sub(base_universe.as_u32()); + adjusted_universe.map(|adjusted| { + ty::Region::new_placeholder( + tcx, + ty::PlaceholderRegion::new(adjusted.into(), e.bound), + ) + }) + }); debug!(?placeholder_region); diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index bbd0a8ae0710..bc2e99b9ceb5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -649,8 +649,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // We want to focus on relevant live locals in diagnostics, so when polonius is enabled, we // ensure that we don't emit live boring locals as explanations. let is_local_boring = |local| { - if let Some(polonius_diagnostics) = self.polonius_diagnostics { - polonius_diagnostics.boring_nll_locals.contains(&local) + if let Some(polonius_context) = self.polonius_context { + polonius_context.boring_nll_locals.contains(&local) } else { assert!(!tcx.sess.opts.unstable_opts.polonius.is_next_enabled()); diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index c15e7041c94d..04bacd049bc9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -4,7 +4,9 @@ use std::collections::BTreeMap; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify}; +use rustc_errors::{ + Applicability, Diag, DiagMessage, EmissionGuarantee, MultiSpan, inline_fluent, listify, +}; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::{ self as hir, CoroutineKind, GenericBound, LangItem, WhereBoundPredicate, WherePredicateKind, @@ -35,7 +37,6 @@ use tracing::debug; use super::MirBorrowckCtxt; use super::borrow_set::BorrowData; use crate::constraints::OutlivesConstraint; -use crate::fluent_generated as fluent; use crate::nll::ConstraintDescription; use crate::session_diagnostics::{ CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause, @@ -700,7 +701,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) .is_some() { - diag.span_note(pred.span, fluent::borrowck_limitations_implies_static); + diag.span_note(pred.span, LIMITATION_NOTE); return; } for bound in bounds.iter() { @@ -711,7 +712,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) .is_some() { - diag.span_note(bound.span, fluent::borrowck_limitations_implies_static); + diag.span_note(bound.span, LIMITATION_NOTE); return; } } @@ -1312,7 +1313,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let mut span: MultiSpan = spans.clone().into(); err.arg("ty", param_ty.to_string()); let msg = err.dcx.eagerly_translate_to_string( - fluent::borrowck_moved_a_fn_once_in_call_def, + inline_fluent!("`{$ty}` is made to be an `FnOnce` closure here"), err.args.iter(), ); err.remove_arg("ty"); @@ -1321,9 +1322,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } span.push_span_label( fn_call_span, - fluent::borrowck_moved_a_fn_once_in_call, + inline_fluent!("this value implements `FnOnce`, which causes it to be moved when called"), + ); + err.span_note( + span, + inline_fluent!("`FnOnce` closures can only be called once"), ); - err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call); } else { err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span }); } @@ -1568,3 +1572,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.local_name(index).is_none_or(|name| name.as_str().starts_with('_')) } } + +const LIMITATION_NOTE: DiagMessage = inline_fluent!( + "due to a current limitation of the type system, this implies a `'static` lifetime" +); diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 4fb739403ced..96090e85e562 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -354,14 +354,71 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span) { if snippet.starts_with("&mut ") { - // We don't have access to the HIR to get accurate spans, but we can - // give a best effort structured suggestion. - err.span_suggestion_verbose( - source_info.span.with_hi(source_info.span.lo() + BytePos(5)), - "if there is only one mutable reborrow, remove the `&mut`", - "", - Applicability::MaybeIncorrect, - ); + // In calls, `&mut &mut T` may be deref-coerced to `&mut T`, and + // removing the extra `&mut` is the most direct suggestion. But for + // pattern-matching expressions (`match`, `if let`, `while let`), that + // can easily turn into a move, so prefer suggesting an explicit + // reborrow via `&mut *x` instead. + let mut in_pat_scrutinee = false; + let mut is_deref_coerced = false; + if let Some(expr) = self.find_expr(source_info.span) { + let tcx = self.infcx.tcx; + let span = expr.span.source_callsite(); + for (_, node) in tcx.hir_parent_iter(expr.hir_id) { + if let Node::Expr(parent_expr) = node { + match parent_expr.kind { + ExprKind::Match(scrutinee, ..) + if scrutinee + .span + .source_callsite() + .contains(span) => + { + in_pat_scrutinee = true; + break; + } + ExprKind::Let(let_expr) + if let_expr + .init + .span + .source_callsite() + .contains(span) => + { + in_pat_scrutinee = true; + break; + } + _ => {} + } + } + } + + let typeck = tcx.typeck(expr.hir_id.owner.def_id); + is_deref_coerced = + typeck.expr_adjustments(expr).iter().any(|adj| { + matches!(adj.kind, ty::adjustment::Adjust::Deref(_)) + }); + } + + if in_pat_scrutinee { + // Best-effort structured suggestion: insert `*` after `&mut `. + err.span_suggestion_verbose( + source_info + .span + .with_lo(source_info.span.lo() + BytePos(5)) + .shrink_to_lo(), + "to reborrow the mutable reference, add `*`", + "*", + Applicability::MaybeIncorrect, + ); + } else if is_deref_coerced { + // We don't have access to the HIR to get accurate spans, but we + // can give a best effort structured suggestion. + err.span_suggestion_verbose( + source_info.span.with_hi(source_info.span.lo() + BytePos(5)), + "if there is only one mutable reborrow, remove the `&mut`", + "", + Applicability::MaybeIncorrect, + ); + } } else { // This can occur with things like `(&mut self).foo()`. err.span_help(source_info.span, "try removing `&mut` here"); @@ -1695,7 +1752,7 @@ fn suggest_ampmut<'tcx>( && let Either::Left(rhs_stmt_new) = body.stmt_at(*assign) && let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind && let rhs_span_new = rhs_stmt_new.source_info.span - && let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span) + && let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span_new) { (rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new); } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 17f1988a17c4..ca6b9965f1e9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1,7 +1,7 @@ //! Error reporting machinery for lifetime errors. use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, inline_fluent}; use rustc_hir as hir; use rustc_hir::GenericBound::Trait; use rustc_hir::QPath::Resolved; @@ -27,16 +27,15 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; use tracing::{debug, instrument, trace}; -use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource}; +use super::{LIMITATION_NOTE, OutlivesSuggestionBuilder, RegionName, RegionNameSource}; use crate::nll::ConstraintDescription; -use crate::region_infer::values::RegionElement; use crate::region_infer::{BlameConstraint, TypeTest}; use crate::session_diagnostics::{ FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr, LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote, }; use crate::universal_regions::DefiningTy; -use crate::{MirBorrowckCtxt, borrowck_errors, fluent_generated as fluent}; +use crate::{MirBorrowckCtxt, borrowck_errors}; impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { fn description(&self) -> &'static str { @@ -104,15 +103,9 @@ pub(crate) enum RegionErrorKind<'tcx> { /// A generic bound failure for a type test (`T: 'a`). TypeTestError { type_test: TypeTest<'tcx> }, - /// Higher-ranked subtyping error. - BoundUniversalRegionError { - /// The placeholder free region. - longer_fr: RegionVid, - /// The region element that erroneously must be outlived by `longer_fr`. - error_element: RegionElement<'tcx>, - /// The placeholder region. - placeholder: ty::PlaceholderRegion<'tcx>, - }, + /// 'p outlives 'r, which does not hold. 'p is always a placeholder + /// and 'r is some other region. + PlaceholderOutlivesIllegalRegion { longer_fr: RegionVid, illegally_outlived_r: RegionVid }, /// Any other lifetime error. RegionError { @@ -265,7 +258,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else { return; }; - diag.span_note(*trait_span, fluent::borrowck_limitations_implies_static); + diag.span_note(*trait_span, LIMITATION_NOTE); let Some(generics_fn) = tcx.hir_get_generics(self.body.source.def_id().expect_local()) else { return; @@ -298,7 +291,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if suggestions.len() > 0 { suggestions.dedup(); diag.multipart_suggestion_verbose( - fluent::borrowck_restrict_to_static, + inline_fluent!("consider restricting the type parameter to the `'static` lifetime"), suggestions, Applicability::MaybeIncorrect, ); @@ -360,28 +353,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - RegionErrorKind::BoundUniversalRegionError { + RegionErrorKind::PlaceholderOutlivesIllegalRegion { longer_fr, - placeholder, - error_element, + illegally_outlived_r, } => { - let error_vid = self.regioncx.region_from_element(longer_fr, &error_element); - - // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. - let cause = self - .regioncx - .best_blame_constraint( - longer_fr, - NllRegionVariableOrigin::Placeholder(placeholder), - error_vid, - ) - .0 - .cause; - - let universe = placeholder.universe; - let universe_info = self.regioncx.universe_info(universe); - - universe_info.report_erroneous_element(self, placeholder, error_element, cause); + self.report_erroneous_rvid_reaches_placeholder(longer_fr, illegally_outlived_r) } RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => { @@ -412,6 +388,43 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { outlives_suggestion.add_suggestion(self); } + /// Report that `longer_fr: error_vid`, which doesn't hold, + /// where `longer_fr` is a placeholder. + fn report_erroneous_rvid_reaches_placeholder( + &mut self, + longer_fr: RegionVid, + error_vid: RegionVid, + ) { + use NllRegionVariableOrigin::*; + + let origin_longer = self.regioncx.definitions[longer_fr].origin; + + let Placeholder(placeholder) = origin_longer else { + bug!("Expected {longer_fr:?} to come from placeholder!"); + }; + + // FIXME: Is throwing away the existential region really the best here? + let error_region = match self.regioncx.definitions[error_vid].origin { + FreeRegion | Existential { .. } => None, + Placeholder(other_placeholder) => Some(other_placeholder), + }; + + // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. + let cause = + self.regioncx.best_blame_constraint(longer_fr, origin_longer, error_vid).0.cause; + + // FIXME these methods should have better names, and also probably not be this generic. + // FIXME note that we *throw away* the error element here! We probably want to + // thread it through the computation further down and use it, but there currently isn't + // anything there to receive it. + self.regioncx.universe_info(placeholder.universe).report_erroneous_element( + self, + placeholder, + error_region, + cause, + ); + } + /// Report an error because the universal region `fr` was required to outlive /// `outlived_fr` but it is not known to do so. For example: /// @@ -872,6 +885,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { for alias_ty in alias_tys { if alias_ty.span.desugaring_kind().is_some() { // Skip `async` desugaring `impl Future`. + continue; } if let TyKind::TraitObject(_, lt) = alias_ty.kind { if lt.kind == hir::LifetimeKind::ImplicitObjectLifetimeDefault { @@ -966,12 +980,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { debug!("trait spans found: {:?}", traits); for span in &traits { let mut multi_span: MultiSpan = vec![*span].into(); - multi_span.push_span_label(*span, fluent::borrowck_implicit_static); - multi_span.push_span_label(ident.span, fluent::borrowck_implicit_static_introduced); + multi_span.push_span_label( + *span, + inline_fluent!("this has an implicit `'static` lifetime requirement"), + ); + multi_span.push_span_label( + ident.span, + inline_fluent!( + "calling this method introduces the `impl`'s `'static` requirement" + ), + ); err.subdiagnostic(RequireStaticErr::UsedImpl { multi_span }); err.span_suggestion_verbose( span.shrink_to_hi(), - fluent::borrowck_implicit_static_relax, + inline_fluent!("consider relaxing the implicit `'static` requirement"), " + '_", Applicability::MaybeIncorrect, ); @@ -1134,7 +1156,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if ocx.evaluate_obligations_error_on_ambiguity().is_empty() && count > 0 { diag.span_suggestion_verbose( tcx.hir_body(*body).value.peel_blocks().span.shrink_to_lo(), - fluent::borrowck_dereference_suggestion, + inline_fluent!("dereference the return value"), "*".repeat(count), Applicability::MachineApplicable, ); @@ -1178,7 +1200,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(closure_span) = closure_span { diag.span_suggestion_verbose( closure_span, - fluent::borrowck_move_closure_suggestion, + inline_fluent!("consider adding 'move' keyword before the nested closure"), "move ", Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 7c5cbc58e25c..2e6c1dceef98 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2,7 +2,7 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] #![feature(default_field_values)] #![feature(file_buffered)] @@ -63,10 +63,10 @@ use crate::diagnostics::{ use crate::path_utils::*; use crate::place_ext::PlaceExt; use crate::places_conflict::{PlaceConflictBias, places_conflict}; +use crate::polonius::PoloniusContext; use crate::polonius::legacy::{ PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, }; -use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext}; use crate::prefixes::PrefixSet; use crate::region_infer::RegionInferenceContext; use crate::region_infer::opaque_types::DeferredOpaqueTypeError; @@ -99,8 +99,6 @@ mod used_muts; /// A public API provided for the Rust compiler consumers. pub mod consumers; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - /// Associate some local constants with the `'tcx` lifetime struct TyCtxtConsts<'tcx>(PhantomData<&'tcx ()>); @@ -123,6 +121,11 @@ fn mir_borrowck( let (input_body, _) = tcx.mir_promoted(def); debug!("run query mir_borrowck: {}", tcx.def_path_str(def)); + // We should eagerly check stalled coroutine obligations from HIR typeck. + // Not doing so leads to silent normalization failures later, which will + // fail to register opaque types in the next solver. + tcx.check_coroutine_obligations(def)?; + let input_body: &Body<'_> = &input_body.borrow(); if let Some(guar) = input_body.tainted_by_errors { debug!("Skipping borrowck because of tainted body"); @@ -421,7 +424,7 @@ fn borrowck_check_region_constraints<'tcx>( polonius_output, opt_closure_req, nll_errors, - polonius_diagnostics, + polonius_context, } = nll::compute_regions( root_cx, &infcx, @@ -445,7 +448,7 @@ fn borrowck_check_region_constraints<'tcx>( ®ioncx, &opt_closure_req, &borrow_set, - polonius_diagnostics.as_ref(), + polonius_context.as_ref(), ); // We also have a `#[rustc_regions]` annotation that causes us to dump @@ -487,7 +490,7 @@ fn borrowck_check_region_constraints<'tcx>( polonius_output: None, move_errors: Vec::new(), diags_buffer, - polonius_diagnostics: polonius_diagnostics.as_ref(), + polonius_context: polonius_context.as_ref(), }; struct MoveVisitor<'a, 'b, 'infcx, 'tcx> { ctxt: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>, @@ -526,7 +529,7 @@ fn borrowck_check_region_constraints<'tcx>( move_errors: Vec::new(), diags_buffer, polonius_output: polonius_output.as_deref(), - polonius_diagnostics: polonius_diagnostics.as_ref(), + polonius_context: polonius_context.as_ref(), }; // Compute and report region errors, if any. @@ -776,7 +779,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { /// Results of Polonius analysis. polonius_output: Option<&'a PoloniusOutput>, /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics. - polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>, + polonius_context: Option<&'a PoloniusContext>, } // Check that: @@ -1207,6 +1210,17 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { "access_place: suppressing error place_span=`{:?}` kind=`{:?}`", place_span, kind ); + + // If the place is being mutated, then mark it as such anyway in order to suppress the + // `unused_mut` lint, which is likely incorrect once the access place error has been + // resolved. + if rw == ReadOrWrite::Write(WriteKind::Mutate) + && let Ok(root_place) = + self.is_mutable(place_span.0.as_ref(), is_local_mutation_allowed) + { + self.add_used_mut(root_place, state); + } + return; } diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 64e3b59acfff..acd01be47070 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -7,6 +7,8 @@ use std::str::FromStr; use polonius_engine::{Algorithm, AllFacts, Output}; use rustc_data_structures::frozen::Frozen; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_index::IndexSlice; use rustc_middle::mir::pretty::PrettyPrintMirOptions; use rustc_middle::mir::{Body, MirDumper, PassWhere, Promoted}; @@ -15,17 +17,16 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_session::config::MirIncludeSpans; -use rustc_span::sym; use tracing::{debug, instrument}; use crate::borrow_set::BorrowSet; use crate::consumers::RustcFacts; use crate::diagnostics::RegionErrors; use crate::handle_placeholders::compute_sccs_applying_placeholder_outlives_constraints; +use crate::polonius::PoloniusContext; use crate::polonius::legacy::{ PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, }; -use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext}; use crate::region_infer::RegionInferenceContext; use crate::type_check::MirTypeckRegionConstraints; use crate::type_check::free_region_relations::UniversalRegionRelations; @@ -46,7 +47,7 @@ pub(crate) struct NllOutput<'tcx> { /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g. /// localized typeck and liveness constraints. - pub polonius_diagnostics: Option, + pub polonius_context: Option, } /// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal @@ -121,7 +122,7 @@ pub(crate) fn compute_regions<'tcx>( universal_region_relations: Frozen>, constraints: MirTypeckRegionConstraints<'tcx>, mut polonius_facts: Option>, - polonius_context: Option, + mut polonius_context: Option, ) -> NllOutput<'tcx> { let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output()) || infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled(); @@ -153,9 +154,9 @@ pub(crate) fn compute_regions<'tcx>( // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints // and use them to compute loan liveness. - let polonius_diagnostics = polonius_context.map(|polonius_context| { - polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set) - }); + if let Some(polonius_context) = polonius_context.as_mut() { + polonius_context.compute_loan_liveness(&mut regioncx, body, borrow_set) + } // If requested: dump NLL facts, and run legacy polonius analysis. let polonius_output = polonius_facts.as_ref().and_then(|polonius_facts| { @@ -188,7 +189,7 @@ pub(crate) fn compute_regions<'tcx>( polonius_output, opt_closure_req: closure_region_requirements, nll_errors, - polonius_diagnostics, + polonius_context, } } @@ -230,13 +231,13 @@ pub(super) fn dump_nll_mir<'tcx>( dumper.dump_mir(body); // Also dump the region constraint graph as a graphviz file. - let _: io::Result<()> = try { + let _ = try { let mut file = dumper.create_dump_file("regioncx.all.dot", body)?; regioncx.dump_graphviz_raw_constraints(tcx, &mut file)?; }; // Also dump the region constraint SCC graph as a graphviz file. - let _: io::Result<()> = try { + let _ = try { let mut file = dumper.create_dump_file("regioncx.scc.dot", body)?; regioncx.dump_graphviz_scc_constraints(tcx, &mut file)?; }; @@ -295,7 +296,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>( ) { let tcx = infcx.tcx; let base_def_id = tcx.typeck_root_def_id(body.source.def_id()); - if !tcx.has_attr(base_def_id, sym::rustc_regions) { + if !find_attr!(tcx.get_all_attrs(base_def_id), AttributeKind::RustcRegions) { return; } diff --git a/compiler/rustc_borrowck/src/polonius/constraints.rs b/compiler/rustc_borrowck/src/polonius/constraints.rs index 525957578595..559b1bdc38d8 100644 --- a/compiler/rustc_borrowck/src/polonius/constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/constraints.rs @@ -1,6 +1,19 @@ +use std::collections::BTreeMap; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_index::interval::SparseIntervalMatrix; +use rustc_middle::mir::{Body, Location}; use rustc_middle::ty::RegionVid; use rustc_mir_dataflow::points::PointIndex; +use crate::BorrowSet; +use crate::constraints::OutlivesConstraint; +use crate::dataflow::BorrowIndex; +use crate::polonius::ConstraintDirection; +use crate::region_infer::values::LivenessValues; +use crate::type_check::Locations; +use crate::universal_regions::UniversalRegions; + /// A localized outlives constraint reifies the CFG location where the outlives constraint holds, /// within the origins themselves as if they were different from point to point: from `a: b` /// outlives constraints to `a@p: b@p`, where `p` is the point in the CFG. @@ -12,32 +25,300 @@ use rustc_mir_dataflow::points::PointIndex; /// of `q`. These depend on the liveness of the regions at these points, as well as their /// variance. /// -/// The `source` origin at `from` flows into the `target` origin at `to`. -/// /// This dual of NLL's [crate::constraints::OutlivesConstraint] therefore encodes the /// position-dependent outlives constraints used by Polonius, to model the flow-sensitive loan /// propagation via reachability within a graph of localized constraints. -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] -pub(crate) struct LocalizedOutlivesConstraint { - pub source: RegionVid, - pub from: PointIndex, - pub target: RegionVid, - pub to: PointIndex, +/// +/// That `LocalizedConstraintGraph` can create these edges on-demand during traversal, and we +/// therefore model them as a pair of `LocalizedNode` vertices. +/// +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub(super) struct LocalizedNode { + pub region: RegionVid, + pub point: PointIndex, } -/// A container of [LocalizedOutlivesConstraint]s that can be turned into a traversable -/// `rustc_data_structures` graph. -#[derive(Clone, Default, Debug)] -pub(crate) struct LocalizedOutlivesConstraintSet { - pub outlives: Vec, +/// The localized constraint graph indexes the physical and logical edges to lazily compute a given +/// node's successors during traversal. +pub(super) struct LocalizedConstraintGraph { + /// The actual, physical, edges we have recorded for a given node. We localize them on-demand + /// when traversing from the node to the successor region. + edges: FxHashMap>, + + /// The logical edges representing the outlives constraints that hold at all points in the CFG, + /// which we don't localize to avoid creating a lot of unnecessary edges in the graph. Some CFGs + /// can be big, and we don't need to create such a physical edge for every point in the CFG. + logical_edges: FxHashMap>, } -impl LocalizedOutlivesConstraintSet { - pub(crate) fn push(&mut self, constraint: LocalizedOutlivesConstraint) { - if constraint.source == constraint.target && constraint.from == constraint.to { - // 'a@p: 'a@p is pretty uninteresting - return; - } - self.outlives.push(constraint); +/// The visitor interface when traversing a `LocalizedConstraintGraph`. +pub(super) trait LocalizedConstraintGraphVisitor { + /// Callback called when traversing a given `loan` encounters a localized `node` it hasn't + /// visited before. + fn on_node_traversed(&mut self, _loan: BorrowIndex, _node: LocalizedNode) {} + + /// Callback called when discovering a new `successor` node for the `current_node`. + fn on_successor_discovered(&mut self, _current_node: LocalizedNode, _successor: LocalizedNode) { + } +} + +impl LocalizedConstraintGraph { + /// Traverses the constraints and returns the indexed graph of edges per node. + pub(super) fn new<'tcx>( + liveness: &LivenessValues, + outlives_constraints: impl Iterator>, + ) -> Self { + let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default(); + let mut logical_edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default(); + + for outlives_constraint in outlives_constraints { + match outlives_constraint.locations { + Locations::All(_) => { + logical_edges + .entry(outlives_constraint.sup) + .or_default() + .insert(outlives_constraint.sub); + } + + Locations::Single(location) => { + let node = LocalizedNode { + region: outlives_constraint.sup, + point: liveness.point_from_location(location), + }; + edges.entry(node).or_default().insert(outlives_constraint.sub); + } + } + } + + LocalizedConstraintGraph { edges, logical_edges } + } + + /// Traverses the localized constraint graph per-loan, and notifies the `visitor` of discovered + /// nodes and successors. + pub(super) fn traverse<'tcx>( + &self, + body: &Body<'tcx>, + liveness: &LivenessValues, + live_region_variances: &BTreeMap, + universal_regions: &UniversalRegions<'tcx>, + borrow_set: &BorrowSet<'tcx>, + visitor: &mut impl LocalizedConstraintGraphVisitor, + ) { + let live_regions = liveness.points(); + + let mut visited = FxHashSet::default(); + let mut stack = Vec::new(); + + // Compute reachability per loan by traversing each loan's subgraph starting from where it + // is introduced. + for (loan_idx, loan) in borrow_set.iter_enumerated() { + visited.clear(); + stack.clear(); + + let start_node = LocalizedNode { + region: loan.region, + point: liveness.point_from_location(loan.reserve_location), + }; + stack.push(start_node); + + while let Some(node) = stack.pop() { + if !visited.insert(node) { + continue; + } + + // We've reached a node we haven't visited before. + let location = liveness.location_from_point(node.point); + visitor.on_node_traversed(loan_idx, node); + + // When we find a _new_ successor, we'd like to + // - visit it eventually, + // - and let the generic visitor know about it. + let mut successor_found = |succ| { + if !visited.contains(&succ) { + stack.push(succ); + visitor.on_successor_discovered(node, succ); + } + }; + + // Then, we propagate the loan along the localized constraint graph. The outgoing + // edges are computed lazily, from: + // - the various physical edges present at this node, + // - the materialized logical edges that exist virtually at all points for this + // node's region, localized at this point. + + // Universal regions propagate loans along the CFG, i.e. forwards only. + let is_universal_region = universal_regions.is_universal_region(node.region); + + // The physical edges present at this node are: + // + // 1. the typeck edges that flow from region to region *at this point*. + for &succ in self.edges.get(&node).into_iter().flatten() { + let succ = LocalizedNode { region: succ, point: node.point }; + successor_found(succ); + } + + // 2a. the liveness edges that flow *forward*, from this node's point to its + // successors in the CFG. + if body[location.block].statements.get(location.statement_index).is_some() { + // Intra-block edges, straight line constraints from each point to its successor + // within the same block. + let next_point = node.point + 1; + if let Some(succ) = compute_forward_successor( + node.region, + next_point, + live_regions, + live_region_variances, + is_universal_region, + ) { + successor_found(succ); + } + } else { + // Inter-block edges, from the block's terminator to each successor block's + // entry point. + for successor_block in body[location.block].terminator().successors() { + let next_location = Location { block: successor_block, statement_index: 0 }; + let next_point = liveness.point_from_location(next_location); + if let Some(succ) = compute_forward_successor( + node.region, + next_point, + live_regions, + live_region_variances, + is_universal_region, + ) { + successor_found(succ); + } + } + } + + // 2b. the liveness edges that flow *backward*, from this node's point to its + // predecessors in the CFG. + if !is_universal_region { + if location.statement_index > 0 { + // Backward edges to the predecessor point in the same block. + let previous_point = PointIndex::from(node.point.as_usize() - 1); + if let Some(succ) = compute_backward_successor( + node.region, + node.point, + previous_point, + live_regions, + live_region_variances, + ) { + successor_found(succ); + } + } else { + // Backward edges from the block entry point to the terminator of the + // predecessor blocks. + let predecessors = body.basic_blocks.predecessors(); + for &pred_block in &predecessors[location.block] { + let previous_location = Location { + block: pred_block, + statement_index: body[pred_block].statements.len(), + }; + let previous_point = liveness.point_from_location(previous_location); + if let Some(succ) = compute_backward_successor( + node.region, + node.point, + previous_point, + live_regions, + live_region_variances, + ) { + successor_found(succ); + } + } + } + } + + // And finally, we have the logical edges, materialized at this point. + for &logical_succ in self.logical_edges.get(&node.region).into_iter().flatten() { + let succ = LocalizedNode { region: logical_succ, point: node.point }; + successor_found(succ); + } + } + } + } +} + +/// Returns the successor for the current region/point node when propagating a loan through forward +/// edges, if applicable, according to liveness and variance. +fn compute_forward_successor( + region: RegionVid, + next_point: PointIndex, + live_regions: &SparseIntervalMatrix, + live_region_variances: &BTreeMap, + is_universal_region: bool, +) -> Option { + // 1. Universal regions are semantically live at all points. + if is_universal_region { + let succ = LocalizedNode { region, point: next_point }; + return Some(succ); + } + + // 2. Otherwise, gather the edges due to explicit region liveness, when applicable. + if !live_regions.contains(region, next_point) { + return None; + } + + // Here, `region` could be live at the current point, and is live at the next point: add a + // constraint between them, according to variance. + + // Note: there currently are cases related to promoted and const generics, where we don't yet + // have variance information (possibly about temporary regions created when typeck sanitizes the + // promoteds). Until that is done, we conservatively fallback to maximizing reachability by + // adding a bidirectional edge here. This will not limit traversal whatsoever, and thus + // propagate liveness when needed. + // + // FIXME: add the missing variance information and remove this fallback bidirectional edge. + let direction = + live_region_variances.get(®ion).unwrap_or(&ConstraintDirection::Bidirectional); + + match direction { + ConstraintDirection::Backward => { + // Contravariant cases: loans flow in the inverse direction, but we're only interested + // in forward successors and there are none here. + None + } + ConstraintDirection::Forward | ConstraintDirection::Bidirectional => { + // 1. For covariant cases: loans flow in the regular direction, from the current point + // to the next point. + // 2. For invariant cases, loans can flow in both directions, but here as well, we only + // want the forward path of the bidirectional edge. + Some(LocalizedNode { region, point: next_point }) + } + } +} + +/// Returns the successor for the current region/point node when propagating a loan through backward +/// edges, if applicable, according to liveness and variance. +fn compute_backward_successor( + region: RegionVid, + current_point: PointIndex, + previous_point: PointIndex, + live_regions: &SparseIntervalMatrix, + live_region_variances: &BTreeMap, +) -> Option { + // Liveness flows into the regions live at the next point. So, in a backwards view, we'll link + // the region from the current point, if it's live there, to the previous point. + if !live_regions.contains(region, current_point) { + return None; + } + + // FIXME: add the missing variance information and remove this fallback bidirectional edge. See + // the same comment in `compute_forward_successor`. + let direction = + live_region_variances.get(®ion).unwrap_or(&ConstraintDirection::Bidirectional); + + match direction { + ConstraintDirection::Forward => { + // Covariant cases: loans flow in the regular direction, but we're only interested in + // backward successors and there are none here. + None + } + ConstraintDirection::Backward | ConstraintDirection::Bidirectional => { + // 1. For contravariant cases: loans flow in the inverse direction, from the current + // point to the previous point. + // 2. For invariant cases, loans can flow in both directions, but here as well, we only + // want the backward path of the bidirectional edge. + Some(LocalizedNode { region, point: previous_point }) + } } } diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 62f9ae173474..af50f68640f8 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -10,9 +10,7 @@ use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; use crate::constraints::OutlivesConstraint; -use crate::polonius::{ - LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, PoloniusDiagnosticsContext, -}; +use crate::polonius::{LocalizedConstraintGraphVisitor, LocalizedNode, PoloniusContext}; use crate::region_infer::values::LivenessValues; use crate::type_check::Locations; use crate::{BorrowckInferCtxt, ClosureRegionRequirements, RegionInferenceContext}; @@ -24,7 +22,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option>, borrow_set: &BorrowSet<'tcx>, - polonius_diagnostics: Option<&PoloniusDiagnosticsContext>, + polonius_context: Option<&PoloniusContext>, ) { let tcx = infcx.tcx; if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { @@ -33,8 +31,22 @@ pub(crate) fn dump_polonius_mir<'tcx>( let Some(dumper) = MirDumper::new(tcx, "polonius", body) else { return }; - let polonius_diagnostics = - polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`"); + let polonius_context = + polonius_context.expect("missing polonius context with `-Zpolonius=next`"); + + // If we have a polonius graph to dump along the rest of the MIR and NLL info, we extract its + // constraints here. + let mut collector = LocalizedOutlivesConstraintCollector { constraints: Vec::new() }; + if let Some(graph) = &polonius_context.graph { + graph.traverse( + body, + regioncx.liveness_constraints(), + &polonius_context.live_region_variances, + regioncx.universal_regions(), + borrow_set, + &mut collector, + ); + } let extra_data = &|pass_where, out: &mut dyn io::Write| { emit_polonius_mir( @@ -42,7 +54,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( regioncx, closure_region_requirements, borrow_set, - &polonius_diagnostics.localized_outlives_constraints, + &collector.constraints, pass_where, out, ) @@ -58,19 +70,36 @@ pub(crate) fn dump_polonius_mir<'tcx>( let dumper = dumper.set_extra_data(extra_data).set_options(options); - let _: io::Result<()> = try { + let _ = try { let mut file = dumper.create_dump_file("html", body)?; - emit_polonius_dump( - &dumper, - body, - regioncx, - borrow_set, - &polonius_diagnostics.localized_outlives_constraints, - &mut file, - )?; + emit_polonius_dump(&dumper, body, regioncx, borrow_set, &collector.constraints, &mut file)?; }; } +/// The constraints we'll dump as text or a mermaid graph. +struct LocalizedOutlivesConstraint { + source: RegionVid, + from: PointIndex, + target: RegionVid, + to: PointIndex, +} + +/// Visitor to record constraints encountered when traversing the localized constraint graph. +struct LocalizedOutlivesConstraintCollector { + constraints: Vec, +} + +impl LocalizedConstraintGraphVisitor for LocalizedOutlivesConstraintCollector { + fn on_successor_discovered(&mut self, current_node: LocalizedNode, successor: LocalizedNode) { + self.constraints.push(LocalizedOutlivesConstraint { + source: current_node.region, + from: current_node.point, + target: successor.region, + to: successor.point, + }); + } +} + /// The polonius dump consists of: /// - the NLL MIR /// - the list of polonius localized constraints @@ -82,7 +111,7 @@ fn emit_polonius_dump<'tcx>( body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, borrow_set: &BorrowSet<'tcx>, - localized_outlives_constraints: &LocalizedOutlivesConstraintSet, + localized_outlives_constraints: &[LocalizedOutlivesConstraint], out: &mut dyn io::Write, ) -> io::Result<()> { // Prepare the HTML dump file prologue. @@ -193,7 +222,7 @@ fn emit_polonius_mir<'tcx>( regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option>, borrow_set: &BorrowSet<'tcx>, - localized_outlives_constraints: &LocalizedOutlivesConstraintSet, + localized_outlives_constraints: &[LocalizedOutlivesConstraint], pass_where: PassWhere, out: &mut dyn io::Write, ) -> io::Result<()> { @@ -212,10 +241,10 @@ fn emit_polonius_mir<'tcx>( // Add localized outlives constraints match pass_where { PassWhere::BeforeCFG => { - if localized_outlives_constraints.outlives.len() > 0 { + if localized_outlives_constraints.len() > 0 { writeln!(out, "| Localized constraints")?; - for constraint in &localized_outlives_constraints.outlives { + for constraint in localized_outlives_constraints { let LocalizedOutlivesConstraint { source, from, target, to } = constraint; let from = liveness.location_from_point(*from); let to = liveness.location_from_point(*to); @@ -399,7 +428,7 @@ fn emit_mermaid_nll_sccs<'tcx>( fn emit_mermaid_constraint_graph<'tcx>( borrow_set: &BorrowSet<'tcx>, liveness: &LivenessValues, - localized_outlives_constraints: &LocalizedOutlivesConstraintSet, + localized_outlives_constraints: &[LocalizedOutlivesConstraint], out: &mut dyn io::Write, ) -> io::Result { let location_name = |location: Location| { @@ -438,7 +467,7 @@ fn emit_mermaid_constraint_graph<'tcx>( // The regions subgraphs containing the region/point nodes. let mut points_per_region: FxIndexMap> = FxIndexMap::default(); - for constraint in &localized_outlives_constraints.outlives { + for constraint in localized_outlives_constraints { points_per_region.entry(constraint.source).or_default().insert(constraint.from); points_per_region.entry(constraint.target).or_default().insert(constraint.to); } @@ -451,7 +480,7 @@ fn emit_mermaid_constraint_graph<'tcx>( } // The constraint graph edges. - for constraint in &localized_outlives_constraints.outlives { + for constraint in localized_outlives_constraints { // FIXME: add killed loans and constraint kind as edge labels. writeln!( out, @@ -463,6 +492,6 @@ fn emit_mermaid_constraint_graph<'tcx>( // Return the number of edges: this is the biggest graph in the dump and its edge count will be // mermaid's max edge count to support. - let edge_count = borrow_set.len() + localized_outlives_constraints.outlives.len(); + let edge_count = borrow_set.len() + localized_outlives_constraints.len(); Ok(edge_count) } diff --git a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs index f1338b3bf1ee..b6f8b4a79f39 100644 --- a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs @@ -1,22 +1,15 @@ use std::collections::BTreeMap; use rustc_hir::def_id::DefId; -use rustc_index::bit_set::SparseBitMatrix; -use rustc_middle::mir::{Body, Location}; use rustc_middle::ty::relate::{ self, Relate, RelateResult, TypeRelation, relate_args_with_variances, }; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable}; -use rustc_mir_dataflow::points::PointIndex; -use super::{ - ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, - PoloniusLivenessContext, -}; -use crate::region_infer::values::LivenessValues; +use super::{ConstraintDirection, PoloniusContext}; use crate::universal_regions::UniversalRegions; -impl PoloniusLivenessContext { +impl PoloniusContext { /// Record the variance of each region contained within the given value. pub(crate) fn record_live_region_variance<'tcx>( &mut self, @@ -34,165 +27,6 @@ impl PoloniusLivenessContext { } } -/// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives -/// constraints for loans that are propagated to the next statements. -pub(super) fn create_liveness_constraints<'tcx>( - body: &Body<'tcx>, - liveness: &LivenessValues, - live_regions: &SparseBitMatrix, - live_region_variances: &BTreeMap, - universal_regions: &UniversalRegions<'tcx>, - localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet, -) { - for (block, bb) in body.basic_blocks.iter_enumerated() { - let statement_count = bb.statements.len(); - for statement_index in 0..=statement_count { - let current_location = Location { block, statement_index }; - let current_point = liveness.point_from_location(current_location); - - if statement_index < statement_count { - // Intra-block edges, straight line constraints from each point to its successor - // within the same block. - let next_location = Location { block, statement_index: statement_index + 1 }; - let next_point = liveness.point_from_location(next_location); - propagate_loans_between_points( - current_point, - next_point, - live_regions, - live_region_variances, - universal_regions, - localized_outlives_constraints, - ); - } else { - // Inter-block edges, from the block's terminator to each successor block's entry - // point. - for successor_block in bb.terminator().successors() { - let next_location = Location { block: successor_block, statement_index: 0 }; - let next_point = liveness.point_from_location(next_location); - propagate_loans_between_points( - current_point, - next_point, - live_regions, - live_region_variances, - universal_regions, - localized_outlives_constraints, - ); - } - } - } - } -} - -/// Propagate loans within a region between two points in the CFG, if that region is live at both -/// the source and target points. -fn propagate_loans_between_points( - current_point: PointIndex, - next_point: PointIndex, - live_regions: &SparseBitMatrix, - live_region_variances: &BTreeMap, - universal_regions: &UniversalRegions<'_>, - localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet, -) { - // Universal regions are semantically live at all points. - // Note: we always have universal regions but they're not always (or often) involved in the - // subset graph. For now, we emit all their edges unconditionally, but some of these subgraphs - // will be disconnected from the rest of the graph and thus, unnecessary. - // - // FIXME: only emit the edges of universal regions that existential regions can reach. - for region in universal_regions.universal_regions_iter() { - localized_outlives_constraints.push(LocalizedOutlivesConstraint { - source: region, - from: current_point, - target: region, - to: next_point, - }); - } - - let Some(next_live_regions) = live_regions.row(next_point) else { - // There are no constraints to add: there are no live regions at the next point. - return; - }; - - for region in next_live_regions.iter() { - // `region` could be live at the current point, and is live at the next point: add a - // constraint between them, according to variance. - if let Some(&direction) = live_region_variances.get(®ion) { - add_liveness_constraint( - region, - current_point, - next_point, - direction, - localized_outlives_constraints, - ); - } else { - // Note: there currently are cases related to promoted and const generics, where we - // don't yet have variance information (possibly about temporary regions created when - // typeck sanitizes the promoteds). Until that is done, we conservatively fallback to - // maximizing reachability by adding a bidirectional edge here. This will not limit - // traversal whatsoever, and thus propagate liveness when needed. - // - // FIXME: add the missing variance information and remove this fallback bidirectional - // edge. - let fallback = ConstraintDirection::Bidirectional; - add_liveness_constraint( - region, - current_point, - next_point, - fallback, - localized_outlives_constraints, - ); - } - } -} - -/// Adds `LocalizedOutlivesConstraint`s between two connected points, according to the given edge -/// direction. -fn add_liveness_constraint( - region: RegionVid, - current_point: PointIndex, - next_point: PointIndex, - direction: ConstraintDirection, - localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet, -) { - match direction { - ConstraintDirection::Forward => { - // Covariant cases: loans flow in the regular direction, from the current point to the - // next point. - localized_outlives_constraints.push(LocalizedOutlivesConstraint { - source: region, - from: current_point, - target: region, - to: next_point, - }); - } - ConstraintDirection::Backward => { - // Contravariant cases: loans flow in the inverse direction, from the next point to the - // current point. - localized_outlives_constraints.push(LocalizedOutlivesConstraint { - source: region, - from: next_point, - target: region, - to: current_point, - }); - } - ConstraintDirection::Bidirectional => { - // For invariant cases, loans can flow in both directions: we add both edges. - localized_outlives_constraints.push(LocalizedOutlivesConstraint { - source: region, - from: current_point, - target: region, - to: next_point, - }); - localized_outlives_constraints.push(LocalizedOutlivesConstraint { - source: region, - from: next_point, - target: region, - to: current_point, - }); - } - } -} - /// Extracts variances for regions contained within types. Follows the same structure as /// `rustc_infer`'s `Generalizer`: we try to relate a type with itself to track and extract the /// variances of regions. diff --git a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs deleted file mode 100644 index bdc3047e5ba0..000000000000 --- a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs +++ /dev/null @@ -1,160 +0,0 @@ -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; -use rustc_middle::ty::RegionVid; -use rustc_mir_dataflow::points::PointIndex; - -use super::{LiveLoans, LocalizedOutlivesConstraintSet}; -use crate::BorrowSet; -use crate::constraints::OutlivesConstraint; -use crate::region_infer::values::LivenessValues; -use crate::type_check::Locations; - -/// Compute loan reachability to approximately trace loan liveness throughout the CFG, by -/// traversing the full graph of constraints that combines: -/// - the localized constraints (the physical edges), -/// - with the constraints that hold at all points (the logical edges). -pub(super) fn compute_loan_liveness<'tcx>( - liveness: &LivenessValues, - outlives_constraints: impl Iterator>, - borrow_set: &BorrowSet<'tcx>, - localized_outlives_constraints: &LocalizedOutlivesConstraintSet, -) -> LiveLoans { - let mut live_loans = LiveLoans::new(borrow_set.len()); - - // Create the full graph with the physical edges we've localized earlier, and the logical edges - // of constraints that hold at all points. - let logical_constraints = - outlives_constraints.filter(|c| matches!(c.locations, Locations::All(_))); - let graph = LocalizedConstraintGraph::new(&localized_outlives_constraints, logical_constraints); - let mut visited = FxHashSet::default(); - let mut stack = Vec::new(); - - // Compute reachability per loan by traversing each loan's subgraph starting from where it is - // introduced. - for (loan_idx, loan) in borrow_set.iter_enumerated() { - visited.clear(); - stack.clear(); - - let start_node = LocalizedNode { - region: loan.region, - point: liveness.point_from_location(loan.reserve_location), - }; - stack.push(start_node); - - while let Some(node) = stack.pop() { - if !visited.insert(node) { - continue; - } - - // Record the loan as being live on entry to this point if it reaches a live region - // there. - // - // This is an approximation of liveness (which is the thing we want), in that we're - // using a single notion of reachability to represent what used to be _two_ different - // transitive closures. It didn't seem impactful when coming up with the single-graph - // and reachability through space (regions) + time (CFG) concepts, but in practice the - // combination of time-traveling with kills is more impactful than initially - // anticipated. - // - // Kills should prevent a loan from reaching its successor points in the CFG, but not - // while time-traveling: we're not actually at that CFG point, but looking for - // predecessor regions that contain the loan. One of the two TCs we had pushed the - // transitive subset edges to each point instead of having backward edges, and the - // problem didn't exist before. In the abstract, naive reachability is not enough to - // model this, we'd need a slightly different solution. For example, maybe with a - // two-step traversal: - // - at each point we first traverse the subgraph (and possibly time-travel) looking for - // exit nodes while ignoring kills, - // - and then when we're back at the current point, we continue normally. - // - // Another (less annoying) subtlety is that kills and the loan use-map are - // flow-insensitive. Kills can actually appear in places before a loan is introduced, or - // at a location that is actually unreachable in the CFG from the introduction point, - // and these can also be encountered during time-traveling. - // - // The simplest change that made sense to "fix" the issues above is taking into - // account kills that are: - // - reachable from the introduction point - // - encountered during forward traversal. Note that this is not transitive like the - // two-step traversal described above: only kills encountered on exit via a backward - // edge are ignored. - // - // This version of the analysis, however, is enough in practice to pass the tests that - // we care about and NLLs reject, without regressions on crater, and is an actionable - // subset of the full analysis. It also naturally points to areas of improvement that we - // wish to explore later, namely handling kills appropriately during traversal, instead - // of continuing traversal to all the reachable nodes. - // - // FIXME: analyze potential unsoundness, possibly in concert with a borrowck - // implementation in a-mir-formality, fuzzing, or manually crafting counter-examples. - - if liveness.is_live_at(node.region, liveness.location_from_point(node.point)) { - live_loans.insert(node.point, loan_idx); - } - - for succ in graph.outgoing_edges(node) { - stack.push(succ); - } - } - } - - live_loans -} - -/// The localized constraint graph indexes the physical and logical edges to compute a given node's -/// successors during traversal. -struct LocalizedConstraintGraph { - /// The actual, physical, edges we have recorded for a given node. - edges: FxHashMap>, - - /// The logical edges representing the outlives constraints that hold at all points in the CFG, - /// which we don't localize to avoid creating a lot of unnecessary edges in the graph. Some CFGs - /// can be big, and we don't need to create such a physical edge for every point in the CFG. - logical_edges: FxHashMap>, -} - -/// A node in the graph to be traversed, one of the two vertices of a localized outlives constraint. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -struct LocalizedNode { - region: RegionVid, - point: PointIndex, -} - -impl LocalizedConstraintGraph { - /// Traverses the constraints and returns the indexed graph of edges per node. - fn new<'tcx>( - constraints: &LocalizedOutlivesConstraintSet, - logical_constraints: impl Iterator>, - ) -> Self { - let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default(); - for constraint in &constraints.outlives { - let source = LocalizedNode { region: constraint.source, point: constraint.from }; - let target = LocalizedNode { region: constraint.target, point: constraint.to }; - edges.entry(source).or_default().insert(target); - } - - let mut logical_edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default(); - for constraint in logical_constraints { - logical_edges.entry(constraint.sup).or_default().insert(constraint.sub); - } - - LocalizedConstraintGraph { edges, logical_edges } - } - - /// Returns the outgoing edges of a given node, not its transitive closure. - fn outgoing_edges(&self, node: LocalizedNode) -> impl Iterator { - // The outgoing edges are: - // - the physical edges present at this node, - // - the materialized logical edges that exist virtually at all points for this node's - // region, localized at this point. - let physical_edges = - self.edges.get(&node).into_iter().flat_map(|targets| targets.iter().copied()); - let materialized_edges = - self.logical_edges.get(&node.region).into_iter().flat_map(move |targets| { - targets - .iter() - .copied() - .map(move |target| LocalizedNode { point: node.point, region: target }) - }); - physical_edges.chain(materialized_edges) - } -} diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index a9092b1981e1..0924ce25c52e 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -32,47 +32,37 @@ //! - //! - //! -//! -//! Data flows like this: -//! 1) during MIR typeck, record liveness data needed later: live region variances, as well as the -//! usual NLL liveness data (just computed on more locals). That's the [PoloniusLivenessContext]. -//! 2) once that is done, variance data is transferred, and the NLL region liveness is converted to -//! the polonius shape. That's the main [PoloniusContext]. -//! 3) during region inference, that data and the NLL outlives constraints are used to create the -//! localized outlives constraints, as described above. That's the [PoloniusDiagnosticsContext]. -//! 4) transfer this back to the main borrowck procedure: it handles computing errors and -//! diagnostics, debugging and MIR dumping concerns. mod constraints; mod dump; pub(crate) mod legacy; mod liveness_constraints; -mod loan_liveness; -mod typeck_constraints; use std::collections::BTreeMap; use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::SparseBitMatrix; -use rustc_index::interval::SparseIntervalMatrix; use rustc_middle::mir::{Body, Local}; -use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_middle::ty::RegionVid; use rustc_mir_dataflow::points::PointIndex; -pub(crate) use self::constraints::*; +pub(self) use self::constraints::*; pub(crate) use self::dump::dump_polonius_mir; -use self::liveness_constraints::create_liveness_constraints; -use self::loan_liveness::compute_loan_liveness; -use self::typeck_constraints::convert_typeck_constraints; use crate::dataflow::BorrowIndex; +use crate::region_infer::values::LivenessValues; use crate::{BorrowSet, RegionInferenceContext}; pub(crate) type LiveLoans = SparseBitMatrix; -/// This struct holds the liveness data created during MIR typeck, and which will be used later in -/// the process, to compute the polonius localized constraints. +/// This struct holds the necessary +/// - liveness data, created during MIR typeck, and which will be used to lazily compute the +/// polonius localized constraints, during NLL region inference as well as MIR dumping, +/// - data needed by the borrowck error computation and diagnostics. #[derive(Default)] -pub(crate) struct PoloniusLivenessContext { +pub(crate) struct PoloniusContext { + /// The graph from which we extract the localized outlives constraints. + graph: Option, + /// The expected edge direction per live region: the kind of directed edge we'll create as /// liveness constraints depends on the variance of types with respect to each contained region. live_region_variances: BTreeMap, @@ -84,27 +74,6 @@ pub(crate) struct PoloniusLivenessContext { pub(crate) boring_nll_locals: FxHashSet, } -/// This struct holds the data needed to create the Polonius localized constraints. Its data is -/// transferred and converted from the [PoloniusLivenessContext] at the end of MIR typeck. -pub(crate) struct PoloniusContext { - /// The liveness data we recorded during MIR typeck. - liveness_context: PoloniusLivenessContext, - - /// The set of regions that are live at a given point in the CFG, used to create localized - /// outlives constraints between regions that are live at connected points in the CFG. - live_regions: SparseBitMatrix, -} - -/// This struct holds the data needed by the borrowck error computation and diagnostics. Its data is -/// computed from the [PoloniusContext] when computing NLL regions. -pub(crate) struct PoloniusDiagnosticsContext { - /// The localized outlives constraints that were computed in the main analysis. - localized_outlives_constraints: LocalizedOutlivesConstraintSet, - - /// The liveness data computed during MIR typeck: [PoloniusLivenessContext::boring_nll_locals]. - pub(crate) boring_nll_locals: FxHashSet, -} - /// The direction a constraint can flow into. Used to create liveness constraints according to /// variance. #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -120,26 +89,6 @@ enum ConstraintDirection { } impl PoloniusContext { - /// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we - /// need to transpose the "points where each region is live" matrix to a "live regions per point" - /// matrix. - // FIXME: avoid this conversion by always storing liveness data in this shape in the rest of - // borrowck. - pub(crate) fn create_from_liveness( - liveness_context: PoloniusLivenessContext, - num_regions: usize, - points_per_live_region: &SparseIntervalMatrix, - ) -> PoloniusContext { - let mut live_regions_per_point = SparseBitMatrix::new(num_regions); - for region in points_per_live_region.rows() { - for point in points_per_live_region.row(region).unwrap().iter() { - live_regions_per_point.insert(point, region); - } - } - - PoloniusContext { live_regions: live_regions_per_point, liveness_context } - } - /// Computes live loans using the set of loans model for `-Zpolonius=next`. /// /// First, creates a constraint graph combining regions and CFG points, by: @@ -151,44 +100,91 @@ impl PoloniusContext { /// /// The constraint data will be used to compute errors and diagnostics. pub(crate) fn compute_loan_liveness<'tcx>( - self, - tcx: TyCtxt<'tcx>, + &mut self, regioncx: &mut RegionInferenceContext<'tcx>, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, - ) -> PoloniusDiagnosticsContext { - let PoloniusLivenessContext { live_region_variances, boring_nll_locals } = - self.liveness_context; + ) { + let liveness = regioncx.liveness_constraints(); - let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default(); - convert_typeck_constraints( - tcx, - body, - regioncx.liveness_constraints(), - regioncx.outlives_constraints(), - regioncx.universal_regions(), - &mut localized_outlives_constraints, - ); + // We don't need to prepare the graph (index NLL constraints, etc.) if we have no loans to + // trace throughout localized constraints. + if borrow_set.len() > 0 { + // From the outlives constraints, liveness, and variances, we can compute reachability + // on the lazy localized constraint graph to trace the liveness of loans, for the next + // step in the chain (the NLL loan scope and active loans computations). + let graph = LocalizedConstraintGraph::new(liveness, regioncx.outlives_constraints()); - create_liveness_constraints( - body, - regioncx.liveness_constraints(), - &self.live_regions, - &live_region_variances, - regioncx.universal_regions(), - &mut localized_outlives_constraints, - ); + let mut live_loans = LiveLoans::new(borrow_set.len()); + let mut visitor = LoanLivenessVisitor { liveness, live_loans: &mut live_loans }; + graph.traverse( + body, + liveness, + &self.live_region_variances, + regioncx.universal_regions(), + borrow_set, + &mut visitor, + ); + regioncx.record_live_loans(live_loans); - // Now that we have a complete graph, we can compute reachability to trace the liveness of - // loans for the next step in the chain, the NLL loan scope and active loans computations. - let live_loans = compute_loan_liveness( - regioncx.liveness_constraints(), - regioncx.outlives_constraints(), - borrow_set, - &localized_outlives_constraints, - ); - regioncx.record_live_loans(live_loans); - - PoloniusDiagnosticsContext { localized_outlives_constraints, boring_nll_locals } + // The graph can be traversed again during MIR dumping, so we store it here. + self.graph = Some(graph); + } + } +} + +/// Visitor to record loan liveness when traversing the localized constraint graph. +struct LoanLivenessVisitor<'a> { + liveness: &'a LivenessValues, + live_loans: &'a mut LiveLoans, +} + +impl LocalizedConstraintGraphVisitor for LoanLivenessVisitor<'_> { + fn on_node_traversed(&mut self, loan: BorrowIndex, node: LocalizedNode) { + // Record the loan as being live on entry to this point if it reaches a live region + // there. + // + // This is an approximation of liveness (which is the thing we want), in that we're + // using a single notion of reachability to represent what used to be _two_ different + // transitive closures. It didn't seem impactful when coming up with the single-graph + // and reachability through space (regions) + time (CFG) concepts, but in practice the + // combination of time-traveling with kills is more impactful than initially + // anticipated. + // + // Kills should prevent a loan from reaching its successor points in the CFG, but not + // while time-traveling: we're not actually at that CFG point, but looking for + // predecessor regions that contain the loan. One of the two TCs we had pushed the + // transitive subset edges to each point instead of having backward edges, and the + // problem didn't exist before. In the abstract, naive reachability is not enough to + // model this, we'd need a slightly different solution. For example, maybe with a + // two-step traversal: + // - at each point we first traverse the subgraph (and possibly time-travel) looking for + // exit nodes while ignoring kills, + // - and then when we're back at the current point, we continue normally. + // + // Another (less annoying) subtlety is that kills and the loan use-map are + // flow-insensitive. Kills can actually appear in places before a loan is introduced, or + // at a location that is actually unreachable in the CFG from the introduction point, + // and these can also be encountered during time-traveling. + // + // The simplest change that made sense to "fix" the issues above is taking into account + // kills that are: + // - reachable from the introduction point + // - encountered during forward traversal. Note that this is not transitive like the + // two-step traversal described above: only kills encountered on exit via a backward + // edge are ignored. + // + // This version of the analysis, however, is enough in practice to pass the tests that + // we care about and NLLs reject, without regressions on crater, and is an actionable + // subset of the full analysis. It also naturally points to areas of improvement that we + // wish to explore later, namely handling kills appropriately during traversal, instead + // of continuing traversal to all the reachable nodes. + // + // FIXME: analyze potential unsoundness, possibly in concert with a borrowck + // implementation in a-mir-formality, fuzzing, or manually crafting counter-examples. + let location = self.liveness.location_from_point(node.point); + if self.liveness.is_live_at(node.region, location) { + self.live_loans.insert(node.point, loan); + } } } diff --git a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs deleted file mode 100644 index cfe9376fb502..000000000000 --- a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs +++ /dev/null @@ -1,208 +0,0 @@ -use rustc_data_structures::fx::FxHashSet; -use rustc_middle::mir::{Body, Location, Statement, StatementKind, Terminator, TerminatorKind}; -use rustc_middle::ty::{TyCtxt, TypeVisitable}; -use rustc_mir_dataflow::points::PointIndex; - -use super::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; -use crate::constraints::OutlivesConstraint; -use crate::region_infer::values::LivenessValues; -use crate::type_check::Locations; -use crate::universal_regions::UniversalRegions; - -/// Propagate loans throughout the subset graph at a given point (with some subtleties around the -/// location where effects start to be visible). -pub(super) fn convert_typeck_constraints<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - liveness: &LivenessValues, - outlives_constraints: impl Iterator>, - universal_regions: &UniversalRegions<'tcx>, - localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet, -) { - for outlives_constraint in outlives_constraints { - match outlives_constraint.locations { - Locations::All(_) => { - // We don't turn constraints holding at all points into physical edges at every - // point in the graph. They are encoded into *traversal* instead: a given node's - // successors will combine these logical edges with the regular, physical, localized - // edges. - continue; - } - - Locations::Single(location) => { - // This constraint is marked as holding at one location, we localize it to that - // location or its successor, depending on the corresponding MIR - // statement/terminator. Unfortunately, they all show up from typeck as coming "on - // entry", so for now we modify them to take effects that should apply "on exit" - // into account. - // - // FIXME: this approach is subtle, complicated, and hard to test, so we should track - // this information better in MIR typeck instead, for example with a new `Locations` - // variant that contains which node is crossing over between entry and exit. - let point = liveness.point_from_location(location); - let localized_constraint = if let Some(stmt) = - body[location.block].statements.get(location.statement_index) - { - localize_statement_constraint( - tcx, - stmt, - &outlives_constraint, - point, - universal_regions, - ) - } else { - assert_eq!(location.statement_index, body[location.block].statements.len()); - let terminator = body[location.block].terminator(); - localize_terminator_constraint( - tcx, - body, - terminator, - liveness, - &outlives_constraint, - point, - universal_regions, - ) - }; - localized_outlives_constraints.push(localized_constraint); - } - } - } -} - -/// For a given outlives constraint arising from a MIR statement, localize the constraint with the -/// needed CFG `from`-`to` intra-block nodes. -fn localize_statement_constraint<'tcx>( - tcx: TyCtxt<'tcx>, - stmt: &Statement<'tcx>, - outlives_constraint: &OutlivesConstraint<'tcx>, - current_point: PointIndex, - universal_regions: &UniversalRegions<'tcx>, -) -> LocalizedOutlivesConstraint { - match &stmt.kind { - StatementKind::Assign(box (lhs, rhs)) => { - // To create localized outlives constraints without midpoints, we rely on the property - // that no input regions from the RHS of the assignment will flow into themselves: they - // should not appear in the output regions in the LHS. We believe this to be true by - // construction of the MIR, via temporaries, and assert it here. - // - // We think we don't need midpoints because: - // - every LHS Place has a unique set of regions that don't appear elsewhere - // - this implies that for them to be part of the RHS, the same Place must be read and - // written - // - and that should be impossible in MIR - // - // When we have a more complete implementation in the future, tested with crater, etc, - // we can remove this assertion. It's a debug assert because it can be expensive. - debug_assert!( - { - let mut lhs_regions = FxHashSet::default(); - tcx.for_each_free_region(lhs, |region| { - let region = universal_regions.to_region_vid(region); - lhs_regions.insert(region); - }); - - let mut rhs_regions = FxHashSet::default(); - tcx.for_each_free_region(rhs, |region| { - let region = universal_regions.to_region_vid(region); - rhs_regions.insert(region); - }); - - // The intersection between LHS and RHS regions should be empty. - lhs_regions.is_disjoint(&rhs_regions) - }, - "there should be no common regions between the LHS and RHS of an assignment" - ); - } - _ => { - // Assignments should be the only statement that can both generate constraints that - // apply on entry (specific to the RHS place) *and* others that only apply on exit (the - // subset of RHS regions that actually flow into the LHS): i.e., where midpoints would - // be used to ensure the former happen before the latter, within the same MIR Location. - } - } - - // We generally localize an outlives constraint to where it arises. - LocalizedOutlivesConstraint { - source: outlives_constraint.sup, - from: current_point, - target: outlives_constraint.sub, - to: current_point, - } -} - -/// For a given outlives constraint arising from a MIR terminator, localize the constraint with the -/// needed CFG `from`-`to` inter-block nodes. -fn localize_terminator_constraint<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - terminator: &Terminator<'tcx>, - liveness: &LivenessValues, - outlives_constraint: &OutlivesConstraint<'tcx>, - current_point: PointIndex, - universal_regions: &UniversalRegions<'tcx>, -) -> LocalizedOutlivesConstraint { - // FIXME: check if other terminators need the same handling as `Call`s, in particular - // Assert/Yield/Drop. - match &terminator.kind { - // FIXME: also handle diverging calls. - TerminatorKind::Call { destination, target: Some(target), .. } => { - // If there is a target for the call we also relate what flows into the destination here - // to entry to that successor. - let destination_ty = destination.ty(&body.local_decls, tcx); - let successor_location = Location { block: *target, statement_index: 0 }; - let successor_point = liveness.point_from_location(successor_location); - compute_constraint_direction( - tcx, - outlives_constraint, - &destination_ty, - current_point, - successor_point, - universal_regions, - ) - } - _ => { - // Typeck constraints guide loans between regions at the current point, so we do that in - // the general case, and liveness will take care of making them flow to the terminator's - // successors. - LocalizedOutlivesConstraint { - source: outlives_constraint.sup, - from: current_point, - target: outlives_constraint.sub, - to: current_point, - } - } - } -} - -/// For a given outlives constraint and CFG edge, returns the localized constraint with the -/// appropriate `from`-`to` direction. This is computed according to whether the constraint flows to -/// or from a free region in the given `value`, some kind of result for an effectful operation, like -/// the LHS of an assignment. -fn compute_constraint_direction<'tcx>( - tcx: TyCtxt<'tcx>, - outlives_constraint: &OutlivesConstraint<'tcx>, - value: &impl TypeVisitable>, - current_point: PointIndex, - successor_point: PointIndex, - universal_regions: &UniversalRegions<'tcx>, -) -> LocalizedOutlivesConstraint { - let mut to = current_point; - let mut from = current_point; - tcx.for_each_free_region(value, |region| { - let region = universal_regions.to_region_vid(region); - if region == outlives_constraint.sub { - // This constraint flows into the result, its effects start becoming visible on exit. - to = successor_point; - } else if region == outlives_constraint.sup { - // This constraint flows from the result, its effects start becoming visible on exit. - from = successor_point; - } - }); - - LocalizedOutlivesConstraint { - source: outlives_constraint.sup, - from, - target: outlives_constraint.sub, - to, - } -} diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 6ed70b39c5b7..5cdda777723b 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1379,11 +1379,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { .elements_contained_in(longer_fr_scc) .find(|e| *e != RegionElement::PlaceholderRegion(placeholder)) { + let illegally_outlived_r = self.region_from_element(longer_fr, &error_element); // Stop after the first error, it gets too noisy otherwise, and does not provide more information. - errors_buffer.push(RegionErrorKind::BoundUniversalRegionError { + errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion { longer_fr, - error_element, - placeholder, + illegally_outlived_r, }); } else { debug!("check_bound_universal_region: all bounds satisfied"); @@ -1572,7 +1572,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Get the region outlived by `longer_fr` and live at `element`. - pub(crate) fn region_from_element( + fn region_from_element( &self, longer_fr: RegionVid, element: &RegionElement<'tcx>, diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 5143b2fa2059..7bde534dafd2 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -7,16 +7,16 @@ use rustc_span::Span; use crate::diagnostics::RegionName; #[derive(Diagnostic)] -#[diag(borrowck_move_unsized, code = E0161)] +#[diag("cannot move a value of type `{$ty}`", code = E0161)] pub(crate) struct MoveUnsized<'tcx> { pub ty: Ty<'tcx>, #[primary_span] - #[label] + #[label("the size of `{$ty}` cannot be statically determined")] pub span: Span, } #[derive(Diagnostic)] -#[diag(borrowck_higher_ranked_lifetime_error)] +#[diag("higher-ranked lifetime error")] pub(crate) struct HigherRankedLifetimeError { #[subdiagnostic] pub cause: Option, @@ -26,21 +26,21 @@ pub(crate) struct HigherRankedLifetimeError { #[derive(Subdiagnostic)] pub(crate) enum HigherRankedErrorCause { - #[note(borrowck_could_not_prove)] + #[note("could not prove `{$predicate}`")] CouldNotProve { predicate: String }, - #[note(borrowck_could_not_normalize)] + #[note("could not normalize `{$value}`")] CouldNotNormalize { value: String }, } #[derive(Diagnostic)] -#[diag(borrowck_higher_ranked_subtype_error)] +#[diag("higher-ranked subtype error")] pub(crate) struct HigherRankedSubtypeError { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(borrowck_generic_does_not_live_long_enough)] +#[diag("`{$kind}` does not live long enough")] pub(crate) struct GenericDoesNotLiveLongEnough { pub kind: String, #[primary_span] @@ -48,15 +48,20 @@ pub(crate) struct GenericDoesNotLiveLongEnough { } #[derive(LintDiagnostic)] -#[diag(borrowck_var_does_not_need_mut)] +#[diag("variable does not need to be mutable")] pub(crate) struct VarNeedNotMut { - #[suggestion(style = "short", applicability = "machine-applicable", code = "")] + #[suggestion( + "remove this `mut`", + style = "short", + applicability = "machine-applicable", + code = "" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(borrowck_var_cannot_escape_closure)] -#[note] -#[note(borrowck_cannot_escape)] +#[diag("captured variable cannot escape `FnMut` closure body")] +#[note("`FnMut` closures only have access to their captured variables while they are executing...")] +#[note("...therefore, they cannot allow references to captured variables to escape")] pub(crate) struct FnMutError { #[primary_span] pub span: Span, @@ -66,17 +71,17 @@ pub(crate) struct FnMutError { #[derive(Subdiagnostic)] pub(crate) enum VarHereDenote { - #[label(borrowck_var_here_captured)] + #[label("variable captured here")] Captured { #[primary_span] span: Span, }, - #[label(borrowck_var_here_defined)] + #[label("variable defined here")] Defined { #[primary_span] span: Span, }, - #[label(borrowck_closure_inferred_mut)] + #[label("inferred to be a `FnMut` closure")] FnMutInferred { #[primary_span] span: Span, @@ -85,17 +90,21 @@ pub(crate) enum VarHereDenote { #[derive(Subdiagnostic)] pub(crate) enum FnMutReturnTypeErr { - #[label(borrowck_returned_closure_escaped)] + #[label( + "returns a closure that contains a reference to a captured variable, which then escapes the closure body" + )] ReturnClosure { #[primary_span] span: Span, }, - #[label(borrowck_returned_async_block_escaped)] + #[label( + "returns an `async` block that contains a reference to a captured variable, which then escapes the closure body" + )] ReturnAsyncBlock { #[primary_span] span: Span, }, - #[label(borrowck_returned_ref_escaped)] + #[label("returns a reference to a captured variable which escapes the closure body")] ReturnRef { #[primary_span] span: Span, @@ -103,7 +112,7 @@ pub(crate) enum FnMutReturnTypeErr { } #[derive(Diagnostic)] -#[diag(borrowck_lifetime_constraints_error)] +#[diag("lifetime may not live long enough")] pub(crate) struct LifetimeOutliveErr { #[primary_span] pub span: Span, @@ -111,7 +120,9 @@ pub(crate) struct LifetimeOutliveErr { #[derive(Subdiagnostic)] pub(crate) enum LifetimeReturnCategoryErr<'a> { - #[label(borrowck_returned_lifetime_wrong)] + #[label( + "{$mir_def_name} was supposed to return data with lifetime `{$outlived_fr_name}` but it is returning data with lifetime `{$fr_name}`" + )] WrongReturn { #[primary_span] span: Span, @@ -119,7 +130,9 @@ pub(crate) enum LifetimeReturnCategoryErr<'a> { outlived_fr_name: RegionName, fr_name: &'a RegionName, }, - #[label(borrowck_returned_lifetime_short)] + #[label( + "{$category_desc}requires that `{$free_region_name}` must outlive `{$outlived_fr_name}`" + )] ShortReturn { #[primary_span] span: Span, @@ -131,7 +144,7 @@ pub(crate) enum LifetimeReturnCategoryErr<'a> { #[derive(Subdiagnostic)] pub(crate) enum RequireStaticErr { - #[note(borrowck_used_impl_require_static)] + #[note("the used `impl` has a `'static` requirement")] UsedImpl { #[primary_span] multi_span: MultiSpan, @@ -140,42 +153,42 @@ pub(crate) enum RequireStaticErr { #[derive(Subdiagnostic)] pub(crate) enum CaptureVarPathUseCause { - #[label(borrowck_borrow_due_to_use_coroutine)] + #[label("borrow occurs due to use in coroutine")] BorrowInCoroutine { #[primary_span] path_span: Span, }, - #[label(borrowck_use_due_to_use_coroutine)] + #[label("use occurs due to use in coroutine")] UseInCoroutine { #[primary_span] path_span: Span, }, - #[label(borrowck_assign_due_to_use_coroutine)] + #[label("assign occurs due to use in coroutine")] AssignInCoroutine { #[primary_span] path_span: Span, }, - #[label(borrowck_assign_part_due_to_use_coroutine)] + #[label("assign to part occurs due to use in coroutine")] AssignPartInCoroutine { #[primary_span] path_span: Span, }, - #[label(borrowck_borrow_due_to_use_closure)] + #[label("borrow occurs due to use in closure")] BorrowInClosure { #[primary_span] path_span: Span, }, - #[label(borrowck_use_due_to_use_closure)] + #[label("use occurs due to use in closure")] UseInClosure { #[primary_span] path_span: Span, }, - #[label(borrowck_assign_due_to_use_closure)] + #[label("assignment occurs due to use in closure")] AssignInClosure { #[primary_span] path_span: Span, }, - #[label(borrowck_assign_part_due_to_use_closure)] + #[label("assignment to part occurs due to use in closure")] AssignPartInClosure { #[primary_span] path_span: Span, @@ -184,17 +197,17 @@ pub(crate) enum CaptureVarPathUseCause { #[derive(Subdiagnostic)] pub(crate) enum CaptureVarKind { - #[label(borrowck_capture_immute)] + #[label("capture is immutable because of use here")] Immut { #[primary_span] kind_span: Span, }, - #[label(borrowck_capture_mut)] + #[label("capture is mutable because of use here")] Mut { #[primary_span] kind_span: Span, }, - #[label(borrowck_capture_move)] + #[label("capture is moved because of use here")] Move { #[primary_span] kind_span: Span, @@ -203,77 +216,97 @@ pub(crate) enum CaptureVarKind { #[derive(Subdiagnostic)] pub(crate) enum CaptureVarCause { - #[label(borrowck_var_borrow_by_use_place_in_coroutine)] + #[label( + "{$is_single_var -> + *[true] borrow occurs + [false] borrows occur + } due to use of {$place} in coroutine" + )] BorrowUsePlaceCoroutine { is_single_var: bool, place: String, #[primary_span] var_span: Span, }, - #[label(borrowck_var_borrow_by_use_place_in_closure)] + #[label( + "{$is_single_var -> + *[true] borrow occurs + [false] borrows occur + } due to use of {$place} in closure" + )] BorrowUsePlaceClosure { is_single_var: bool, place: String, #[primary_span] var_span: Span, }, - #[label(borrowck_var_borrow_by_use_in_coroutine)] + #[label("borrow occurs due to use in coroutine")] BorrowUseInCoroutine { #[primary_span] var_span: Span, }, - #[label(borrowck_var_borrow_by_use_in_closure)] + #[label("borrow occurs due to use in closure")] BorrowUseInClosure { #[primary_span] var_span: Span, }, - #[label(borrowck_var_move_by_use_in_coroutine)] + #[label("move occurs due to use in coroutine")] MoveUseInCoroutine { #[primary_span] var_span: Span, }, - #[label(borrowck_var_move_by_use_in_closure)] + #[label("move occurs due to use in closure")] MoveUseInClosure { #[primary_span] var_span: Span, }, - #[label(borrowck_var_first_borrow_by_use_place_in_coroutine)] + #[label("first borrow occurs due to use of {$place} in coroutine")] FirstBorrowUsePlaceCoroutine { place: String, #[primary_span] var_span: Span, }, - #[label(borrowck_var_first_borrow_by_use_place_in_closure)] + #[label("first borrow occurs due to use of {$place} in closure")] FirstBorrowUsePlaceClosure { place: String, #[primary_span] var_span: Span, }, - #[label(borrowck_var_second_borrow_by_use_place_in_coroutine)] + #[label("second borrow occurs due to use of {$place} in coroutine")] SecondBorrowUsePlaceCoroutine { place: String, #[primary_span] var_span: Span, }, - #[label(borrowck_var_second_borrow_by_use_place_in_closure)] + #[label("second borrow occurs due to use of {$place} in closure")] SecondBorrowUsePlaceClosure { place: String, #[primary_span] var_span: Span, }, - #[label(borrowck_var_mutable_borrow_by_use_place_in_closure)] + #[label("mutable borrow occurs due to use of {$place} in closure")] MutableBorrowUsePlaceClosure { place: String, #[primary_span] var_span: Span, }, - #[label(borrowck_partial_var_move_by_use_in_coroutine)] + #[label( + "variable {$is_partial -> + [true] partially moved + *[false] moved + } due to use in coroutine" + )] PartialMoveUseInCoroutine { #[primary_span] var_span: Span, is_partial: bool, }, - #[label(borrowck_partial_var_move_by_use_in_closure)] + #[label( + "variable {$is_partial -> + [true] partially moved + *[false] moved + } due to use in closure" + )] PartialMoveUseInClosure { #[primary_span] var_span: Span, @@ -282,34 +315,57 @@ pub(crate) enum CaptureVarCause { } #[derive(Diagnostic)] -#[diag(borrowck_cannot_move_when_borrowed, code = E0505)] +#[diag("cannot move out of {$place -> + [value] value + *[other] {$place} +} because it is borrowed", code = E0505)] pub(crate) struct MoveBorrow<'a> { pub place: &'a str, pub borrow_place: &'a str, pub value_place: &'a str, #[primary_span] - #[label(borrowck_move_label)] + #[label( + "move out of {$value_place -> + [value] value + *[other] {$value_place} + } occurs here" + )] pub span: Span, - #[label] + #[label( + "borrow of {$borrow_place -> + [value] value + *[other] {$borrow_place} + } occurs here" + )] pub borrow_span: Span, } #[derive(Diagnostic)] -#[diag(borrowck_opaque_type_lifetime_mismatch)] +#[diag("opaque type used twice with different lifetimes")] pub(crate) struct LifetimeMismatchOpaqueParam<'tcx> { pub arg: GenericArg<'tcx>, pub prev: GenericArg<'tcx>, #[primary_span] - #[label] - #[note] + #[label("lifetime `{$arg}` used here")] + #[note( + "if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types" + )] pub span: Span, - #[label(borrowck_prev_lifetime_label)] + #[label("lifetime `{$prev}` previously used here")] pub prev_span: Span, } #[derive(Subdiagnostic)] pub(crate) enum CaptureReasonLabel<'a> { - #[label(borrowck_moved_due_to_call)] + #[label( + "{$place_name} {$is_partial -> + [true] partially moved + *[false] moved + } due to this {$is_loop_message -> + [true] call, in previous iteration of loop + *[false] call + }" + )] Call { #[primary_span] fn_call_span: Span, @@ -317,7 +373,15 @@ pub(crate) enum CaptureReasonLabel<'a> { is_partial: bool, is_loop_message: bool, }, - #[label(borrowck_moved_due_to_usage_in_operator)] + #[label( + "{$place_name} {$is_partial -> + [true] partially moved + *[false] moved + } due to usage in {$is_loop_message -> + [true] operator, in previous iteration of loop + *[false] operator + }" + )] OperatorUse { #[primary_span] fn_call_span: Span, @@ -325,7 +389,15 @@ pub(crate) enum CaptureReasonLabel<'a> { is_partial: bool, is_loop_message: bool, }, - #[label(borrowck_moved_due_to_implicit_into_iter_call)] + #[label( + "{$place_name} {$is_partial -> + [true] partially moved + *[false] moved + } due to this implicit call to {$is_loop_message -> + [true] `.into_iter()`, in previous iteration of loop + *[false] `.into_iter()` + }" + )] ImplicitCall { #[primary_span] fn_call_span: Span, @@ -333,7 +405,15 @@ pub(crate) enum CaptureReasonLabel<'a> { is_partial: bool, is_loop_message: bool, }, - #[label(borrowck_moved_due_to_method_call)] + #[label( + "{$place_name} {$is_partial -> + [true] partially moved + *[false] moved + } due to this method {$is_loop_message -> + [true] call, in previous iteration of loop + *[false] call + }" + )] MethodCall { #[primary_span] fn_call_span: Span, @@ -341,7 +421,15 @@ pub(crate) enum CaptureReasonLabel<'a> { is_partial: bool, is_loop_message: bool, }, - #[label(borrowck_moved_due_to_await)] + #[label( + "{$place_name} {$is_partial -> + [true] partially moved + *[false] moved + } due to this {$is_loop_message -> + [true] await, in previous iteration of loop + *[false] await + }" + )] Await { #[primary_span] fn_call_span: Span, @@ -349,7 +437,18 @@ pub(crate) enum CaptureReasonLabel<'a> { is_partial: bool, is_loop_message: bool, }, - #[label(borrowck_value_moved_here)] + #[label( + "value {$is_partial -> + [true] partially moved + *[false] moved + } {$is_move_msg -> + [true] into closure here + *[false] here + }{$is_loop_message -> + [true] , in previous iteration of loop + *[false] {\"\"} + }" + )] MovedHere { #[primary_span] move_span: Span, @@ -357,7 +456,7 @@ pub(crate) enum CaptureReasonLabel<'a> { is_move_msg: bool, is_loop_message: bool, }, - #[label(borrowck_consider_borrow_type_contents)] + #[label("help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents")] BorrowContent { #[primary_span] var_span: Span, @@ -366,22 +465,22 @@ pub(crate) enum CaptureReasonLabel<'a> { #[derive(Subdiagnostic)] pub(crate) enum CaptureReasonNote { - #[note(borrowck_moved_a_fn_once_in_call)] + #[note("this value implements `FnOnce`, which causes it to be moved when called")] FnOnceMoveInCall { #[primary_span] var_span: Span, }, - #[note(borrowck_calling_operator_moves)] + #[note("calling this operator moves the value")] UnOpMoveByOperator { #[primary_span] span: Span, }, - #[note(borrowck_calling_operator_moves_lhs)] + #[note("calling this operator moves the left-hand side")] LhsMoveByOperator { #[primary_span] span: Span, }, - #[note(borrowck_func_take_self_moved_place)] + #[note("`{$func}` takes ownership of the receiver `self`, which moves {$place_name}")] FuncTakeSelf { func: String, place_name: String, @@ -393,7 +492,7 @@ pub(crate) enum CaptureReasonNote { #[derive(Subdiagnostic)] pub(crate) enum CaptureReasonSuggest<'tcx> { #[suggestion( - borrowck_suggest_iterate_over_slice, + "consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop", applicability = "maybe-incorrect", code = "&", style = "verbose" @@ -404,7 +503,7 @@ pub(crate) enum CaptureReasonSuggest<'tcx> { span: Span, }, #[suggestion( - borrowck_suggest_create_fresh_reborrow, + "consider reborrowing the `Pin` instead of moving it", applicability = "maybe-incorrect", code = ".as_mut()", style = "verbose" @@ -417,13 +516,18 @@ pub(crate) enum CaptureReasonSuggest<'tcx> { #[derive(Subdiagnostic)] pub(crate) enum CaptureArgLabel { - #[label(borrowck_value_capture_here)] + #[label( + "value captured {$is_within -> + [true] here by coroutine + *[false] here + }" + )] Capture { is_within: bool, #[primary_span] args_span: Span, }, - #[label(borrowck_move_out_place_here)] + #[label("{$place} is moved here")] MoveOutPlace { place: String, #[primary_span] @@ -433,13 +537,17 @@ pub(crate) enum CaptureArgLabel { #[derive(Subdiagnostic)] pub(crate) enum OnClosureNote<'a> { - #[note(borrowck_closure_invoked_twice)] + #[note( + "closure cannot be invoked more than once because it moves the variable `{$place_name}` out of its environment" + )] InvokedTwice { place_name: &'a str, #[primary_span] span: Span, }, - #[note(borrowck_closure_moved_twice)] + #[note( + "closure cannot be moved more than once as it is not `Copy` due to moving the variable `{$place_name}` out of its environment" + )] MovedTwice { place_name: &'a str, #[primary_span] @@ -449,7 +557,12 @@ pub(crate) enum OnClosureNote<'a> { #[derive(Subdiagnostic)] pub(crate) enum TypeNoCopy<'a, 'tcx> { - #[label(borrowck_ty_no_impl_copy)] + #[label( + "{$is_partial_move -> + [true] partial move + *[false] move + } occurs because {$place} has type `{$ty}`, which does not implement the `Copy` trait" + )] Label { is_partial_move: bool, ty: Ty<'tcx>, @@ -457,12 +570,24 @@ pub(crate) enum TypeNoCopy<'a, 'tcx> { #[primary_span] span: Span, }, - #[note(borrowck_ty_no_impl_copy)] + #[note( + "{$is_partial_move -> + [true] partial move + *[false] move + } occurs because {$place} has type `{$ty}`, which does not implement the `Copy` trait" + )] Note { is_partial_move: bool, ty: Ty<'tcx>, place: &'a str }, } #[derive(Diagnostic)] -#[diag(borrowck_simd_intrinsic_arg_const)] +#[diag( + "{$arg -> + [1] 1st + [2] 2nd + [3] 3rd + *[other] {$arg}th + } argument of `{$intrinsic}` is required to be a `const` item" +)] pub(crate) struct SimdIntrinsicArgConst { #[primary_span] pub span: Span, @@ -471,8 +596,8 @@ pub(crate) struct SimdIntrinsicArgConst { } #[derive(LintDiagnostic)] -#[diag(borrowck_tail_expr_drop_order)] +#[diag("relative drop order changing in Rust 2024")] pub(crate) struct TailExprDropOrder { - #[label] + #[label("this temporary value will be dropped at the end of the block")] pub borrowed: Span, } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index ca1b850f7665..442c37e26ec1 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -11,7 +11,7 @@ use tracing::debug; use super::TypeChecker; use crate::constraints::OutlivesConstraintSet; -use crate::polonius::PoloniusLivenessContext; +use crate::polonius::PoloniusContext; use crate::region_infer::values::LivenessValues; use crate::universal_regions::UniversalRegions; @@ -48,7 +48,7 @@ pub(super) fn generate<'tcx>( if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() { let (_, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body); - typeck.polonius_liveness.as_mut().unwrap().boring_nll_locals = + typeck.polonius_context.as_mut().unwrap().boring_nll_locals = boring_locals.into_iter().collect(); free_regions = typeck.universal_regions.universal_regions_iter().collect(); } @@ -63,7 +63,7 @@ pub(super) fn generate<'tcx>( typeck.tcx(), &mut typeck.constraints.liveness_constraints, &typeck.universal_regions, - &mut typeck.polonius_liveness, + &mut typeck.polonius_context, typeck.body, ); } @@ -140,11 +140,11 @@ fn record_regular_live_regions<'tcx>( tcx: TyCtxt<'tcx>, liveness_constraints: &mut LivenessValues, universal_regions: &UniversalRegions<'tcx>, - polonius_liveness: &mut Option, + polonius_context: &mut Option, body: &Body<'tcx>, ) { let mut visitor = - LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_liveness }; + LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_context }; for (bb, data) in body.basic_blocks.iter_enumerated() { visitor.visit_basic_block_data(bb, data); } @@ -155,7 +155,7 @@ struct LiveVariablesVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, liveness_constraints: &'a mut LivenessValues, universal_regions: &'a UniversalRegions<'tcx>, - polonius_liveness: &'a mut Option, + polonius_context: &'a mut Option, } impl<'a, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'a, 'tcx> { @@ -207,8 +207,8 @@ impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> { }); // When using `-Zpolonius=next`, we record the variance of each live region. - if let Some(polonius_liveness) = self.polonius_liveness { - polonius_liveness.record_live_region_variance(self.tcx, self.universal_regions, value); + if let Some(polonius_context) = self.polonius_context { + polonius_context.record_live_region_variance(self.tcx, self.universal_regions, value); } } } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 7ac94020de03..840210496eb4 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -622,8 +622,8 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { }); // When using `-Zpolonius=next`, we record the variance of each live region. - if let Some(polonius_liveness) = typeck.polonius_liveness.as_mut() { - polonius_liveness.record_live_region_variance( + if let Some(polonius_context) = typeck.polonius_context.as_mut() { + polonius_context.record_live_region_variance( typeck.infcx.tcx, typeck.universal_regions, value, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 98b4e4d81b92..7d34d7c88e62 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -42,8 +42,8 @@ use tracing::{debug, instrument, trace}; use crate::borrow_set::BorrowSet; use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet}; use crate::diagnostics::UniverseInfo; +use crate::polonius::PoloniusContext; use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable}; -use crate::polonius::{PoloniusContext, PoloniusLivenessContext}; use crate::region_infer::TypeTest; use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderIndices}; use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst}; @@ -139,8 +139,8 @@ pub(crate) fn type_check<'tcx>( debug!(?normalized_inputs_and_output); - let polonius_liveness = if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { - Some(PoloniusLivenessContext::default()) + let polonius_context = if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { + Some(PoloniusContext::default()) } else { None }; @@ -162,7 +162,7 @@ pub(crate) fn type_check<'tcx>( borrow_set, constraints: &mut constraints, deferred_closure_requirements: &mut deferred_closure_requirements, - polonius_liveness, + polonius_context, }; typeck.check_user_type_annotations(); @@ -172,14 +172,7 @@ pub(crate) fn type_check<'tcx>( liveness::generate(&mut typeck, &location_map, move_data); - // We're done with typeck, we can finalize the polonius liveness context for region inference. - let polonius_context = typeck.polonius_liveness.take().map(|liveness_context| { - PoloniusContext::create_from_liveness( - liveness_context, - infcx.num_region_vars(), - typeck.constraints.liveness_constraints.points(), - ) - }); + let polonius_context = typeck.polonius_context; // In case type check encountered an error region, we suppress unhelpful extra // errors in by clearing out all outlives bounds that we may end up checking. @@ -238,7 +231,7 @@ struct TypeChecker<'a, 'tcx> { constraints: &'a mut MirTypeckRegionConstraints<'tcx>, deferred_closure_requirements: &'a mut DeferredClosureRequirements<'tcx>, /// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints. - polonius_liveness: Option, + polonius_context: Option, } /// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index 12ccc4a6de04..c87f1e41043d 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -43,7 +43,7 @@ pub(crate) fn expand( // Generate anonymous constant serving as container for the allocator methods. let const_ty = ecx.ty(sig_span, TyKind::Tup(ThinVec::new())); - let const_body = ast::ConstItemRhs::Body(ecx.expr_block(ecx.block(span, stmts))); + let const_body = ast::ConstItemRhsKind::new_body(ecx.expr_block(ecx.block(span, stmts))); let const_item = ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body); let const_item = if is_stmt { Annotatable::Stmt(Box::new(ecx.stmt_item(span, const_item))) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 2958686f86cd..62a39d957b9c 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -288,6 +288,18 @@ fn expand_preparsed_asm( let msg = "asm template must be a string literal"; let template_sp = template_expr.span; let template_is_mac_call = matches!(template_expr.kind, ast::ExprKind::MacCall(_)); + + // Gets the span inside `template_sp` corresponding to the given range + let span_in_template = |range: std::ops::Range| -> Span { + if template_is_mac_call { + // When the template is a macro call we can't reliably get inner spans + // so just use the entire template span (see ICEs #129503, #131292) + template_sp + } else { + template_sp.from_inner(InnerSpan::new(range.start, range.end)) + } + }; + let ExprToSpannedString { symbol: template_str, style: template_style, @@ -382,13 +394,8 @@ fn expand_preparsed_asm( if !parser.errors.is_empty() { let err = parser.errors.remove(0); - let err_sp = if template_is_mac_call { - // If the template is a macro call we can't reliably point to the error's - // span so just use the template's span as the error span (fixes #129503) - template_span - } else { - template_span.from_inner(InnerSpan::new(err.span.start, err.span.end)) - }; + + let err_sp = span_in_template(err.span); let msg = format!("invalid asm template string: {}", err.description); let mut e = ecx.dcx().struct_span_err(err_sp, msg); @@ -397,8 +404,7 @@ fn expand_preparsed_asm( e.note(note); } if let Some((label, span)) = err.secondary_label { - let err_sp = template_span.from_inner(InnerSpan::new(span.start, span.end)); - e.span_label(err_sp, label); + e.span_label(span_in_template(span), label); } let guar = e.emit(); return ExpandResult::Ready(Err(guar)); @@ -477,8 +483,7 @@ fn expand_preparsed_asm( ecx.dcx() .create_err(errors::AsmNoMatchedArgumentName { name: name.to_owned(), - span: template_span - .from_inner(InnerSpan::new(span.start, span.end)), + span: span_in_template(span), }) .emit(); None @@ -490,11 +495,7 @@ fn expand_preparsed_asm( let mut chars = arg.format.ty.chars(); let mut modifier = chars.next(); if chars.next().is_some() { - let span = arg - .format - .ty_span - .map(|sp| template_sp.from_inner(InnerSpan::new(sp.start, sp.end))) - .unwrap_or(template_sp); + let span = arg.format.ty_span.map(span_in_template).unwrap_or(template_sp); ecx.dcx().emit_err(errors::AsmModifierInvalid { span }); modifier = None; } diff --git a/compiler/rustc_builtin_macros/src/cfg_select.rs b/compiler/rustc_builtin_macros/src/cfg_select.rs index f11190b28105..35098722a910 100644 --- a/compiler/rustc_builtin_macros/src/cfg_select.rs +++ b/compiler/rustc_builtin_macros/src/cfg_select.rs @@ -1,14 +1,12 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::{Expr, ast}; use rustc_attr_parsing as attr; -use rustc_attr_parsing::{ - CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, parse_cfg_select, -}; +use rustc_attr_parsing::{CfgSelectBranches, EvalConfigResult, parse_cfg_select}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult}; use rustc_span::{Ident, Span, sym}; use smallvec::SmallVec; -use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable}; +use crate::errors::CfgSelectNoMatches; /// This intermediate structure is used to emit parse errors for the branches that are not chosen. /// The `MacResult` instance below parses all branches, emitting any errors it encounters, but only @@ -75,18 +73,6 @@ pub(super) fn expand_cfg_select<'cx>( ecx.current_expansion.lint_node_id, ) { Ok(mut branches) => { - if let Some((underscore, _, _)) = branches.wildcard { - // Warn for every unreachable predicate. We store the fully parsed branch for rustfmt. - for (predicate, _, _) in &branches.unreachable { - let span = match predicate { - CfgSelectPredicate::Wildcard(underscore) => underscore.span, - CfgSelectPredicate::Cfg(cfg) => cfg.span(), - }; - let err = CfgSelectUnreachable { span, wildcard_span: underscore.span }; - ecx.dcx().emit_warn(err); - } - } - if let Some((selected_tts, selected_span)) = branches.pop_first_match(|cfg| { matches!(attr::eval_config_entry(&ecx.sess, cfg), EvalConfigResult::True) }) { diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index bc86cdbc9f7e..3fe5e89ef06d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -540,6 +540,7 @@ impl<'a> TraitDef<'a> { .filter(|a| { a.has_any_name(&[ sym::allow, + sym::expect, sym::warn, sym::deny, sym::forbid, diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 538f5afae5fc..7b651ed84828 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -232,7 +232,7 @@ fn generate_default_impl( span, underscore, unit, - ast::ConstItemRhs::Body(ecx.expr_block(ecx.block(span, stmts))), + ast::ConstItemRhsKind::new_body(ecx.expr_block(ecx.block(span, stmts))), ) }; diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index d3f7e1c5d8e3..862ce3e62cc9 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -678,6 +678,18 @@ pub(crate) enum InvalidFormatStringSuggestion { #[primary_span] span: Span, }, + + #[suggestion( + "use rust debug printing macro", + code = "{replacement}", + style = "verbose", + applicability = "machine-applicable" + )] + UseRustDebugPrintingMacro { + #[primary_span] + macro_span: Span, + replacement: String, + }, } #[derive(Diagnostic)] @@ -990,20 +1002,6 @@ pub(crate) struct AsmUnsupportedClobberAbi { pub(crate) macro_name: &'static str, } -#[derive(Diagnostic)] -#[diag("`test_runner` argument must be a path")] -pub(crate) struct TestRunnerInvalid { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("`#![test_runner(..)]` accepts exactly 1 argument")] -pub(crate) struct TestRunnerNargs { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag("expected token: `,`")] pub(crate) struct ExpectedCommaInList { @@ -1086,17 +1084,6 @@ pub(crate) struct CfgSelectNoMatches { pub span: Span, } -#[derive(Diagnostic)] -#[diag("unreachable predicate")] -pub(crate) struct CfgSelectUnreachable { - #[primary_span] - #[label("this predicate is never reached")] - pub span: Span, - - #[label("always matches")] - pub wildcard_span: Span, -} - #[derive(Diagnostic)] #[diag("`#[eii_declaration(...)]` is only valid on macros")] pub(crate) struct EiiExternTargetExpectedMacro { diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 12cb2cd00694..f47dae5eba00 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -160,6 +160,7 @@ fn make_format_args( ecx: &mut ExtCtxt<'_>, input: MacroInput, append_newline: bool, + macro_span: Span, ) -> ExpandResult, ()> { let msg = "format argument must be a string literal"; let unexpanded_fmt_span = input.fmtstr.span; @@ -333,6 +334,23 @@ fn make_format_args( let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); e.sugg_ = Some(errors::InvalidFormatStringSuggestion::AddMissingColon { span }); } + parse::Suggestion::UseRustDebugPrintingMacro => { + // This targets `println!("{=}", x);` and `println!("{0=}", x);` + if let [arg] = args.all_args() { + let expr_span = arg.expr.span; + if let Ok(expr_snippet) = ecx.source_map().span_to_snippet(expr_span) { + let replacement = format!("{}!({})", "dbg", expr_snippet); + + let call_span = macro_span.source_callsite(); + e.sugg_ = Some( + errors::InvalidFormatStringSuggestion::UseRustDebugPrintingMacro { + macro_span: call_span, + replacement, + }, + ); + } + } + } } let guar = ecx.dcx().emit_err(e); return ExpandResult::Ready(Err(guar)); @@ -1048,7 +1066,7 @@ fn expand_format_args_impl<'cx>( sp = ecx.with_def_site_ctxt(sp); ExpandResult::Ready(match parse_args(ecx, sp, tts) { Ok(input) => { - let ExpandResult::Ready(mac) = make_format_args(ecx, input, nl) else { + let ExpandResult::Ready(mac) = make_format_args(ecx, input, nl, sp) else { return ExpandResult::Retry(()); }; match mac { diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index bb031dafdcfd..9e22d408c125 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -47,7 +47,7 @@ pub(crate) fn expand( // Generate anonymous constant serving as container for the allocator methods. let const_ty = ecx.ty(ty_span, TyKind::Tup(ThinVec::new())); - let const_body = ast::ConstItemRhs::Body(ecx.expr_block(ecx.block(span, stmts))); + let const_body = ast::ConstItemRhsKind::new_body(ecx.expr_block(ecx.block(span, stmts))); let const_item = ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body); let const_item = if is_stmt { Annotatable::Stmt(Box::new(ecx.stmt_item(span, const_item))) diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 8ac15ceb4bbe..08da5935bbea 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -3,7 +3,7 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index e30d506a31b9..24a5d79958c6 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -385,7 +385,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> Box { cx.attr_nested_word(sym::allow, sym::deprecated, span), ]); - let block = ast::ConstItemRhs::Body(cx.expr_block( + let block = ast::ConstItemRhsKind::new_body(cx.expr_block( cx.block(span, thin_vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)]), )); diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index d5f774865a9e..a9718d53ac49 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -289,7 +289,7 @@ pub(crate) fn expand_test_or_bench( ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), define_opaque: None, // test::TestDescAndFn { - rhs: Some(ast::ConstItemRhs::Body( + rhs_kind: ast::ConstItemRhsKind::new_body( cx.expr_struct( sp, test_path("TestDescAndFn"), @@ -371,7 +371,7 @@ pub(crate) fn expand_test_or_bench( field("testfn", test_fn), // } ], ), // } - )), + ), } .into(), ), diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 8d6969b0ca12..b5d63511fce9 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -8,10 +8,11 @@ use rustc_ast::entry::EntryPointType; use rustc_ast::mut_visit::*; use rustc_ast::visit::Visitor; use rustc_ast::{ModKind, attr}; -use rustc_errors::DiagCtxtHandle; +use rustc_attr_parsing::AttributeParser; use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; +use rustc_hir::attrs::AttributeKind; use rustc_session::Session; use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS; use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; @@ -60,7 +61,7 @@ pub fn inject( // Do this here so that the test_runner crate attribute gets marked as used // even in non-test builds - let test_runner = get_test_runner(dcx, krate); + let test_runner = get_test_runner(sess, features, krate); if sess.is_test_crate() { let panic_strategy = match (panic_strategy, sess.opts.unstable_opts.panic_abort_tests) { @@ -386,20 +387,16 @@ fn get_test_name(i: &ast::Item) -> Option { attr::first_attr_value_str_by_name(&i.attrs, sym::rustc_test_marker) } -fn get_test_runner(dcx: DiagCtxtHandle<'_>, krate: &ast::Crate) -> Option { - let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?; - let meta_list = test_attr.meta_item_list()?; - let span = test_attr.span; - match &*meta_list { - [single] => match single.meta_item() { - Some(meta_item) if meta_item.is_word() => return Some(meta_item.path.clone()), - _ => { - dcx.emit_err(errors::TestRunnerInvalid { span }); - } - }, - _ => { - dcx.emit_err(errors::TestRunnerNargs { span }); - } +fn get_test_runner(sess: &Session, features: &Features, krate: &ast::Crate) -> Option { + match AttributeParser::parse_limited( + sess, + &krate.attrs, + sym::test_runner, + krate.spans.inner_span, + krate.id, + Some(features), + ) { + Some(rustc_hir::Attribute::Parsed(AttributeKind::TestRunner(path))) => Some(path), + _ => None, } - None } diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 760e23f2171b..fc5c634d9570 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -2,29 +2,26 @@ //! standalone executable. use std::env; -use std::fs::{self, File}; +use std::fs::File; use std::io::BufWriter; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::Arc; use std::thread::JoinHandle; use cranelift_object::{ObjectBuilder, ObjectModule}; use rustc_codegen_ssa::assert_module_sources::CguReuse; -use rustc_codegen_ssa::back::link::ensure_removed; +use rustc_codegen_ssa::back::write::{CompiledModules, produce_final_output_artifacts}; use rustc_codegen_ssa::base::determine_cgu_reuse; -use rustc_codegen_ssa::{ - CodegenResults, CompiledModule, CrateInfo, ModuleKind, errors as ssa_errors, -}; +use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_hir::attrs::Linkage as RLinkage; -use rustc_metadata::fs::copy_to_stdout; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::mono::{CodegenUnit, MonoItem, MonoItemData, Visibility}; use rustc_session::Session; -use rustc_session::config::{OutFileName, OutputFilenames, OutputType}; +use rustc_session::config::{OutputFilenames, OutputType}; use crate::base::CodegenedFunction; use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken}; @@ -125,203 +122,22 @@ impl OngoingCodegen { sess.dcx().abort_if_errors(); - let codegen_results = CodegenResults { - modules, - allocator_module: self.allocator_module, - crate_info: self.crate_info, - }; + let compiled_modules = CompiledModules { modules, allocator_module: self.allocator_module }; - produce_final_output_artifacts(sess, &codegen_results, outputs); + produce_final_output_artifacts(sess, &compiled_modules, outputs); - (codegen_results, work_products) + ( + CodegenResults { + crate_info: self.crate_info, + + modules: compiled_modules.modules, + allocator_module: compiled_modules.allocator_module, + }, + work_products, + ) } } -// Adapted from https://github.com/rust-lang/rust/blob/73476d49904751f8d90ce904e16dfbc278083d2c/compiler/rustc_codegen_ssa/src/back/write.rs#L547C1-L706C2 -fn produce_final_output_artifacts( - sess: &Session, - codegen_results: &CodegenResults, - crate_output: &OutputFilenames, -) { - let user_wants_bitcode = false; - let mut user_wants_objects = false; - - // Produce final compile outputs. - let copy_gracefully = |from: &Path, to: &OutFileName| match to { - OutFileName::Stdout => { - if let Err(e) = copy_to_stdout(from) { - sess.dcx().emit_err(ssa_errors::CopyPath::new(from, to.as_path(), e)); - } - } - OutFileName::Real(path) => { - if let Err(e) = fs::copy(from, path) { - sess.dcx().emit_err(ssa_errors::CopyPath::new(from, path, e)); - } - } - }; - - let copy_if_one_unit = |output_type: OutputType, keep_numbered: bool| { - if codegen_results.modules.len() == 1 { - // 1) Only one codegen unit. In this case it's no difficulty - // to copy `foo.0.x` to `foo.x`. - let path = crate_output.temp_path_for_cgu( - output_type, - &codegen_results.modules[0].name, - sess.invocation_temp.as_deref(), - ); - let output = crate_output.path(output_type); - if !output_type.is_text_output() && output.is_tty() { - sess.dcx() - .emit_err(ssa_errors::BinaryOutputToTty { shorthand: output_type.shorthand() }); - } else { - copy_gracefully(&path, &output); - } - if !sess.opts.cg.save_temps && !keep_numbered { - // The user just wants `foo.x`, not `foo.#module-name#.x`. - ensure_removed(sess.dcx(), &path); - } - } else { - if crate_output.outputs.contains_explicit_name(&output_type) { - // 2) Multiple codegen units, with `--emit foo=some_name`. We have - // no good solution for this case, so warn the user. - sess.dcx() - .emit_warn(ssa_errors::IgnoringEmitPath { extension: output_type.extension() }); - } else if crate_output.single_output_file.is_some() { - // 3) Multiple codegen units, with `-o some_name`. We have - // no good solution for this case, so warn the user. - sess.dcx() - .emit_warn(ssa_errors::IgnoringOutput { extension: output_type.extension() }); - } else { - // 4) Multiple codegen units, but no explicit name. We - // just leave the `foo.0.x` files in place. - // (We don't have to do any work in this case.) - } - } - }; - - // Flag to indicate whether the user explicitly requested bitcode. - // Otherwise, we produced it only as a temporary output, and will need - // to get rid of it. - for output_type in crate_output.outputs.keys() { - match *output_type { - OutputType::Bitcode | OutputType::ThinLinkBitcode => { - // Cranelift doesn't have bitcode - // user_wants_bitcode = true; - // // Copy to .bc, but always keep the .0.bc. There is a later - // // check to figure out if we should delete .0.bc files, or keep - // // them for making an rlib. - // copy_if_one_unit(OutputType::Bitcode, true); - } - OutputType::LlvmAssembly => { - // Cranelift IR text already emitted during codegen - // copy_if_one_unit(OutputType::LlvmAssembly, false); - } - OutputType::Assembly => { - // Currently no support for emitting raw assembly files - // copy_if_one_unit(OutputType::Assembly, false); - } - OutputType::Object => { - user_wants_objects = true; - copy_if_one_unit(OutputType::Object, true); - } - OutputType::Mir | OutputType::Metadata | OutputType::Exe | OutputType::DepInfo => {} - } - } - - // Clean up unwanted temporary files. - - // We create the following files by default: - // - #crate#.#module-name#.bc - // - #crate#.#module-name#.o - // - #crate#.crate.metadata.bc - // - #crate#.crate.metadata.o - // - #crate#.o (linked from crate.##.o) - // - #crate#.bc (copied from crate.##.bc) - // We may create additional files if requested by the user (through - // `-C save-temps` or `--emit=` flags). - - if !sess.opts.cg.save_temps { - // Remove the temporary .#module-name#.o objects. If the user didn't - // explicitly request bitcode (with --emit=bc), and the bitcode is not - // needed for building an rlib, then we must remove .#module-name#.bc as - // well. - - // Specific rules for keeping .#module-name#.bc: - // - If the user requested bitcode (`user_wants_bitcode`), and - // codegen_units > 1, then keep it. - // - If the user requested bitcode but codegen_units == 1, then we - // can toss .#module-name#.bc because we copied it to .bc earlier. - // - If we're not building an rlib and the user didn't request - // bitcode, then delete .#module-name#.bc. - // If you change how this works, also update back::link::link_rlib, - // where .#module-name#.bc files are (maybe) deleted after making an - // rlib. - let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe); - - let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units().as_usize() > 1; - - let keep_numbered_objects = - needs_crate_object || (user_wants_objects && sess.codegen_units().as_usize() > 1); - - for module in codegen_results.modules.iter() { - if let Some(ref path) = module.object { - if !keep_numbered_objects { - ensure_removed(sess.dcx(), path); - } - } - - if let Some(ref path) = module.dwarf_object { - if !keep_numbered_objects { - ensure_removed(sess.dcx(), path); - } - } - - if let Some(ref path) = module.bytecode { - if !keep_numbered_bitcode { - ensure_removed(sess.dcx(), path); - } - } - } - - if !user_wants_bitcode { - if let Some(ref allocator_module) = codegen_results.allocator_module { - if let Some(ref path) = allocator_module.bytecode { - ensure_removed(sess.dcx(), path); - } - } - } - } - - if sess.opts.json_artifact_notifications { - if codegen_results.modules.len() == 1 { - codegen_results.modules[0].for_each_output(|_path, ty| { - if sess.opts.output_types.contains_key(&ty) { - let descr = ty.shorthand(); - // for single cgu file is renamed to drop cgu specific suffix - // so we regenerate it the same way - let path = crate_output.path(ty); - sess.dcx().emit_artifact_notification(path.as_path(), descr); - } - }); - } else { - for module in &codegen_results.modules { - module.for_each_output(|path, ty| { - if sess.opts.output_types.contains_key(&ty) { - let descr = ty.shorthand(); - sess.dcx().emit_artifact_notification(path, descr); - } - }); - } - } - } - - // We leave the following files around by default: - // - #crate#.o - // - #crate#.crate.metadata.o - // - #crate#.bc - // These are used in linking steps and will be cleaned up afterward. -} - fn make_module(sess: &Session, name: String) -> UnwindModule { let isa = crate::build_isa(sess, false); diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index a49dc9be3458..e7daf57de0f0 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -23,7 +23,6 @@ extern crate rustc_hir; extern crate rustc_incremental; extern crate rustc_index; extern crate rustc_log; -extern crate rustc_metadata; extern crate rustc_session; extern crate rustc_span; extern crate rustc_symbol_mangling; diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index c93a2e8f8da5..9a9040708ef8 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -30,6 +30,7 @@ use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter} use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::memmap::Mmap; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_log::tracing::info; use rustc_middle::bug; @@ -50,7 +51,7 @@ struct LtoData { } fn prepare_lto( - cgcx: &CodegenContext, + cgcx: &CodegenContext, each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>, ) -> LtoData { @@ -111,7 +112,8 @@ fn save_as_file(obj: &[u8], path: &Path) -> Result<(), LtoBitcodeFromRlib> { /// Performs fat LTO by merging all modules into a single one and returning it /// for further optimization. pub(crate) fn run_fat( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, @@ -123,6 +125,7 @@ pub(crate) fn run_fat( lto_data.symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>();*/ fat_lto( cgcx, + prof, dcx, modules, lto_data.upstream_modules, @@ -132,14 +135,15 @@ pub(crate) fn run_fat( } fn fat_lto( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, _dcx: DiagCtxtHandle<'_>, modules: Vec>, mut serialized_modules: Vec<(SerializedModule, CString)>, tmp_path: TempDir, //symbols_below_threshold: &[String], ) -> ModuleCodegen { - let _timer = cgcx.prof.generic_activity("GCC_fat_lto_build_monolithic_module"); + let _timer = prof.generic_activity("GCC_fat_lto_build_monolithic_module"); info!("going for a fat lto"); // Sort out all our lists of incoming modules into two lists. @@ -223,8 +227,7 @@ fn fat_lto( // We add the object files and save in should_combine_object_files that we should combine // them into a single object file when compiling later. for (bc_decoded, name) in serialized_modules { - let _timer = cgcx - .prof + let _timer = prof .generic_activity_with_arg_recorder("GCC_fat_lto_link_module", |recorder| { recorder.record_arg(format!("{:?}", name)) }); @@ -283,7 +286,8 @@ impl ModuleBufferMethods for ModuleBuffer { /// lists, one of the modules that need optimization and another for modules that /// can simply be copied over from the incr. comp. cache. pub(crate) fn run_thin( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, ThinBuffer)>, @@ -298,6 +302,7 @@ pub(crate) fn run_thin( } thin_lto( cgcx, + prof, dcx, modules, lto_data.upstream_modules, @@ -345,7 +350,8 @@ pub(crate) fn prepare_thin(module: ModuleCodegen) -> (String, ThinBu /// all of the `LtoModuleCodegen` units returned below and destroyed once /// they all go out of scope. fn thin_lto( - cgcx: &CodegenContext, + _cgcx: &CodegenContext, + prof: &SelfProfilerRef, _dcx: DiagCtxtHandle<'_>, modules: Vec<(String, ThinBuffer)>, serialized_modules: Vec<(SerializedModule, CString)>, @@ -353,7 +359,7 @@ fn thin_lto( cached_modules: Vec<(SerializedModule, WorkProduct)>, //_symbols_below_threshold: &[String], ) -> (Vec>, Vec) { - let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis"); + let _timer = prof.generic_activity("LLVM_thin_lto_global_analysis"); info!("going for that thin, thin LTO"); /*let green_modules: FxHashMap<_, _> = @@ -520,11 +526,9 @@ fn thin_lto( pub fn optimize_thin_module( thin_module: ThinModule, - _cgcx: &CodegenContext, + _cgcx: &CodegenContext, ) -> ModuleCodegen { //let module_name = &thin_module.shared.module_names[thin_module.idx]; - /*let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap()); - let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&dcx, e))?;*/ // Right now the implementation we've got only works over serialized // modules, so we create a fresh new LLVM context and parse the module diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index b6223c5be370..ddf13558027b 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -6,6 +6,7 @@ use rustc_codegen_ssa::back::write::{ BitcodeSection, CodegenContext, EmitObj, ModuleConfig, SharedEmitter, }; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::DiagCtxt; use rustc_fs_util::link_or_copy; use rustc_log::tracing::debug; @@ -14,10 +15,11 @@ use rustc_target::spec::SplitDebuginfo; use crate::base::add_pic_option; use crate::errors::CopyBitcode; -use crate::{GccCodegenBackend, GccContext, LtoMode}; +use crate::{GccContext, LtoMode}; pub(crate) fn codegen( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, module: ModuleCodegen, config: &ModuleConfig, @@ -25,7 +27,7 @@ pub(crate) fn codegen( let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); - let _timer = cgcx.prof.generic_activity_with_arg("GCC_module_codegen", &*module.name); + let _timer = prof.generic_activity_with_arg("GCC_module_codegen", &*module.name); { let context = &module.module_llvm.context; @@ -44,9 +46,8 @@ pub(crate) fn codegen( ); if config.bitcode_needed() { - let _timer = cgcx - .prof - .generic_activity_with_arg("GCC_module_codegen_make_bitcode", &*module.name); + let _timer = + prof.generic_activity_with_arg("GCC_module_codegen_make_bitcode", &*module.name); // TODO(antoyo) /*if let Some(bitcode_filename) = bc_out.file_name() { @@ -58,8 +59,7 @@ pub(crate) fn codegen( }*/ if config.emit_bc || config.emit_obj == EmitObj::Bitcode { - let _timer = cgcx - .prof + let _timer = prof .generic_activity_with_arg("GCC_module_codegen_emit_bitcode", &*module.name); if lto_supported { context.add_command_line_option("-flto=auto"); @@ -70,8 +70,7 @@ pub(crate) fn codegen( } if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) { - let _timer = cgcx - .prof + let _timer = prof .generic_activity_with_arg("GCC_module_codegen_embed_bitcode", &*module.name); if lto_supported { // TODO(antoyo): maybe we should call embed_bitcode to have the proper iOS fixes? @@ -98,7 +97,7 @@ pub(crate) fn codegen( if config.emit_asm { let _timer = - cgcx.prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name); + prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name); let path = cgcx.output_filenames.temp_path_for_cgu( OutputType::Assembly, &module.name, @@ -109,9 +108,8 @@ pub(crate) fn codegen( match config.emit_obj { EmitObj::ObjectCode(_) => { - let _timer = cgcx - .prof - .generic_activity_with_arg("GCC_module_codegen_emit_obj", &*module.name); + let _timer = + prof.generic_activity_with_arg("GCC_module_codegen_emit_obj", &*module.name); if env::var("CG_GCCJIT_DUMP_MODULE_NAMES").as_deref() == Ok("1") { println!("Module {}", module.name); } @@ -227,7 +225,7 @@ pub(crate) fn codegen( } pub(crate) fn save_temp_bitcode( - cgcx: &CodegenContext, + cgcx: &CodegenContext, _module: &ModuleCodegen, _name: &str, ) { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 553e4d3d2fe0..68939c9476d6 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -22,13 +22,18 @@ use rustc_codegen_ssa::traits::{ ArgAbiBuilderMethods, BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, IntrinsicCallBuilderMethods, LayoutTypeCodegenMethods, }; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; -use rustc_middle::ty::layout::{FnAbiOf, LayoutOf}; +#[cfg(feature = "master")] +use rustc_middle::ty::layout::FnAbiOf; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{Span, Symbol, sym}; use rustc_target::callconv::{ArgAbi, PassMode}; -use crate::abi::{FnAbiGccExt, GccType}; +#[cfg(feature = "master")] +use crate::abi::FnAbiGccExt; +use crate::abi::GccType; use crate::builder::Builder; use crate::common::{SignType, TypeReflection}; use crate::context::CodegenCx; @@ -617,8 +622,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc *func } else { self.linkage.set(FunctionType::Extern); - let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); - let fn_ty = fn_abi.gcc_type(self); let func = match sym { "llvm.fma.f16" => { @@ -631,13 +634,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc self.intrinsics.borrow_mut().insert(sym.to_string(), func); - self.on_stack_function_params - .borrow_mut() - .insert(func, fn_ty.on_stack_param_indices); - #[cfg(feature = "master")] - for fn_attr in fn_ty.fn_attributes { - func.add_attribute(fn_attr); - } + self.on_stack_function_params.borrow_mut().insert(func, FxHashSet::default()); crate::attributes::from_fn_attrs(self, func, instance); diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index cc88fd02435e..24a065d69eca 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -90,6 +90,7 @@ use rustc_codegen_ssa::target_features::cfg_target_feature; use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods}; use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig}; use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::IntoDynSyncSend; use rustc_errors::DiagCtxtHandle; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; @@ -374,7 +375,7 @@ impl ExtraBackendMethods for GccCodegenBackend { _features: &[String], ) -> TargetMachineFactoryFn { // TODO(antoyo): set opt level. - Arc::new(|_| Ok(())) + Arc::new(|_, _| ()) } } @@ -421,24 +422,26 @@ unsafe impl Sync for SyncContext {} impl WriteBackendMethods for GccCodegenBackend { type Module = GccContext; type TargetMachine = (); - type TargetMachineError = (); type ModuleBuffer = ModuleBuffer; type ThinData = ThinData; type ThinBuffer = ThinBuffer; fn run_and_optimize_fat_lto( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, + _tm_factory: TargetMachineFactoryFn, // FIXME(bjorn3): Limit LTO exports to these symbols _exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, ) -> ModuleCodegen { - back::lto::run_fat(cgcx, shared_emitter, each_linked_rlib_for_lto, modules) + back::lto::run_fat(cgcx, prof, shared_emitter, each_linked_rlib_for_lto, modules) } fn run_thin_lto( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, // FIXME(bjorn3): Limit LTO exports to these symbols _exported_symbols_for_lto: &[String], @@ -446,7 +449,7 @@ impl WriteBackendMethods for GccCodegenBackend { modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec) { - back::lto::run_thin(cgcx, dcx, each_linked_rlib_for_lto, modules, cached_modules) + back::lto::run_thin(cgcx, prof, dcx, each_linked_rlib_for_lto, modules, cached_modules) } fn print_pass_timings(&self) { @@ -458,7 +461,8 @@ impl WriteBackendMethods for GccCodegenBackend { } fn optimize( - _cgcx: &CodegenContext, + _cgcx: &CodegenContext, + _prof: &SelfProfilerRef, _shared_emitter: &SharedEmitter, module: &mut ModuleCodegen, config: &ModuleConfig, @@ -467,20 +471,23 @@ impl WriteBackendMethods for GccCodegenBackend { } fn optimize_thin( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + _prof: &SelfProfilerRef, _shared_emitter: &SharedEmitter, + _tm_factory: TargetMachineFactoryFn, thin: ThinModule, ) -> ModuleCodegen { back::lto::optimize_thin_module(thin, cgcx) } fn codegen( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule { - back::write::codegen(cgcx, shared_emitter, module, config) + back::write::codegen(cgcx, prof, shared_emitter, module, config) } fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer) { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index f5eb9c10db99..b5ab26aea492 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -301,17 +301,6 @@ fn stackprotector_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll A Some(sspattr.create_attr(cx.llcx)) } -fn backchain_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> { - if sess.target.arch != Arch::S390x { - return None; - } - - let requested_features = sess.opts.cg.target_feature.split(','); - let found_positive = requested_features.clone().any(|r| r == "+backchain"); - - if found_positive { Some(llvm::CreateAttrString(cx.llcx, "backchain")) } else { None } -} - pub(crate) fn target_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> &'ll Attribute { let target_cpu = llvm_util::target_cpu(sess); llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu) @@ -530,9 +519,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( if let Some(align) = codegen_fn_attrs.alignment { llvm::set_alignment(llfn, align); } - if let Some(backchain) = backchain_attr(cx, sess) { - to_add.push(backchain); - } to_add.extend(patchable_function_entry_attrs( cx, sess, diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 71327ed6d2d1..5d272d10930b 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -9,11 +9,14 @@ use std::{io, iter, slice}; use object::read::archive::ArchiveFile; use object::{Object, ObjectSection}; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; -use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter}; +use rustc_codegen_ssa::back::write::{ + CodegenContext, FatLtoInput, SharedEmitter, TargetMachineFactoryFn, +}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::memmap::Mmap; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_hir::attrs::SanitizerSet; use rustc_middle::bug; @@ -33,7 +36,7 @@ use crate::{LlvmCodegenBackend, ModuleLlvm}; const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin"; fn prepare_lto( - cgcx: &CodegenContext, + cgcx: &CodegenContext, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>, @@ -123,7 +126,7 @@ fn prepare_lto( fn get_bitcode_slice_from_object_data<'a>( obj: &'a [u8], - cgcx: &CodegenContext, + cgcx: &CodegenContext, ) -> Result<&'a [u8], LtoBitcodeFromRlib> { // We're about to assume the data here is an object file with sections, but if it's raw LLVM IR // that won't work. Fortunately, if that's what we have we can just return the object directly, @@ -149,8 +152,10 @@ fn get_bitcode_slice_from_object_data<'a>( /// Performs fat LTO by merging all modules into a single one and returning it /// for further optimization. pub(crate) fn run_fat( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, + tm_factory: TargetMachineFactoryFn, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, @@ -161,14 +166,24 @@ pub(crate) fn run_fat( prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx); let symbols_below_threshold = symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>(); - fat_lto(cgcx, dcx, shared_emitter, modules, upstream_modules, &symbols_below_threshold) + fat_lto( + cgcx, + prof, + dcx, + shared_emitter, + tm_factory, + modules, + upstream_modules, + &symbols_below_threshold, + ) } /// Performs thin LTO by performing necessary global analysis and returning two /// lists, one of the modules that need optimization and another for modules that /// can simply be copied over from the incr. comp. cache. pub(crate) fn run_thin( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], @@ -185,7 +200,7 @@ pub(crate) fn run_thin( is deferred to the linker" ); } - thin_lto(cgcx, dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold) + thin_lto(cgcx, prof, dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold) } pub(crate) fn prepare_thin(module: ModuleCodegen) -> (String, ThinBuffer) { @@ -195,14 +210,16 @@ pub(crate) fn prepare_thin(module: ModuleCodegen) -> (String, ThinBu } fn fat_lto( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, shared_emitter: &SharedEmitter, + tm_factory: TargetMachineFactoryFn, modules: Vec>, mut serialized_modules: Vec<(SerializedModule, CString)>, symbols_below_threshold: &[*const libc::c_char], ) -> ModuleCodegen { - let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_build_monolithic_module"); + let _timer = prof.generic_activity("LLVM_fat_lto_build_monolithic_module"); info!("going for a fat lto"); // Sort out all our lists of incoming modules into two lists. @@ -252,7 +269,7 @@ fn fat_lto( assert!(!serialized_modules.is_empty(), "must have at least one serialized module"); let (buffer, name) = serialized_modules.remove(0); info!("no in-memory regular modules to choose from, parsing {:?}", name); - let llvm_module = ModuleLlvm::parse(cgcx, &name, buffer.data(), dcx); + let llvm_module = ModuleLlvm::parse(cgcx, tm_factory, &name, buffer.data(), dcx); ModuleCodegen::new_regular(name.into_string().unwrap(), llvm_module) } }; @@ -291,8 +308,7 @@ fn fat_lto( // above, this is all mostly handled in C++. let mut linker = Linker::new(llmod); for (bc_decoded, name) in serialized_modules { - let _timer = cgcx - .prof + let _timer = prof .generic_activity_with_arg_recorder("LLVM_fat_lto_link_module", |recorder| { recorder.record_arg(format!("{name:?}")) }); @@ -381,14 +397,15 @@ impl Drop for Linker<'_> { /// all of the `LtoModuleCodegen` units returned below and destroyed once /// they all go out of scope. fn thin_lto( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, modules: Vec<(String, ThinBuffer)>, serialized_modules: Vec<(SerializedModule, CString)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, symbols_below_threshold: &[*const libc::c_char], ) -> (Vec>, Vec) { - let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis"); + let _timer = prof.generic_activity("LLVM_thin_lto_global_analysis"); unsafe { info!("going for that thin, thin LTO"); @@ -585,12 +602,13 @@ pub(crate) fn enable_autodiff_settings(ad: &[config::AutoDiff]) { } pub(crate) fn run_pass_manager( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, module: &mut ModuleCodegen, thin: bool, ) { - let _timer = cgcx.prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name); + let _timer = prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name); let config = &cgcx.module_config; // Now we have one massive module inside of llmod. Time to run the @@ -616,7 +634,7 @@ pub(crate) fn run_pass_manager( }; unsafe { - write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage); + write::llvm_optimize(cgcx, prof, dcx, module, None, config, opt_level, opt_stage, stage); } if cfg!(feature = "llvm_enzyme") && enable_ad && !thin { @@ -624,7 +642,9 @@ pub(crate) fn run_pass_manager( let stage = write::AutodiffStage::PostAD; if !config.autodiff.contains(&config::AutoDiff::NoPostopt) { unsafe { - write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage); + write::llvm_optimize( + cgcx, prof, dcx, module, None, config, opt_level, opt_stage, stage, + ); } } @@ -726,8 +746,10 @@ impl Drop for ThinBuffer { } pub(crate) fn optimize_thin_module( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, + tm_factory: TargetMachineFactoryFn, thin_module: ThinModule, ) -> ModuleCodegen { let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); @@ -740,7 +762,7 @@ pub(crate) fn optimize_thin_module( // into that context. One day, however, we may do this for upstream // crates but for locally codegened modules we may be able to reuse // that LLVM Context and Module. - let module_llvm = ModuleLlvm::parse(cgcx, module_name, thin_module.data(), dcx); + let module_llvm = ModuleLlvm::parse(cgcx, tm_factory, module_name, thin_module.data(), dcx); let mut module = ModuleCodegen::new_regular(thin_module.name(), module_llvm); // Given that the newly created module lacks a thinlto buffer for embedding, we need to re-add it here. if cgcx.module_config.embed_bitcode() { @@ -760,8 +782,7 @@ pub(crate) fn optimize_thin_module( // You can find some more comments about these functions in the LLVM // bindings we've got (currently `PassWrapper.cpp`) { - let _timer = - cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name()); + let _timer = prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name()); unsafe { llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target.raw()) }; @@ -769,9 +790,8 @@ pub(crate) fn optimize_thin_module( } { - let _timer = cgcx - .prof - .generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name()); + let _timer = + prof.generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name()); if unsafe { !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) } { write::llvm_err(dcx, LlvmError::PrepareThinLtoModule); @@ -780,9 +800,8 @@ pub(crate) fn optimize_thin_module( } { - let _timer = cgcx - .prof - .generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name()); + let _timer = + prof.generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name()); if unsafe { !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) } { write::llvm_err(dcx, LlvmError::PrepareThinLtoModule); @@ -791,8 +810,7 @@ pub(crate) fn optimize_thin_module( } { - let _timer = - cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name()); + let _timer = prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name()); if unsafe { !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target.raw()) } { @@ -808,7 +826,7 @@ pub(crate) fn optimize_thin_module( // little differently. { info!("running thin lto passes over {}", module.name); - run_pass_manager(cgcx, dcx, &mut module, true); + run_pass_manager(cgcx, prof, dcx, &mut module, true); save_temp_bitcode(cgcx, &module, "thin-lto-after-pm"); } } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index fb07794e1bba..3e3ccd39e674 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -38,8 +38,8 @@ use crate::builder::SBuilder; use crate::builder::gpu_offload::scalar_width; use crate::common::AsCCharPtr; use crate::errors::{ - CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, UnknownCompression, - WithLlvmError, WriteBytecode, + CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, ParseTargetMachineConfig, + UnknownCompression, WithLlvmError, WriteBytecode, }; use crate::llvm::diagnostic::OptimizationDiagnosticKind::*; use crate::llvm::{self, DiagnosticInfo}; @@ -111,8 +111,7 @@ pub(crate) fn create_informational_target_machine( // Can't use query system here quite yet because this function is invoked before the query // system/tcx is set up. let features = llvm_util::global_llvm_features(sess, only_base_features); - target_machine_factory(sess, config::OptLevel::No, &features)(config) - .unwrap_or_else(|err| llvm_err(sess.dcx(), err)) + target_machine_factory(sess, config::OptLevel::No, &features)(sess.dcx(), config) } pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTargetMachine { @@ -138,8 +137,7 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar tcx.sess, tcx.backend_optimization_level(()), tcx.global_backend_features(()), - )(config) - .unwrap_or_else(|err| llvm_err(tcx.dcx(), err)) + )(tcx.dcx(), config) } fn to_llvm_opt_settings(cfg: config::OptLevel) -> (llvm::CodeGenOptLevel, llvm::CodeGenOptSize) { @@ -278,7 +276,7 @@ pub(crate) fn target_machine_factory( let large_data_threshold = sess.opts.unstable_opts.large_data_threshold.unwrap_or(0); let prof = SelfProfilerRef::clone(&sess.prof); - Arc::new(move |config: TargetMachineFactoryConfig| { + Arc::new(move |dcx: DiagCtxtHandle<'_>, config: TargetMachineFactoryConfig| { // Self-profile timer for invoking a factory to create a target machine. let _prof_timer = prof.generic_activity("target_machine_factory_inner"); @@ -320,11 +318,12 @@ pub(crate) fn target_machine_factory( use_wasm_eh, large_data_threshold, ) + .unwrap_or_else(|err| dcx.emit_fatal(ParseTargetMachineConfig(err))) }) } pub(crate) fn save_temp_bitcode( - cgcx: &CodegenContext, + cgcx: &CodegenContext, module: &ModuleCodegen, name: &str, ) { @@ -359,14 +358,14 @@ pub(crate) enum CodegenDiagnosticsStage { } pub(crate) struct DiagnosticHandlers<'a> { - data: *mut (&'a CodegenContext, &'a SharedEmitter), + data: *mut (&'a CodegenContext, &'a SharedEmitter), llcx: &'a llvm::Context, old_handler: Option<&'a llvm::DiagnosticHandler>, } impl<'a> DiagnosticHandlers<'a> { pub(crate) fn new( - cgcx: &'a CodegenContext, + cgcx: &'a CodegenContext, shared_emitter: &'a SharedEmitter, llcx: &'a llvm::Context, module: &ModuleCodegen, @@ -432,7 +431,7 @@ impl<'a> Drop for DiagnosticHandlers<'a> { } fn report_inline_asm( - cgcx: &CodegenContext, + cgcx: &CodegenContext, msg: String, level: llvm::DiagnosticLevel, cookie: u64, @@ -464,8 +463,7 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void if user.is_null() { return; } - let (cgcx, shared_emitter) = - unsafe { *(user as *const (&CodegenContext, &SharedEmitter)) }; + let (cgcx, shared_emitter) = unsafe { *(user as *const (&CodegenContext, &SharedEmitter)) }; let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); @@ -561,7 +559,8 @@ pub(crate) enum AutodiffStage { } pub(crate) unsafe fn llvm_optimize( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, module: &ModuleCodegen, thin_lto_buffer: Option<&mut *mut llvm::ThinLTOBuffer>, @@ -758,10 +757,9 @@ pub(crate) unsafe fn llvm_optimize( } } - let mut llvm_profiler = cgcx - .prof + let mut llvm_profiler = prof .llvm_recording_enabled() - .then(|| LlvmSelfProfiler::new(cgcx.prof.get_self_profiler().unwrap())); + .then(|| LlvmSelfProfiler::new(prof.get_self_profiler().unwrap())); let llvm_selfprofiler = llvm_profiler.as_mut().map(|s| s as *mut _ as *mut c_void).unwrap_or(std::ptr::null_mut()); @@ -880,7 +878,7 @@ pub(crate) unsafe fn llvm_optimize( &out_obj, None, llvm::FileType::ObjectFile, - &cgcx.prof, + prof, true, ); // We ignore cgcx.save_temps here and unconditionally always keep our `host.out` artifact. @@ -893,12 +891,13 @@ pub(crate) unsafe fn llvm_optimize( // Unsafe due to LLVM calls. pub(crate) fn optimize( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, module: &mut ModuleCodegen, config: &ModuleConfig, ) { - let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_optimize", &*module.name); + let _timer = prof.generic_activity_with_arg("LLVM_module_optimize", &*module.name); let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); @@ -945,6 +944,7 @@ pub(crate) fn optimize( unsafe { llvm_optimize( cgcx, + prof, dcx, module, thin_lto_buffer.as_mut(), @@ -966,12 +966,12 @@ pub(crate) fn optimize( && let Some(thin_link_bitcode_filename) = bc_summary_out.file_name() { let summary_data = thin_lto_buffer.thin_link_data(); - cgcx.prof.artifact_size( + prof.artifact_size( "llvm_bitcode_summary", thin_link_bitcode_filename.to_string_lossy(), summary_data.len() as u64, ); - let _timer = cgcx.prof.generic_activity_with_arg( + let _timer = prof.generic_activity_with_arg( "LLVM_module_codegen_emit_bitcode_summary", &*module.name, ); @@ -984,12 +984,13 @@ pub(crate) fn optimize( } pub(crate) fn codegen( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule { - let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name); + let _timer = prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name); let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); @@ -1028,18 +1029,17 @@ pub(crate) fn codegen( if config.bitcode_needed() { if config.emit_bc || config.emit_obj == EmitObj::Bitcode { let thin = { - let _timer = cgcx.prof.generic_activity_with_arg( + let _timer = prof.generic_activity_with_arg( "LLVM_module_codegen_make_bitcode", &*module.name, ); ThinBuffer::new(llmod, config.emit_thin_lto) }; let data = thin.data(); - let _timer = cgcx - .prof + let _timer = prof .generic_activity_with_arg("LLVM_module_codegen_emit_bitcode", &*module.name); if let Some(bitcode_filename) = bc_out.file_name() { - cgcx.prof.artifact_size( + prof.artifact_size( "llvm_bitcode", bitcode_filename.to_string_lossy(), data.len() as u64, @@ -1051,8 +1051,7 @@ pub(crate) fn codegen( } if config.embed_bitcode() && module.kind == ModuleKind::Regular { - let _timer = cgcx - .prof + let _timer = prof .generic_activity_with_arg("LLVM_module_codegen_embed_bitcode", &*module.name); let thin_bc = module.thin_lto_buffer.as_deref().expect("cannot find embedded bitcode"); @@ -1062,7 +1061,7 @@ pub(crate) fn codegen( if config.emit_ir { let _timer = - cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name); + prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name); let out = cgcx.output_filenames.temp_path_for_cgu( OutputType::LlvmAssembly, &module.name, @@ -1100,7 +1099,7 @@ pub(crate) fn codegen( unsafe { llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback) }; if result == llvm::LLVMRustResult::Success { - record_artifact_size(&cgcx.prof, "llvm_ir", &out); + record_artifact_size(prof, "llvm_ir", &out); } result @@ -1110,7 +1109,7 @@ pub(crate) fn codegen( if config.emit_asm { let _timer = - cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name); + prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name); let path = cgcx.output_filenames.temp_path_for_cgu( OutputType::Assembly, &module.name, @@ -1134,16 +1133,15 @@ pub(crate) fn codegen( &path, None, llvm::FileType::AssemblyFile, - &cgcx.prof, + prof, config.verify_llvm_ir, ); } match config.emit_obj { EmitObj::ObjectCode(_) => { - let _timer = cgcx - .prof - .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name); + let _timer = + prof.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name); let dwo_out = cgcx .output_filenames @@ -1170,7 +1168,7 @@ pub(crate) fn codegen( &obj_out, dwo_out, llvm::FileType::ObjectFile, - &cgcx.prof, + prof, config.verify_llvm_ir, ); } @@ -1190,7 +1188,7 @@ pub(crate) fn codegen( EmitObj::None => {} } - record_llvm_cgu_instructions_stats(&cgcx.prof, &module.name, llmod); + record_llvm_cgu_instructions_stats(prof, &module.name, llmod); } // `.dwo` files are only emitted if: @@ -1239,7 +1237,7 @@ fn create_section_with_flags_asm(section_name: &str, section_flags: &str, data: asm } -pub(crate) fn bitcode_section_name(cgcx: &CodegenContext) -> &'static CStr { +pub(crate) fn bitcode_section_name(cgcx: &CodegenContext) -> &'static CStr { if cgcx.target_is_like_darwin { c"__LLVM,__bitcode" } else if cgcx.target_is_like_aix { @@ -1251,7 +1249,7 @@ pub(crate) fn bitcode_section_name(cgcx: &CodegenContext) -> /// Embed the bitcode of an LLVM module for LTO in the LLVM module itself. fn embed_bitcode( - cgcx: &CodegenContext, + cgcx: &CodegenContext, llcx: &llvm::Context, llmod: &llvm::Module, bitcode: &[u8], @@ -1335,11 +1333,7 @@ fn embed_bitcode( // when using MSVC linker. We do this only for data, as linker can fix up // code references on its own. // See #26591, #27438 -fn create_msvc_imps( - cgcx: &CodegenContext, - llcx: &llvm::Context, - llmod: &llvm::Module, -) { +fn create_msvc_imps(cgcx: &CodegenContext, llcx: &llvm::Context, llmod: &llvm::Module) { if !cgcx.msvc_imps_needed { return; } diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index 16ab18a568e8..a60e2d1f0ab0 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -1,12 +1,13 @@ use std::ffi::CString; +use bitflags::Flags; use llvm::Linkage::*; use rustc_abi::Align; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; use rustc_middle::bug; -use rustc_middle::ty::offload_meta::OffloadMetadata; +use rustc_middle::ty::offload_meta::{MappingFlags, OffloadMetadata}; use crate::builder::Builder; use crate::common::CodegenCx; @@ -28,10 +29,6 @@ pub(crate) struct OffloadGlobals<'ll> { pub mapper_fn_ty: &'ll llvm::Type, pub ident_t_global: &'ll llvm::Value, - - // FIXME(offload): Drop this, once we fully automated our offload compilation pipeline, since - // LLVM will initialize them for us if it sees gpu kernels being registered. - pub init_rtls: &'ll llvm::Value, } impl<'ll> OffloadGlobals<'ll> { @@ -42,9 +39,6 @@ impl<'ll> OffloadGlobals<'ll> { let (begin_mapper, _, end_mapper, mapper_fn_ty) = gen_tgt_data_mappers(cx); let ident_t_global = generate_at_one(cx); - let init_ty = cx.type_func(&[], cx.type_void()); - let init_rtls = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty); - // We want LLVM's openmp-opt pass to pick up and optimize this module, since it covers both // openmp and offload optimizations. llvm::add_module_flag_u32(cx.llmod(), llvm::ModuleFlagMergeBehavior::Max, "openmp", 51); @@ -58,7 +52,6 @@ impl<'ll> OffloadGlobals<'ll> { end_mapper, mapper_fn_ty, ident_t_global, - init_rtls, } } } @@ -91,6 +84,11 @@ pub(crate) fn register_offload<'ll>(cx: &CodegenCx<'ll, '_>) { let atexit = cx.type_func(&[cx.type_ptr()], cx.type_i32()); let atexit_fn = declare_offload_fn(cx, "atexit", atexit); + // FIXME(offload): Drop this, once we fully automated our offload compilation pipeline, since + // LLVM will initialize them for us if it sees gpu kernels being registered. + let init_ty = cx.type_func(&[], cx.type_void()); + let init_rtls = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty); + let desc_ty = cx.type_func(&[], cx.type_void()); let reg_name = ".omp_offloading.descriptor_reg"; let unreg_name = ".omp_offloading.descriptor_unreg"; @@ -104,12 +102,14 @@ pub(crate) fn register_offload<'ll>(cx: &CodegenCx<'ll, '_>) { // define internal void @.omp_offloading.descriptor_reg() section ".text.startup" { // entry: // call void @__tgt_register_lib(ptr @.omp_offloading.descriptor) + // call void @__tgt_init_all_rtls() // %0 = call i32 @atexit(ptr @.omp_offloading.descriptor_unreg) // ret void // } let bb = Builder::append_block(cx, desc_reg_fn, "entry"); let mut a = Builder::build(cx, bb); a.call(reg_lib_decl, None, None, register_lib, &[omp_descriptor], None, None); + a.call(init_ty, None, None, init_rtls, &[], None, None); a.call(atexit, None, None, atexit_fn, &[desc_unreg_fn], None, None); a.ret_void(); @@ -345,7 +345,9 @@ impl KernelArgsTy { #[derive(Copy, Clone)] pub(crate) struct OffloadKernelGlobals<'ll> { pub offload_sizes: &'ll llvm::Value, - pub memtransfer_types: &'ll llvm::Value, + pub memtransfer_begin: &'ll llvm::Value, + pub memtransfer_kernel: &'ll llvm::Value, + pub memtransfer_end: &'ll llvm::Value, pub region_id: &'ll llvm::Value, } @@ -423,18 +425,38 @@ pub(crate) fn gen_define_handling<'ll>( let offload_entry_ty = offload_globals.offload_entry_ty; - // FIXME(Sa4dUs): add `OMP_MAP_TARGET_PARAM = 0x20` only if necessary let (sizes, transfer): (Vec<_>, Vec<_>) = - metadata.iter().map(|m| (m.payload_size, m.mode.bits() | 0x20)).unzip(); + metadata.iter().map(|m| (m.payload_size, m.mode)).unzip(); + // Our begin mapper should only see simplified information about which args have to be + // transferred to the device, the end mapper only about which args should be transferred back. + // Any information beyond that makes it harder for LLVM's opt pass to evaluate whether it can + // safely move (=optimize) the LLVM-IR location of this data transfer. Only the mapping types + // mentioned below are handled, so make sure that we don't generate any other ones. + let handled_mappings = MappingFlags::TO + | MappingFlags::FROM + | MappingFlags::TARGET_PARAM + | MappingFlags::LITERAL + | MappingFlags::IMPLICIT; + for arg in &transfer { + debug_assert!(!arg.contains_unknown_bits()); + debug_assert!(handled_mappings.contains(*arg)); + } + + let valid_begin_mappings = MappingFlags::TO | MappingFlags::LITERAL | MappingFlags::IMPLICIT; + let transfer_to: Vec = + transfer.iter().map(|m| m.intersection(valid_begin_mappings).bits()).collect(); + let transfer_from: Vec = + transfer.iter().map(|m| m.intersection(MappingFlags::FROM).bits()).collect(); + // FIXME(offload): add `OMP_MAP_TARGET_PARAM = 0x20` only if necessary + let transfer_kernel = vec![MappingFlags::TARGET_PARAM.bits(); transfer_to.len()]; let offload_sizes = add_priv_unnamed_arr(&cx, &format!(".offload_sizes.{symbol}"), &sizes); - // Here we figure out whether something needs to be copied to the gpu (=1), from the gpu (=2), - // or both to and from the gpu (=3). Other values shouldn't affect us for now. - // A non-mutable reference or pointer will be 1, an array that's not read, but fully overwritten - // will be 2. For now, everything is 3, until we have our frontend set up. - // 1+2+32: 1 (MapTo), 2 (MapFrom), 32 (Add one extra input ptr per function, to be used later). - let memtransfer_types = - add_priv_unnamed_arr(&cx, &format!(".offload_maptypes.{symbol}"), &transfer); + let memtransfer_begin = + add_priv_unnamed_arr(&cx, &format!(".offload_maptypes.{symbol}.begin"), &transfer_to); + let memtransfer_kernel = + add_priv_unnamed_arr(&cx, &format!(".offload_maptypes.{symbol}.kernel"), &transfer_kernel); + let memtransfer_end = + add_priv_unnamed_arr(&cx, &format!(".offload_maptypes.{symbol}.end"), &transfer_from); // Next: For each function, generate these three entries. A weak constant, // the llvm.rodata entry name, and the llvm_offload_entries value @@ -469,7 +491,13 @@ pub(crate) fn gen_define_handling<'ll>( cx.add_compiler_used_global(offload_entry); - let result = OffloadKernelGlobals { offload_sizes, memtransfer_types, region_id }; + let result = OffloadKernelGlobals { + offload_sizes, + memtransfer_begin, + memtransfer_kernel, + memtransfer_end, + region_id, + }; // FIXME(Sa4dUs): use this global for constant offload sizes cx.add_compiler_used_global(result.offload_sizes); @@ -535,7 +563,13 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( offload_dims: &OffloadKernelDims<'ll>, ) { let cx = builder.cx; - let OffloadKernelGlobals { memtransfer_types, region_id, .. } = offload_data; + let OffloadKernelGlobals { + memtransfer_begin, + memtransfer_kernel, + memtransfer_end, + region_id, + .. + } = offload_data; let OffloadKernelDims { num_workgroups, threads_per_block, workgroup_dims, thread_dims } = offload_dims; @@ -608,12 +642,6 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( geps.push(gep); } - let init_ty = cx.type_func(&[], cx.type_void()); - let init_rtls_decl = offload_globals.init_rtls; - - // call void @__tgt_init_all_rtls() - builder.call(init_ty, None, None, init_rtls_decl, &[], None, None); - for i in 0..num_args { let idx = cx.get_const_i32(i); let gep1 = builder.inbounds_gep(ty, a1, &[i32_0, idx]); @@ -668,14 +696,14 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( generate_mapper_call( builder, geps, - memtransfer_types, + memtransfer_begin, begin_mapper_decl, fn_ty, num_args, s_ident_t, ); let values = - KernelArgsTy::new(&cx, num_args, memtransfer_types, geps, workgroup_dims, thread_dims); + KernelArgsTy::new(&cx, num_args, memtransfer_kernel, geps, workgroup_dims, thread_dims); // Step 3) // Here we fill the KernelArgsTy, see the documentation above @@ -701,7 +729,7 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( generate_mapper_call( builder, geps, - memtransfer_types, + memtransfer_end, end_mapper_decl, fn_ty, num_args, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 1c674f0e8a85..c759d46b7d2f 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -479,7 +479,18 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>( AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id, span), }, ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id), - _ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t), + ty::Pat(base, _) => return type_di_node(cx, base), + // FIXME(unsafe_binders): impl debug info + ty::UnsafeBinder(_) => unimplemented!(), + ty::Alias(..) + | ty::Param(_) + | ty::Bound(..) + | ty::Infer(_) + | ty::Placeholder(_) + | ty::CoroutineWitness(..) + | ty::Error(_) => { + bug!("debuginfo: unexpected type in type_di_node(): {:?}", t) + } }; { diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 23fa05f3d02f..37ca2a959745 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -94,15 +94,13 @@ pub(crate) struct LtoBitcodeFromRlib { } #[derive(Diagnostic)] -pub enum LlvmError<'a> { +pub(crate) enum LlvmError<'a> { #[diag("could not write output to {$path}")] WriteOutput { path: &'a Path }, #[diag("could not create LLVM TargetMachine for triple: {$triple}")] CreateTargetMachine { triple: SmallCStr }, #[diag("failed to run LLVM passes")] RunLlvmPasses, - #[diag("failed to serialize module {$name}")] - SerializeModule { name: &'a str }, #[diag("failed to write LLVM IR to {$path}")] WriteIr { path: &'a Path }, #[diag("failed to prepare thin LTO context")] @@ -115,8 +113,6 @@ pub enum LlvmError<'a> { PrepareThinLtoModule, #[diag("failed to parse bitcode for LTO module")] ParseBitcode, - #[diag("failed to prepare autodiff: src: {$src}, target: {$target}, {$error}")] - PrepareAutoDiff { src: String, target: String, error: String }, } pub(crate) struct WithLlvmError<'a>(pub LlvmError<'a>, pub String); @@ -130,9 +126,6 @@ impl Diagnostic<'_, G> for WithLlvmError<'_> { "could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}" ), RunLlvmPasses => inline_fluent!("failed to run LLVM passes: {$llvm_err}"), - SerializeModule { .. } => { - inline_fluent!("failed to serialize module {$name}: {$llvm_err}") - } WriteIr { .. } => inline_fluent!("failed to write LLVM IR to {$path}: {$llvm_err}"), PrepareThinLtoContext => { inline_fluent!("failed to prepare thin LTO context: {$llvm_err}") @@ -147,9 +140,6 @@ impl Diagnostic<'_, G> for WithLlvmError<'_> { inline_fluent!("failed to prepare thin LTO module: {$llvm_err}") } ParseBitcode => inline_fluent!("failed to parse bitcode for LTO module: {$llvm_err}"), - PrepareAutoDiff { .. } => inline_fluent!( - "failed to prepare autodiff: {$llvm_err}, src: {$src}, target: {$target}, {$error}" - ), }; self.0 .into_diag(dcx, level) @@ -214,5 +204,5 @@ pub(crate) struct FixedX18InvalidArch<'a> { } #[derive(Diagnostic)] -#[diag("`-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later.")] +#[diag("`-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later")] pub(crate) struct SanitizerKcfiArityRequiresLLVM2100; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index e035f0809d68..c0e7c18b4d47 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -646,10 +646,32 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ) -> Self::Value { let tcx = self.tcx(); - // FIXME remove usage of fn_abi - let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); - assert!(!fn_abi.ret.is_indirect()); - let fn_ty = fn_abi.llvm_type(self); + let fn_ty = instance.ty(tcx, self.typing_env()); + let fn_sig = match *fn_ty.kind() { + ty::FnDef(def_id, args) => { + tcx.instantiate_bound_regions_with_erased(tcx.fn_sig(def_id).instantiate(tcx, args)) + } + _ => unreachable!(), + }; + assert!(!fn_sig.c_variadic); + + let ret_layout = self.layout_of(fn_sig.output()); + let llreturn_ty = if ret_layout.is_zst() { + self.type_void() + } else { + ret_layout.immediate_llvm_type(self) + }; + + let mut llargument_tys = Vec::with_capacity(fn_sig.inputs().len()); + for &arg in fn_sig.inputs() { + let arg_layout = self.layout_of(arg); + if arg_layout.is_zst() { + continue; + } + llargument_tys.push(arg_layout.immediate_llvm_type(self)); + } + + let fn_ty = self.type_func(&llargument_tys, llreturn_ty); let fn_ptr = if let Some(&llfn) = self.intrinsic_instances.borrow().get(&instance) { llfn @@ -665,12 +687,11 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let llfn = declare_raw_fn( self, sym, - fn_abi.llvm_cconv(self), + llvm::CCallConv, llvm::UnnamedAddr::Global, llvm::Visibility::Default, fn_ty, ); - fn_abi.apply_attrs_llfn(self, llfn, Some(instance)); llfn }; diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index bf3ec1f39330..c92de64a3349 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -5,7 +5,7 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(extern_types)] #![feature(file_buffered)] #![feature(if_let_guard)] @@ -25,7 +25,6 @@ use std::path::PathBuf; use back::owned_target_machine::OwnedTargetMachine; use back::write::{create_informational_target_machine, create_target_machine}; use context::SimpleCx; -use errors::ParseTargetMachineConfig; use llvm_util::target_config; use rustc_ast::expand::allocator::AllocatorMethod; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; @@ -36,6 +35,7 @@ use rustc_codegen_ssa::back::write::{ use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig}; use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; @@ -152,7 +152,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { type Module = ModuleLlvm; type ModuleBuffer = back::lto::ModuleBuffer; type TargetMachine = OwnedTargetMachine; - type TargetMachineError = crate::errors::LlvmError<'static>; type ThinData = back::lto::ThinData; type ThinBuffer = back::lto::ThinBuffer; fn print_pass_timings(&self) { @@ -164,15 +163,19 @@ impl WriteBackendMethods for LlvmCodegenBackend { print!("{stats}"); } fn run_and_optimize_fat_lto( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, + tm_factory: TargetMachineFactoryFn, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, ) -> ModuleCodegen { let mut module = back::lto::run_fat( cgcx, + prof, shared_emitter, + tm_factory, exported_symbols_for_lto, each_linked_rlib_for_lto, modules, @@ -180,12 +183,13 @@ impl WriteBackendMethods for LlvmCodegenBackend { let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); - back::lto::run_pass_manager(cgcx, dcx, &mut module, false); + back::lto::run_pass_manager(cgcx, prof, dcx, &mut module, false); module } fn run_thin_lto( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], @@ -194,6 +198,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> (Vec>, Vec) { back::lto::run_thin( cgcx, + prof, dcx, exported_symbols_for_lto, each_linked_rlib_for_lto, @@ -202,27 +207,31 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) } fn optimize( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, module: &mut ModuleCodegen, config: &ModuleConfig, ) { - back::write::optimize(cgcx, shared_emitter, module, config) + back::write::optimize(cgcx, prof, shared_emitter, module, config) } fn optimize_thin( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, + tm_factory: TargetMachineFactoryFn, thin: ThinModule, ) -> ModuleCodegen { - back::lto::optimize_thin_module(cgcx, shared_emitter, thin) + back::lto::optimize_thin_module(cgcx, prof, shared_emitter, tm_factory, thin) } fn codegen( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule { - back::write::codegen(cgcx, shared_emitter, module, config) + back::write::codegen(cgcx, prof, shared_emitter, module, config) } fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer) { back::lto::prepare_thin(module) @@ -440,22 +449,9 @@ impl ModuleLlvm { } } - fn tm_from_cgcx( - cgcx: &CodegenContext, - name: &str, - dcx: DiagCtxtHandle<'_>, - ) -> OwnedTargetMachine { - let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, name); - match (cgcx.tm_factory)(tm_factory_config) { - Ok(m) => m, - Err(e) => { - dcx.emit_fatal(ParseTargetMachineConfig(e)); - } - } - } - fn parse( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + tm_factory: TargetMachineFactoryFn, name: &CStr, buffer: &[u8], dcx: DiagCtxtHandle<'_>, @@ -464,7 +460,7 @@ impl ModuleLlvm { let llcx = llvm::LLVMContextCreate(); llvm::LLVMContextSetDiscardValueNames(llcx, cgcx.fewer_names.to_llvm_bool()); let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx); - let tm = ModuleLlvm::tm_from_cgcx(cgcx, name.to_str().unwrap(), dcx); + let tm = tm_factory(dcx, TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap())); ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) } } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 864220635120..c7d7b7429f34 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -36,7 +36,6 @@ rustc_trait_selection = { path = "../rustc_trait_selection" } serde_json = "1.0.59" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tempfile = "3.2" -thin-vec = "0.2.12" thorin-dwp = "0.9" tracing = "0.1" wasm-encoder = "0.219" diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index 43e1e135a666..081fe0aa91aa 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -28,13 +28,13 @@ use std::fmt; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{DiagArgValue, IntoDiagArg}; -use rustc_hir as hir; +use rustc_hir::attrs::{AttributeKind, CguFields, CguKind}; use rustc_hir::def_id::LOCAL_CRATE; +use rustc_hir::{self as hir, find_attr}; use rustc_middle::mir::mono::CodegenUnitNameBuilder; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::{Span, Symbol, sym}; -use thin_vec::ThinVec; +use rustc_span::{Span, Symbol}; use tracing::debug; use crate::errors; @@ -63,9 +63,7 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTr }, }; - for attr in tcx.hir_attrs(rustc_hir::CRATE_HIR_ID) { - ams.check_attr(attr); - } + ams.check_attrs(tcx.hir_attrs(rustc_hir::CRATE_HIR_ID)); set_reuse(&mut ams.cgu_reuse_tracker); @@ -89,109 +87,91 @@ struct AssertModuleSource<'tcx> { } impl<'tcx> AssertModuleSource<'tcx> { - fn check_attr(&mut self, attr: &hir::Attribute) { - let (expected_reuse, comp_kind) = if attr.has_name(sym::rustc_partition_reused) { - (CguReuse::PreLto, ComparisonKind::AtLeast) - } else if attr.has_name(sym::rustc_partition_codegened) { - (CguReuse::No, ComparisonKind::Exact) - } else if attr.has_name(sym::rustc_expected_cgu_reuse) { - match self.field(attr, sym::kind) { - sym::no => (CguReuse::No, ComparisonKind::Exact), - sym::pre_dash_lto => (CguReuse::PreLto, ComparisonKind::Exact), - sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact), - sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast), - other => { - self.tcx - .dcx() - .emit_fatal(errors::UnknownReuseKind { span: attr.span(), kind: other }); - } + fn check_attrs(&mut self, attrs: &[hir::Attribute]) { + for &(span, cgu_fields) in find_attr!(attrs, + AttributeKind::RustcCguTestAttr(e) => e) + .into_iter() + .flatten() + { + let (expected_reuse, comp_kind) = match cgu_fields { + CguFields::PartitionReused { .. } => (CguReuse::PreLto, ComparisonKind::AtLeast), + CguFields::PartitionCodegened { .. } => (CguReuse::No, ComparisonKind::Exact), + CguFields::ExpectedCguReuse { kind, .. } => match kind { + CguKind::No => (CguReuse::No, ComparisonKind::Exact), + CguKind::PreDashLto => (CguReuse::PreLto, ComparisonKind::Exact), + CguKind::PostDashLto => (CguReuse::PostLto, ComparisonKind::Exact), + CguKind::Any => (CguReuse::PreLto, ComparisonKind::AtLeast), + }, + }; + let (CguFields::ExpectedCguReuse { cfg, module, .. } + | CguFields::PartitionCodegened { cfg, module } + | CguFields::PartitionReused { cfg, module }) = cgu_fields; + + if !self.tcx.sess.opts.unstable_opts.query_dep_graph { + self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span }); } - } else { - return; - }; - if !self.tcx.sess.opts.unstable_opts.query_dep_graph { - self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span: attr.span() }); - } + if !self.check_config(cfg) { + debug!("check_attr: config does not match, ignoring attr"); + return; + } - if !self.check_config(attr) { - debug!("check_attr: config does not match, ignoring attr"); - return; - } + let user_path = module.as_str(); + let crate_name = self.tcx.crate_name(LOCAL_CRATE); + let crate_name = crate_name.as_str(); - let user_path = self.field(attr, sym::module).to_string(); - let crate_name = self.tcx.crate_name(LOCAL_CRATE).to_string(); + if !user_path.starts_with(&crate_name) { + self.tcx.dcx().emit_fatal(errors::MalformedCguName { span, user_path, crate_name }); + } - if !user_path.starts_with(&crate_name) { - self.tcx.dcx().emit_fatal(errors::MalformedCguName { - span: attr.span(), - user_path, - crate_name, - }); - } + // Split of the "special suffix" if there is one. + let (user_path, cgu_special_suffix) = if let Some(index) = user_path.rfind('.') { + (&user_path[..index], Some(&user_path[index + 1..])) + } else { + (&user_path[..], None) + }; - // Split of the "special suffix" if there is one. - let (user_path, cgu_special_suffix) = if let Some(index) = user_path.rfind('.') { - (&user_path[..index], Some(&user_path[index + 1..])) - } else { - (&user_path[..], None) - }; + let mut iter = user_path.split('-'); - let mut iter = user_path.split('-'); + // Remove the crate name + assert_eq!(iter.next().unwrap(), crate_name); - // Remove the crate name - assert_eq!(iter.next().unwrap(), crate_name); + let cgu_path_components = iter.collect::>(); - let cgu_path_components = iter.collect::>(); + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(self.tcx); + let cgu_name = cgu_name_builder.build_cgu_name( + LOCAL_CRATE, + cgu_path_components, + cgu_special_suffix, + ); - let cgu_name_builder = &mut CodegenUnitNameBuilder::new(self.tcx); - let cgu_name = - cgu_name_builder.build_cgu_name(LOCAL_CRATE, cgu_path_components, cgu_special_suffix); + debug!("mapping '{user_path}' to cgu name '{cgu_name}'"); - debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name); + if !self.available_cgus.contains(&cgu_name) { + let cgu_names: Vec<&str> = + self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord(); + self.tcx.dcx().emit_err(errors::NoModuleNamed { + span, + user_path, + cgu_name, + cgu_names: cgu_names.join(", "), + }); + } - if !self.available_cgus.contains(&cgu_name) { - let cgu_names: Vec<&str> = - self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord(); - self.tcx.dcx().emit_err(errors::NoModuleNamed { - span: attr.span(), - user_path, + self.cgu_reuse_tracker.set_expectation( cgu_name, - cgu_names: cgu_names.join(", "), - }); + user_path, + span, + expected_reuse, + comp_kind, + ); } - - self.cgu_reuse_tracker.set_expectation( - cgu_name, - user_path, - attr.span(), - expected_reuse, - comp_kind, - ); - } - - fn field(&self, attr: &hir::Attribute, name: Symbol) -> Symbol { - for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { - if item.has_name(name) { - if let Some(value) = item.value_str() { - return value; - } else { - self.tcx.dcx().emit_fatal(errors::FieldAssociatedValueExpected { - span: item.span(), - name, - }); - } - } - } - - self.tcx.dcx().emit_fatal(errors::NoField { span: attr.span(), name }); } /// Scan for a `cfg="foo"` attribute and check whether we have a /// cfg flag called `foo`. - fn check_config(&self, attr: &hir::Attribute) -> bool { + fn check_config(&self, value: Symbol) -> bool { let config = &self.tcx.sess.psess.config; - let value = self.field(attr, sym::cfg); debug!("check_config(config={:?}, value={:?})", config, value); if config.iter().any(|&(name, _)| name == value) { debug!("check_config: matched"); diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index db49f92e39ac..10c4eedb58e8 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -2,7 +2,7 @@ use std::ffi::{OsStr, OsString}; use std::fs::{self, File}; use std::io::prelude::*; use std::path::{Path, PathBuf}; -use std::{env, io, iter, mem, str}; +use std::{env, iter, mem, str}; use find_msvc_tools; use rustc_hir::attrs::WindowsSubsystemKind; @@ -809,7 +809,7 @@ impl<'a> Linker for GccLinker<'a> { if self.sess.target.is_like_darwin { // Write a plain, newline-separated list of symbols - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; for (sym, _) in symbols { debug!(" _{sym}"); @@ -821,7 +821,7 @@ impl<'a> Linker for GccLinker<'a> { } self.link_arg("-exported_symbols_list").link_arg(path); } else if self.sess.target.is_like_windows { - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; // .def file similar to MSVC one but without LIBRARY section @@ -845,7 +845,7 @@ impl<'a> Linker for GccLinker<'a> { self.link_arg("--export").link_arg(sym); } } else if crate_type == CrateType::Executable && !self.sess.target.is_like_solaris { - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; writeln!(f, "{{")?; for (sym, _) in symbols { @@ -860,7 +860,7 @@ impl<'a> Linker for GccLinker<'a> { self.link_arg("--dynamic-list").link_arg(path); } else { // Write an LD version script - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; writeln!(f, "{{")?; if !symbols.is_empty() { @@ -1139,7 +1139,7 @@ impl<'a> Linker for MsvcLinker<'a> { } let path = tmpdir.join("lib.def"); - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; // Start off with the standard module name header and then go @@ -1735,7 +1735,7 @@ impl<'a> Linker for AixLinker<'a> { symbols: &[(String, SymbolExportKind)], ) { let path = tmpdir.join("list.exp"); - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; // FIXME: use llvm-nm to generate export list. for (symbol, _) in symbols { @@ -2135,7 +2135,7 @@ impl<'a> Linker for BpfLinker<'a> { symbols: &[(String, SymbolExportKind)], ) { let path = tmpdir.join("symbols"); - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; for (sym, _) in symbols { writeln!(f, "{sym}")?; diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index e3cf28acfa53..80b3b5a4d7c0 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -127,10 +127,7 @@ pub(super) fn exported_symbols_for_lto( symbols_below_threshold } -pub(super) fn check_lto_allowed( - cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, -) { +pub(super) fn check_lto_allowed(cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>) { if cgcx.lto == Lto::ThinLocal { // Crate local LTO is always allowed return; diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 8321f7fde503..87a043fbdf24 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -23,13 +23,15 @@ use rustc_hir::find_attr; use rustc_incremental::{ copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, }; +use rustc_macros::{Decodable, Encodable}; use rustc_metadata::fs::copy_to_stdout; use rustc_middle::bug; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{ - self, CrateType, Lto, OutFileName, OutputFilenames, OutputType, Passes, SwitchWithOptPath, + self, CrateType, Lto, OptLevel, OutFileName, OutputFilenames, OutputType, Passes, + SwitchWithOptPath, }; use rustc_span::source_map::SourceMap; use rustc_span::{FileName, InnerSpan, Span, SpanData}; @@ -49,7 +51,7 @@ use crate::{ const PRE_LTO_BC_EXT: &str = "pre-lto.bc"; /// What kind of object file to emit. -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Encodable, Decodable)] pub enum EmitObj { // No object file. None, @@ -63,7 +65,7 @@ pub enum EmitObj { } /// What kind of llvm bitcode section to embed in an object file. -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Encodable, Decodable)] pub enum BitcodeSection { // No bitcode section. None, @@ -73,6 +75,7 @@ pub enum BitcodeSection { } /// Module-specific configuration for `optimize_and_codegen`. +#[derive(Encodable, Decodable)] pub struct ModuleConfig { /// Names of additional optimization passes to run. pub passes: Vec, @@ -287,10 +290,7 @@ pub struct TargetMachineFactoryConfig { } impl TargetMachineFactoryConfig { - pub fn new( - cgcx: &CodegenContext, - module_name: &str, - ) -> TargetMachineFactoryConfig { + pub fn new(cgcx: &CodegenContext, module_name: &str) -> TargetMachineFactoryConfig { let split_dwarf_file = if cgcx.target_can_use_split_dwarf { cgcx.output_filenames.split_dwarf_path( cgcx.split_debuginfo, @@ -313,19 +313,17 @@ impl TargetMachineFactoryConfig { pub type TargetMachineFactoryFn = Arc< dyn Fn( + DiagCtxtHandle<'_>, TargetMachineFactoryConfig, - ) -> Result< - ::TargetMachine, - ::TargetMachineError, - > + Send + ) -> ::TargetMachine + + Send + Sync, >; /// Additional resources used by optimize_and_codegen (not module specific) -#[derive(Clone)] -pub struct CodegenContext { +#[derive(Clone, Encodable, Decodable)] +pub struct CodegenContext { // Resources needed when running LTO - pub prof: SelfProfilerRef, pub lto: Lto, pub use_linker_plugin_lto: bool, pub dylib_lto: bool, @@ -337,7 +335,8 @@ pub struct CodegenContext { pub output_filenames: Arc, pub invocation_temp: Option, pub module_config: Arc, - pub tm_factory: TargetMachineFactoryFn, + pub opt_level: OptLevel, + pub backend_features: Vec, pub msvc_imps_needed: bool, pub is_pe_coff: bool, pub target_can_use_split_dwarf: bool, @@ -364,17 +363,19 @@ pub struct CodegenContext { } fn generate_thin_lto_work( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], needs_thin_lto: Vec<(String, B::ThinBuffer)>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Vec<(ThinLtoWorkItem, u64)> { - let _prof_timer = cgcx.prof.generic_activity("codegen_thin_generate_lto_work"); + let _prof_timer = prof.generic_activity("codegen_thin_generate_lto_work"); let (lto_modules, copy_jobs) = B::run_thin_lto( cgcx, + prof, dcx, exported_symbols_for_lto, each_linked_rlib_for_lto, @@ -399,9 +400,9 @@ fn generate_thin_lto_work( .collect() } -struct CompiledModules { - modules: Vec, - allocator_module: Option, +pub struct CompiledModules { + pub modules: Vec, + pub allocator_module: Option, } enum MaybeLtoModules { @@ -410,7 +411,7 @@ enum MaybeLtoModules { allocator_module: Option, }, FatLto { - cgcx: CodegenContext, + cgcx: CodegenContext, exported_symbols_for_lto: Arc>, each_linked_rlib_file_for_lto: Vec, needs_fat_lto: Vec>, @@ -418,7 +419,7 @@ enum MaybeLtoModules { Vec<(SerializedModule<::ModuleBuffer>, WorkProduct)>, }, ThinLto { - cgcx: CodegenContext, + cgcx: CodegenContext, exported_symbols_for_lto: Arc>, each_linked_rlib_file_for_lto: Vec, needs_thin_lto: Vec<(String, ::ThinBuffer)>, @@ -534,7 +535,7 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( work_products } -fn produce_final_output_artifacts( +pub fn produce_final_output_artifacts( sess: &Session, compiled_modules: &CompiledModules, crate_output: &OutputFilenames, @@ -842,13 +843,14 @@ pub(crate) fn compute_per_cgu_lto_type( } fn execute_optimize_work_item( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: SharedEmitter, mut module: ModuleCodegen, ) -> WorkItemResult { - let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &*module.name); + let _timer = prof.generic_activity_with_arg("codegen_module_optimize", &*module.name); - B::optimize(cgcx, &shared_emitter, &mut module, &cgcx.module_config); + B::optimize(cgcx, prof, &shared_emitter, &mut module, &cgcx.module_config); // After we've done the initial round of optimizations we need to // decide whether to synchronously codegen this module or ship it @@ -869,7 +871,7 @@ fn execute_optimize_work_item( match lto_type { ComputedLtoType::No => { - let module = B::codegen(cgcx, &shared_emitter, module, &cgcx.module_config); + let module = B::codegen(cgcx, &prof, &shared_emitter, module, &cgcx.module_config); WorkItemResult::Finished(module) } ComputedLtoType::Thin => { @@ -897,14 +899,14 @@ fn execute_optimize_work_item( } } -fn execute_copy_from_cache_work_item( - cgcx: &CodegenContext, +fn execute_copy_from_cache_work_item( + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: SharedEmitter, module: CachedModuleCodegen, ) -> CompiledModule { - let _timer = cgcx - .prof - .generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &*module.name); + let _timer = + prof.generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &*module.name); let dcx = DiagCtxt::new(Box::new(shared_emitter)); let dcx = dcx.handle(); @@ -986,14 +988,16 @@ fn execute_copy_from_cache_work_item( } fn do_fat_lto( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: SharedEmitter, + tm_factory: TargetMachineFactoryFn, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], mut needs_fat_lto: Vec>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, ) -> CompiledModule { - let _timer = cgcx.prof.verbose_generic_activity("LLVM_fatlto"); + let _timer = prof.verbose_generic_activity("LLVM_fatlto"); let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); @@ -1006,17 +1010,21 @@ fn do_fat_lto( let module = B::run_and_optimize_fat_lto( cgcx, + prof, &shared_emitter, + tm_factory, exported_symbols_for_lto, each_linked_rlib_for_lto, needs_fat_lto, ); - B::codegen(cgcx, &shared_emitter, module, &cgcx.module_config) + B::codegen(cgcx, prof, &shared_emitter, module, &cgcx.module_config) } -fn do_thin_lto<'a, B: ExtraBackendMethods>( - cgcx: &'a CodegenContext, +fn do_thin_lto( + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: SharedEmitter, + tm_factory: TargetMachineFactoryFn, exported_symbols_for_lto: Arc>, each_linked_rlib_for_lto: Vec, needs_thin_lto: Vec<(String, ::ThinBuffer)>, @@ -1025,7 +1033,7 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>( WorkProduct, )>, ) -> Vec { - let _timer = cgcx.prof.verbose_generic_activity("LLVM_thinlto"); + let _timer = prof.verbose_generic_activity("LLVM_thinlto"); let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); @@ -1053,8 +1061,9 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>( // bunch of work items onto our queue to do LTO. This all // happens on the coordinator thread but it's very quick so // we don't worry about tokens. - for (work, cost) in generate_thin_lto_work( + for (work, cost) in generate_thin_lto_work::( cgcx, + prof, dcx, &exported_symbols_for_lto, &each_linked_rlib_for_lto, @@ -1097,7 +1106,14 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>( while used_token_count < tokens.len() + 1 && let Some((item, _)) = work_items.pop() { - spawn_thin_lto_work(&cgcx, shared_emitter.clone(), coordinator_send.clone(), item); + spawn_thin_lto_work( + &cgcx, + prof, + shared_emitter.clone(), + Arc::clone(&tm_factory), + coordinator_send.clone(), + item, + ); used_token_count += 1; } } else { @@ -1158,14 +1174,16 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>( } fn execute_thin_lto_work_item( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: SharedEmitter, + tm_factory: TargetMachineFactoryFn, module: lto::ThinModule, ) -> CompiledModule { - let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", module.name()); + let _timer = prof.generic_activity_with_arg("codegen_module_perform_lto", module.name()); - let module = B::optimize_thin(cgcx, &shared_emitter, module); - B::codegen(cgcx, &shared_emitter, module, &cgcx.module_config) + let module = B::optimize_thin(cgcx, prof, &shared_emitter, tm_factory, module); + B::codegen(cgcx, prof, &shared_emitter, module, &cgcx.module_config) } /// Messages sent to the coordinator. @@ -1265,6 +1283,7 @@ fn start_executing_work( coordinator_send: Sender>, ) -> thread::JoinHandle, ()>> { let sess = tcx.sess; + let prof = sess.prof.clone(); let mut each_linked_rlib_for_lto = Vec::new(); let mut each_linked_rlib_file_for_lto = Vec::new(); @@ -1292,8 +1311,9 @@ fn start_executing_work( }) .expect("failed to spawn helper thread"); - let ol = tcx.backend_optimization_level(()); - let backend_features = tcx.global_backend_features(()); + let opt_level = tcx.backend_optimization_level(()); + let backend_features = tcx.global_backend_features(()).clone(); + let tm_factory = backend.target_machine_factory(tcx.sess, opt_level, &backend_features); let remark_dir = if let Some(ref dir) = sess.opts.unstable_opts.remark_dir { let result = fs::create_dir_all(dir).and_then(|_| dir.canonicalize()); @@ -1305,7 +1325,7 @@ fn start_executing_work( None }; - let cgcx = CodegenContext:: { + let cgcx = CodegenContext { crate_types: tcx.crate_types().to_vec(), lto: sess.lto(), use_linker_plugin_lto: sess.opts.cg.linker_plugin_lto.enabled(), @@ -1314,13 +1334,13 @@ fn start_executing_work( fewer_names: sess.fewer_names(), save_temps: sess.opts.cg.save_temps, time_trace: sess.opts.unstable_opts.llvm_time_trace, - prof: sess.prof.clone(), remark: sess.opts.cg.remark.clone(), remark_dir, incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), output_filenames: Arc::clone(tcx.output_filenames(())), module_config: regular_config, - tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features), + opt_level, + backend_features, msvc_imps_needed: msvc_imps_needed(tcx), is_pe_coff: tcx.sess.target.is_like_windows, target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(), @@ -1514,7 +1534,7 @@ fn start_executing_work( let mut llvm_start_time: Option> = None; if let Some(allocator_module) = &mut allocator_module { - B::optimize(&cgcx, &shared_emitter, allocator_module, &allocator_config); + B::optimize(&cgcx, &prof, &shared_emitter, allocator_module, &allocator_config); } // Run the message loop while there's still anything that needs message @@ -1554,6 +1574,7 @@ fn start_executing_work( main_thread_state = MainThreadState::Lending; spawn_work( &cgcx, + &prof, shared_emitter.clone(), coordinator_send.clone(), &mut llvm_start_time, @@ -1578,6 +1599,7 @@ fn start_executing_work( main_thread_state = MainThreadState::Lending; spawn_work( &cgcx, + &prof, shared_emitter.clone(), coordinator_send.clone(), &mut llvm_start_time, @@ -1620,6 +1642,7 @@ fn start_executing_work( { spawn_work( &cgcx, + &prof, shared_emitter.clone(), coordinator_send.clone(), &mut llvm_start_time, @@ -1776,9 +1799,11 @@ fn start_executing_work( assert!(needs_fat_lto.is_empty()); if cgcx.lto == Lto::ThinLocal { - compiled_modules.extend(do_thin_lto( + compiled_modules.extend(do_thin_lto::( &cgcx, + &prof, shared_emitter.clone(), + tm_factory, exported_symbols_for_lto, each_linked_rlib_file_for_lto, needs_thin_lto, @@ -1803,7 +1828,7 @@ fn start_executing_work( Ok(MaybeLtoModules::NoLto { modules: compiled_modules, allocator_module: allocator_module.map(|allocator_module| { - B::codegen(&cgcx, &shared_emitter, allocator_module, &allocator_config) + B::codegen(&cgcx, &prof, &shared_emitter, allocator_module, &allocator_config) }), }) }) @@ -1872,23 +1897,25 @@ fn start_executing_work( pub(crate) struct WorkerFatalError; fn spawn_work<'a, B: ExtraBackendMethods>( - cgcx: &'a CodegenContext, + cgcx: &CodegenContext, + prof: &'a SelfProfilerRef, shared_emitter: SharedEmitter, coordinator_send: Sender>, llvm_start_time: &mut Option>, work: WorkItem, ) { if llvm_start_time.is_none() { - *llvm_start_time = Some(cgcx.prof.verbose_generic_activity("LLVM_passes")); + *llvm_start_time = Some(prof.verbose_generic_activity("LLVM_passes")); } let cgcx = cgcx.clone(); + let prof = prof.clone(); B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { - WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, shared_emitter, m), + WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, &prof, shared_emitter, m), WorkItem::CopyPostLtoArtifacts(m) => WorkItemResult::Finished( - execute_copy_from_cache_work_item(&cgcx, shared_emitter, m), + execute_copy_from_cache_work_item(&cgcx, &prof, shared_emitter, m), ), })); @@ -1909,20 +1936,25 @@ fn spawn_work<'a, B: ExtraBackendMethods>( .expect("failed to spawn work thread"); } -fn spawn_thin_lto_work<'a, B: ExtraBackendMethods>( - cgcx: &'a CodegenContext, +fn spawn_thin_lto_work( + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: SharedEmitter, + tm_factory: TargetMachineFactoryFn, coordinator_send: Sender, work: ThinLtoWorkItem, ) { let cgcx = cgcx.clone(); + let prof = prof.clone(); B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { ThinLtoWorkItem::CopyPostLtoArtifacts(m) => { - execute_copy_from_cache_work_item(&cgcx, shared_emitter, m) + execute_copy_from_cache_work_item(&cgcx, &prof, shared_emitter, m) + } + ThinLtoWorkItem::ThinLto(m) => { + execute_thin_lto_work_item(&cgcx, &prof, shared_emitter, tm_factory, m) } - ThinLtoWorkItem::ThinLto(m) => execute_thin_lto_work_item(&cgcx, shared_emitter, m), })); let msg = match result { @@ -1981,11 +2013,7 @@ impl SharedEmitter { } impl Emitter for SharedEmitter { - fn emit_diagnostic( - &mut self, - mut diag: rustc_errors::DiagInner, - _registry: &rustc_errors::registry::Registry, - ) { + fn emit_diagnostic(&mut self, mut diag: rustc_errors::DiagInner) { // Check that we aren't missing anything interesting when converting to // the cut-down local `DiagInner`. assert!(!diag.span.has_span_labels()); @@ -2163,34 +2191,54 @@ impl OngoingCodegen { each_linked_rlib_file_for_lto, needs_fat_lto, lto_import_only_modules, - } => CompiledModules { - modules: vec![do_fat_lto( - &cgcx, - shared_emitter, - &exported_symbols_for_lto, - &each_linked_rlib_file_for_lto, - needs_fat_lto, - lto_import_only_modules, - )], - allocator_module: None, - }, + } => { + let tm_factory = self.backend.target_machine_factory( + sess, + cgcx.opt_level, + &cgcx.backend_features, + ); + + CompiledModules { + modules: vec![do_fat_lto( + &cgcx, + &sess.prof, + shared_emitter, + tm_factory, + &exported_symbols_for_lto, + &each_linked_rlib_file_for_lto, + needs_fat_lto, + lto_import_only_modules, + )], + allocator_module: None, + } + } MaybeLtoModules::ThinLto { cgcx, exported_symbols_for_lto, each_linked_rlib_file_for_lto, needs_thin_lto, lto_import_only_modules, - } => CompiledModules { - modules: do_thin_lto( - &cgcx, - shared_emitter, - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, - needs_thin_lto, - lto_import_only_modules, - ), - allocator_module: None, - }, + } => { + let tm_factory = self.backend.target_machine_factory( + sess, + cgcx.opt_level, + &cgcx.backend_features, + ); + + CompiledModules { + modules: do_thin_lto::( + &cgcx, + &sess.prof, + shared_emitter, + tm_factory, + exported_symbols_for_lto, + each_linked_rlib_file_for_lto, + needs_thin_lto, + lto_import_only_modules, + ), + allocator_module: None, + } + } }); shared_emitter_main.check(sess, true); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 43767dff92bf..85c8890d661c 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -1111,7 +1111,7 @@ pub fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> // know that later). If we are not doing LTO, there is only one optimized // version of each module, so we re-use that. let dep_node = cgu.codegen_dep_node(tcx); - tcx.dep_graph.assert_dep_node_not_yet_allocated_in_current_session(&dep_node, || { + tcx.dep_graph.assert_dep_node_not_yet_allocated_in_current_session(tcx.sess, &dep_node, || { format!( "CompileCodegenUnit dep-node for CGU `{}` already exists before marking.", cgu.name() diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 742e05973ee5..a50a1da7aaa8 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -42,14 +42,6 @@ pub(crate) struct CguNotRecorded<'a> { pub cgu_name: &'a str, } -#[derive(Diagnostic)] -#[diag("unknown cgu-reuse-kind `{$kind}` specified")] -pub(crate) struct UnknownReuseKind { - #[primary_span] - pub span: Span, - pub kind: Symbol, -} - #[derive(Diagnostic)] #[diag("found CGU-reuse attribute but `-Zquery-dep-graph` was not specified")] pub(crate) struct MissingQueryDepGraph { @@ -59,13 +51,13 @@ pub(crate) struct MissingQueryDepGraph { #[derive(Diagnostic)] #[diag( - "found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case)." + "found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case)" )] -pub(crate) struct MalformedCguName { +pub(crate) struct MalformedCguName<'a> { #[primary_span] pub span: Span, - pub user_path: String, - pub crate_name: String, + pub user_path: &'a str, + pub crate_name: &'a str, } #[derive(Diagnostic)] @@ -78,22 +70,6 @@ pub(crate) struct NoModuleNamed<'a> { pub cgu_names: String, } -#[derive(Diagnostic)] -#[diag("associated value expected for `{$name}`")] -pub(crate) struct FieldAssociatedValueExpected { - #[primary_span] - pub span: Span, - pub name: Symbol, -} - -#[derive(Diagnostic)] -#[diag("no field `{$name}`")] -pub(crate) struct NoField { - #[primary_span] - pub span: Span, - pub name: Symbol, -} - #[derive(Diagnostic)] #[diag("failed to write lib.def file: {$error}")] pub(crate) struct LibDefWriteFailure { @@ -154,14 +130,14 @@ pub(crate) struct CopyPathBuf { // Reports Paths using `Debug` implementation rather than Path's `Display` implementation. #[derive(Diagnostic)] #[diag("could not copy {$from} to {$to}: {$error}")] -pub struct CopyPath<'a> { +pub(crate) struct CopyPath<'a> { from: DebugArgPath<'a>, to: DebugArgPath<'a>, error: Error, } impl<'a> CopyPath<'a> { - pub fn new(from: &'a Path, to: &'a Path, error: Error) -> CopyPath<'a> { + pub(crate) fn new(from: &'a Path, to: &'a Path, error: Error) -> CopyPath<'a> { CopyPath { from: DebugArgPath(from), to: DebugArgPath(to), error } } } @@ -178,19 +154,19 @@ impl IntoDiagArg for DebugArgPath<'_> { #[diag( "option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty" )] -pub struct BinaryOutputToTty { +pub(crate) struct BinaryOutputToTty { pub shorthand: &'static str, } #[derive(Diagnostic)] #[diag("ignoring emit path because multiple .{$extension} files were produced")] -pub struct IgnoringEmitPath { +pub(crate) struct IgnoringEmitPath { pub extension: &'static str, } #[derive(Diagnostic)] #[diag("ignoring -o because multiple .{$extension} files were produced")] -pub struct IgnoringOutput { +pub(crate) struct IgnoringOutput { pub extension: &'static str, } @@ -562,12 +538,12 @@ pub(crate) struct SelfContainedLinkerMissing; #[derive(Diagnostic)] #[diag( - "please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option." + "please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option" )] pub(crate) struct CheckInstalledVisualStudio; #[derive(Diagnostic)] -#[diag("VS Code is a different product, and is not sufficient.")] +#[diag("VS Code is a different product, and is not sufficient")] pub(crate) struct InsufficientVSCodeProduct; #[derive(Diagnostic)] @@ -610,13 +586,13 @@ pub(crate) struct LinkerFileStem; #[derive(Diagnostic)] #[diag( - "link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms." + "link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms" )] pub(crate) struct StaticLibraryNativeArtifacts; #[derive(Diagnostic)] #[diag( - "native artifacts to link against have been written to {$path}. The order and any duplication can be significant on some platforms." + "native artifacts to link against have been written to {$path}. The order and any duplication can be significant on some platforms" )] pub(crate) struct StaticLibraryNativeArtifactsToFile<'a> { pub path: &'a Path, diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index e3934065b0f7..cbfa12e21eba 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -1,5 +1,5 @@ // tidy-alphabetical-start -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] #![feature(file_buffered)] #![feature(if_let_guard)] diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index e1d23841118c..5f5d0ac5d9fc 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -1,16 +1,18 @@ use std::path::PathBuf; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::DiagCtxtHandle; use rustc_middle::dep_graph::WorkProduct; use crate::back::lto::{SerializedModule, ThinModule}; -use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter}; +use crate::back::write::{ + CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryFn, +}; use crate::{CompiledModule, ModuleCodegen}; pub trait WriteBackendMethods: Clone + 'static { type Module: Send + Sync; type TargetMachine; - type TargetMachineError; type ModuleBuffer: ModuleBufferMethods; type ThinData: Send + Sync; type ThinBuffer: ThinBufferMethods; @@ -18,8 +20,10 @@ pub trait WriteBackendMethods: Clone + 'static { /// Performs fat LTO by merging all modules into a single one, running autodiff /// if necessary and running any further optimizations fn run_and_optimize_fat_lto( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, + tm_factory: TargetMachineFactoryFn, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, @@ -28,7 +32,8 @@ pub trait WriteBackendMethods: Clone + 'static { /// lists, one of the modules that need optimization and another for modules that /// can simply be copied over from the incr. comp. cache. fn run_thin_lto( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], @@ -38,18 +43,22 @@ pub trait WriteBackendMethods: Clone + 'static { fn print_pass_timings(&self); fn print_statistics(&self); fn optimize( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, module: &mut ModuleCodegen, config: &ModuleConfig, ); fn optimize_thin( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, + tm_factory: TargetMachineFactoryFn, thin: ThinModule, ) -> ModuleCodegen; fn codegen( - cgcx: &CodegenContext, + cgcx: &CodegenContext, + prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, module: ModuleCodegen, config: &ModuleConfig, diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml index 51dcee8d8822..aa013cf61f48 100644 --- a/compiler/rustc_const_eval/Cargo.toml +++ b/compiler/rustc_const_eval/Cargo.toml @@ -11,7 +11,6 @@ rustc_apfloat = "0.2.0" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl deleted file mode 100644 index 4aa0a0b2a96f..000000000000 --- a/compiler/rustc_const_eval/messages.ftl +++ /dev/null @@ -1,507 +0,0 @@ -const_eval_address_space_full = - there are no more free addresses in the address space - -const_eval_alignment_check_failed = - {$msg -> - [AccessedPtr] accessing memory - *[other] accessing memory based on pointer - } with alignment {$has}, but alignment {$required} is required - -const_eval_already_reported = - an error has already been reported elsewhere (this should not usually be printed) -const_eval_assume_false = - `assume` called with `false` - -const_eval_bad_pointer_op = {$operation -> - [MemoryAccess] memory access failed - [InboundsPointerArithmetic] in-bounds pointer arithmetic failed - *[Dereferenceable] pointer not dereferenceable -} -const_eval_bad_pointer_op_attempting = {const_eval_bad_pointer_op}: {$operation -> - [MemoryAccess] attempting to access {$inbounds_size -> - [1] 1 byte - *[x] {$inbounds_size} bytes - } - [InboundsPointerArithmetic] attempting to offset pointer by {$inbounds_size -> - [1] 1 byte - *[x] {$inbounds_size} bytes - } - *[Dereferenceable] pointer must {$inbounds_size -> - [0] point to some allocation - [1] be dereferenceable for 1 byte - *[x] be dereferenceable for {$inbounds_size} bytes - } - } - -const_eval_bounds_check_failed = - indexing out of bounds: the len is {$len} but the index is {$index} -const_eval_call_nonzero_intrinsic = - `{$name}` called on 0 - -const_eval_closure_call = - closures need an RFC before allowed to be called in {const_eval_const_context}s -const_eval_closure_fndef_not_const = - function defined here, but it is not `const` - -const_eval_consider_dereferencing = - consider dereferencing here - -const_eval_const_accesses_mut_global = - constant accesses mutable global memory - -const_eval_const_context = {$kind -> - [const] constant - [static] static - [const_fn] constant function - *[other] {""} -} - -const_eval_const_heap_ptr_in_final = encountered `const_allocate` pointer in final value that was not made global - .note = use `const_make_global` to turn allocated pointers into immutable globals before returning - -const_eval_const_make_global_ptr_already_made_global = attempting to call `const_make_global` twice on the same allocation {$alloc} - -const_eval_const_make_global_ptr_is_non_heap = pointer passed to `const_make_global` does not point to a heap allocation: {$ptr} - -const_eval_const_make_global_with_dangling_ptr = pointer passed to `const_make_global` is dangling: {$ptr} - -const_eval_const_make_global_with_offset = making {$ptr} global which does not point to the beginning of an object - -const_eval_copy_nonoverlapping_overlapping = - `copy_nonoverlapping` called on overlapping ranges - -const_eval_dangling_int_pointer = - {const_eval_bad_pointer_op_attempting}, but got {$pointer} which is a dangling pointer (it has no provenance) -const_eval_dangling_null_pointer = - {const_eval_bad_pointer_op_attempting}, but got null pointer - -const_eval_dangling_ptr_in_final = encountered dangling pointer in final value of {const_eval_intern_kind} -const_eval_dead_local = - accessing a dead local variable -const_eval_dealloc_immutable = - deallocating immutable allocation {$alloc} - -const_eval_dealloc_incorrect_layout = - incorrect layout on deallocation: {$alloc} has size {$size} and alignment {$align}, but gave size {$size_found} and alignment {$align_found} - -const_eval_dealloc_kind_mismatch = - deallocating {$alloc}, which is {$alloc_kind} memory, using {$kind} deallocation operation - -const_eval_deref_function_pointer = - accessing {$allocation} which contains a function -const_eval_deref_typeid_pointer = - accessing {$allocation} which contains a `TypeId` -const_eval_deref_vtable_pointer = - accessing {$allocation} which contains a vtable -const_eval_division_by_zero = - dividing by zero -const_eval_division_overflow = - overflow in signed division (dividing MIN by -1) - -const_eval_dyn_call_not_a_method = - `dyn` call trying to call something that is not a method - -const_eval_error = evaluation of `{$instance}` failed {$num_frames -> - [0] here - *[other] inside this call -} - -const_eval_exact_div_has_remainder = - exact_div: {$a} cannot be divided by {$b} without remainder - -const_eval_extern_static = - cannot access extern static `{$did}` -const_eval_extern_type_field = `extern type` field does not have a known offset - -const_eval_fn_ptr_call = - function pointers need an RFC before allowed to be called in {const_eval_const_context}s -const_eval_frame_note = {$times -> - [0] {const_eval_frame_note_inner} - *[other] [... {$times} additional calls {const_eval_frame_note_inner} ...] -} - -const_eval_frame_note_inner = inside {$where_ -> - [closure] closure - [instance] `{$instance}` - *[other] {""} -} - -const_eval_frame_note_last = the failure occurred here - -const_eval_incompatible_arg_types = - calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty} - -const_eval_incompatible_calling_conventions = - calling a function with calling convention "{$callee_conv}" using calling convention "{$caller_conv}" - -const_eval_incompatible_return_types = - calling a function with return type {$callee_ty} passing return place of type {$caller_ty} - -const_eval_interior_mutable_borrow_escaping = - interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed - .label = this borrow of an interior mutable value refers to such a temporary - .note = temporaries in constants and statics can have their lifetime extended until the end of the program - .note2 = to avoid accidentally creating global mutable state, such temporaries must be immutable - .help = if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut` - -const_eval_intern_kind = {$kind -> - [static] static - [static_mut] mutable static - [const] constant - [promoted] promoted - *[other] {""} -} - -const_eval_interrupted = compilation was interrupted - -const_eval_invalid_align_details = - invalid align passed to `{$name}`: {$align} is {$err_kind -> - [not_power_of_two] not a power of 2 - [too_large] too large - *[other] {""} - } - -const_eval_invalid_bool = - interpreting an invalid 8-bit value as a bool: 0x{$value} -const_eval_invalid_char = - interpreting an invalid 32-bit value as a char: 0x{$value} -const_eval_invalid_dealloc = - deallocating {$alloc_id}, which is {$kind -> - [fn] a function - [vtable] a vtable - [static_mem] static memory - *[other] {""} - } - -const_eval_invalid_function_pointer = - using {$pointer} as function pointer but it does not point to a function -const_eval_invalid_meta = - invalid metadata in wide pointer: total size is bigger than largest supported object -const_eval_invalid_meta_slice = - invalid metadata in wide pointer: slice is bigger than largest supported object - -const_eval_invalid_niched_enum_variant_written = - trying to set discriminant of a {$ty} to the niched variant, but the value does not match - -const_eval_invalid_str = - this string is not valid UTF-8: {$err} -const_eval_invalid_tag = - enum value has invalid tag: {$tag} -const_eval_invalid_transmute = - transmuting from {$src_bytes}-byte type to {$dest_bytes}-byte type: `{$src}` -> `{$dest}` - -const_eval_invalid_uninit_bytes = - reading memory at {$alloc}{$access}, but memory is uninitialized at {$uninit}, and this operation requires initialized memory -const_eval_invalid_uninit_bytes_unknown = - using uninitialized data, but this operation requires initialized memory - -const_eval_invalid_vtable_pointer = - using {$pointer} as vtable pointer but it does not point to a vtable - -const_eval_invalid_vtable_trait = - using vtable for `{$vtable_dyn_type}` but `{$expected_dyn_type}` was expected - -const_eval_lazy_lock = - consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -const_eval_live_drop = - destructor of `{$dropped_ty}` cannot be evaluated at compile-time - .label = the destructor for this type cannot be evaluated in {const_eval_const_context}s - .dropped_at_label = value is dropped here - -const_eval_long_running = - constant evaluation is taking a long time - .note = this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval. - If your compilation actually takes a long time, you can safely allow the lint. - .label = the const evaluator is currently interpreting this expression - .help = the constant being evaluated - -const_eval_memory_exhausted = - tried to allocate more memory than available to compiler - -const_eval_modified_global = - modifying a static's initial value from another static's initializer - -const_eval_mutable_borrow_escaping = - mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed - .label = this mutable borrow refers to such a temporary - .note = temporaries in constants and statics can have their lifetime extended until the end of the program - .note2 = to avoid accidentally creating global mutable state, such temporaries must be immutable - .help = if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut` - -const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind} - -const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead - -const_eval_non_const_await = - cannot convert `{$ty}` into a future in {const_eval_const_context}s - -const_eval_non_const_closure = - cannot call {$non_or_conditionally}-const closure in {const_eval_const_context}s - -const_eval_non_const_deref_coercion = - cannot perform {$non_or_conditionally}-const deref coercion on `{$ty}` in {const_eval_const_context}s - .note = attempting to deref into `{$target_ty}` - .target_note = deref defined here - -const_eval_non_const_fmt_macro_call = - cannot call {$non_or_conditionally}-const formatting macro in {const_eval_const_context}s - -const_eval_non_const_fn_call = - cannot call {$non_or_conditionally}-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s - -const_eval_non_const_for_loop_into_iter = - cannot use `for` loop on `{$ty}` in {const_eval_const_context}s - -const_eval_non_const_impl = - impl defined here, but it is not `const` - -const_eval_non_const_intrinsic = - cannot call non-const intrinsic `{$name}` in {const_eval_const_context}s - -const_eval_non_const_match_eq = cannot match on `{$ty}` in {const_eval_const_context}s - .note = `{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es - -const_eval_non_const_operator = - cannot call {$non_or_conditionally}-const operator in {const_eval_const_context}s - -const_eval_non_const_question_branch = - `?` is not allowed on `{$ty}` in {const_eval_const_context}s -const_eval_non_const_question_from_residual = - `?` is not allowed on `{$ty}` in {const_eval_const_context}s - -const_eval_non_const_try_block_from_output = - `try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s - -const_eval_not_enough_caller_args = - calling a function with fewer arguments than it requires - -const_eval_offset_from_different_allocations = - `{$name}` called on two different pointers that are not both derived from the same allocation -const_eval_offset_from_out_of_bounds = - `{$name}` called on two different pointers where the memory range between them is not in-bounds of an allocation -const_eval_offset_from_overflow = - `{$name}` called when first pointer is too far ahead of second -const_eval_offset_from_underflow = - `{$name}` called when first pointer is too far before second -const_eval_offset_from_unsigned_overflow = - `ptr_offset_from_unsigned` called when first pointer has smaller {$is_addr -> - [true] address - *[false] offset - } than second: {$a_offset} < {$b_offset} - -const_eval_overflow_arith = - arithmetic overflow in `{$intrinsic}` -const_eval_overflow_shift = - overflowing shift by {$shift_amount} in `{$intrinsic}` - -const_eval_panic = evaluation panicked: {$msg} - -const_eval_panic_non_str = argument to `panic!()` in a const context must have type `&str` - -const_eval_partial_pointer_in_final = encountered partial pointer in final value of {const_eval_intern_kind} - .note = while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value - -const_eval_partial_pointer_read = - unable to read parts of a pointer from memory at {$ptr} -const_eval_pointer_arithmetic_overflow = - overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize` - -const_eval_pointer_out_of_bounds = - {const_eval_bad_pointer_op_attempting}, but got {$pointer} which {$ptr_offset_is_neg -> - [true] points to before the beginning of the allocation - *[false] {$inbounds_size_is_neg -> - [false] {$alloc_size_minus_ptr_offset -> - [0] is at or beyond the end of the allocation of size {$alloc_size -> - [1] 1 byte - *[x] {$alloc_size} bytes - } - [1] is only 1 byte from the end of the allocation - *[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation - } - *[true] {$ptr_offset_abs -> - [0] is at the beginning of the allocation - *[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation - } - } - } - -const_eval_pointer_use_after_free = - {const_eval_bad_pointer_op}: {$alloc_id} has been freed, so this pointer is dangling -const_eval_ptr_as_bytes_1 = - this code performed an operation that depends on the underlying bytes representing a pointer -const_eval_ptr_as_bytes_2 = - the absolute address of a pointer is not known at compile-time, so such operations are not supported - -const_eval_range = in the range {$lo}..={$hi} -const_eval_range_lower = greater or equal to {$lo} -const_eval_range_singular = equal to {$lo} -const_eval_range_upper = less or equal to {$hi} -const_eval_range_wrapping = less or equal to {$hi}, or greater or equal to {$lo} -const_eval_raw_bytes = the raw bytes of the constant (size: {$size}, align: {$align}) {"{"}{$bytes}{"}"} - -const_eval_raw_ptr_comparison = - pointers cannot be reliably compared during const eval - .note = see issue #53020 for more information - -const_eval_raw_ptr_to_int = - pointers cannot be cast to integers during const eval - .note = at compile-time, pointers do not have an integer value - .note2 = avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior - -const_eval_read_pointer_as_int = - unable to turn pointer into integer -const_eval_realloc_or_alloc_with_offset = - {$kind -> - [dealloc] deallocating - [realloc] reallocating - *[other] {""} - } {$ptr} which does not point to the beginning of an object - -const_eval_recursive_static = encountered static that tried to access itself during initialization - -const_eval_remainder_by_zero = - calculating the remainder with a divisor of zero -const_eval_remainder_overflow = - overflow in signed remainder (dividing MIN by -1) -const_eval_scalar_size_mismatch = - scalar size mismatch: expected {$target_size} bytes but got {$data_size} bytes instead -const_eval_size_overflow = - overflow computing total size of `{$name}` - -const_eval_stack_frame_limit_reached = - reached the configured maximum number of stack frames - -const_eval_thread_local_access = - thread-local statics cannot be accessed at compile-time - -const_eval_thread_local_static = - cannot access thread local static `{$did}` -const_eval_too_generic = - encountered overly generic constant -const_eval_too_many_caller_args = - calling a function with more arguments than it expected - -const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {const_eval_const_context}s - -const_eval_unallowed_heap_allocations = - allocations are not allowed in {const_eval_const_context}s - .label = allocation not allowed in {const_eval_const_context}s - .teach_note = - The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created. - -const_eval_unallowed_inline_asm = - inline assembly is not allowed in {const_eval_const_context}s - -const_eval_unallowed_op_in_const_context = - {$msg} - -const_eval_uninhabited_enum_variant_read = - read discriminant of an uninhabited enum variant -const_eval_uninhabited_enum_variant_written = - writing discriminant of an uninhabited enum variant - -const_eval_unmarked_const_item_exposed = `{$def_path}` cannot be (indirectly) exposed to stable - .help = either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]` -const_eval_unmarked_intrinsic_exposed = intrinsic `{$def_path}` cannot be (indirectly) exposed to stable - .help = mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_intrinsic_const_stable_indirect]` (but this requires team approval) - -const_eval_unreachable = entering unreachable code -const_eval_unreachable_unwind = - unwinding past a stack frame that does not allow unwinding - -const_eval_unsized_local = unsized locals are not supported -const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn -const_eval_unstable_const_trait = `{$def_path}` is not yet stable as a const trait -const_eval_unstable_in_stable_exposed = - const function that might be (indirectly) exposed to stable cannot use `#[feature({$gate})]` - .is_function_call = mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features - .unstable_sugg = if the {$is_function_call2 -> - [true] caller - *[false] function - } is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` - -const_eval_unstable_intrinsic = `{$name}` is not yet stable as a const intrinsic -const_eval_unstable_intrinsic_suggestion = add `#![feature({$feature})]` to the crate attributes to enable - -const_eval_unterminated_c_string = - reading a null-terminated string starting at {$pointer} with no null found before end of allocation - -const_eval_unwind_past_top = - unwinding past the topmost frame of the stack - -## The `front_matter`s here refer to either `const_eval_front_matter_invalid_value` or `const_eval_front_matter_invalid_value_with_path`. -## (We'd love to sort this differently to make that more clear but tidy won't let us...) -const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty} - -const_eval_validation_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance) -const_eval_validation_dangling_box_out_of_bounds = {$front_matter}: encountered a dangling box (going beyond the bounds of its allocation) -const_eval_validation_dangling_box_use_after_free = {$front_matter}: encountered a dangling box (use-after-free) -const_eval_validation_dangling_ref_no_provenance = {$front_matter}: encountered a dangling reference ({$pointer} has no provenance) -const_eval_validation_dangling_ref_out_of_bounds = {$front_matter}: encountered a dangling reference (going beyond the bounds of its allocation) -const_eval_validation_dangling_ref_use_after_free = {$front_matter}: encountered a dangling reference (use-after-free) - -const_eval_validation_expected_bool = expected a boolean -const_eval_validation_expected_box = expected a box -const_eval_validation_expected_char = expected a unicode scalar value -const_eval_validation_expected_enum_tag = expected a valid enum tag -const_eval_validation_expected_float = expected a floating point number -const_eval_validation_expected_fn_ptr = expected a function pointer -const_eval_validation_expected_init_scalar = expected initialized scalar value -const_eval_validation_expected_int = expected an integer -const_eval_validation_expected_raw_ptr = expected a raw pointer -const_eval_validation_expected_ref = expected a reference -const_eval_validation_expected_str = expected a string - -const_eval_validation_failure = - it is undefined behavior to use this value - -const_eval_validation_failure_note = - the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -const_eval_validation_front_matter_invalid_value = constructing invalid value -const_eval_validation_front_matter_invalid_value_with_path = constructing invalid value at {$path} - -const_eval_validation_invalid_bool = {$front_matter}: encountered {$value}, but expected a boolean -const_eval_validation_invalid_box_meta = {$front_matter}: encountered invalid box metadata: total size is bigger than largest supported object -const_eval_validation_invalid_box_slice_meta = {$front_matter}: encountered invalid box metadata: slice is bigger than largest supported object -const_eval_validation_invalid_char = {$front_matter}: encountered {$value}, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) - -const_eval_validation_invalid_enum_tag = {$front_matter}: encountered {$value}, but expected a valid enum tag -const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, but expected a function pointer -const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object -const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object -const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer -const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$expected_dyn_type}`, but encountered `{$vtable_dyn_type}` -const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory -const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` -const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero -const_eval_validation_null_box = {$front_matter}: encountered a {$maybe -> - [true] maybe-null - *[false] null - } box -const_eval_validation_null_fn_ptr = {$front_matter}: encountered a {$maybe -> - [true] maybe-null - *[false] null - } function pointer -const_eval_validation_null_ref = {$front_matter}: encountered a {$maybe -> - [true] maybe-null - *[false] null - } reference -const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range} -const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers -const_eval_validation_pointer_as_int = {$front_matter}: encountered a pointer, but {$expected} -const_eval_validation_ptr_out_of_range = {$front_matter}: encountered a pointer with unknown absolute address, but expected something that is definitely {$in_range} -const_eval_validation_ref_to_uninhabited = {$front_matter}: encountered a reference pointing to uninhabited type {$ty} -const_eval_validation_unaligned_box = {$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes}) -const_eval_validation_unaligned_ref = {$front_matter}: encountered an unaligned reference (required {$required_bytes} byte alignment but found {$found_bytes}) -const_eval_validation_uninhabited_enum_variant = {$front_matter}: encountered an uninhabited enum variant -const_eval_validation_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}` -const_eval_validation_uninit = {$front_matter}: encountered uninitialized memory, but {$expected} -const_eval_validation_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in read-only memory - -const_eval_write_through_immutable_pointer = - writing through a pointer that was derived from a shared (immutable) reference - -const_eval_write_to_read_only = - writing to {$allocation} which is read-only diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index a9366bf29fbe..ecf52e4aa605 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -2,7 +2,7 @@ use hir::{ConstContext, LangItem}; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, MultiSpan}; +use rustc_errors::{Applicability, Diag, MultiSpan, inline_fluent}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; @@ -23,7 +23,7 @@ use rustc_trait_selection::traits::SelectionContext; use tracing::debug; use super::ConstCx; -use crate::{errors, fluent_generated}; +use crate::errors; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Status { @@ -181,7 +181,9 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { ); if let ConstContext::Static(_) = ccx.const_kind() { - err.note(fluent_generated::const_eval_lazy_lock); + err.note(inline_fluent!( + "consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`" + )); } err diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index fe3891d0dd7e..462254f064cf 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -345,7 +345,7 @@ where let uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Param(_) | ty::ConstKind::Error(_) => None, - // Unevaluated consts in MIR bodies don't have associated MIR (e.g. `#[type_const]`). + // Unevaluated consts in MIR bodies don't have associated MIR (e.g. `type const`). ty::ConstKind::Unevaluated(_) => None, // FIXME(mgca): Investigate whether using `None` for `ConstKind::Value` is overly // strict, and if instead we should be doing some kind of value-based analysis. diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 69a8f163ca93..e87d2cbb20de 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -43,21 +43,41 @@ pub enum ConstEvalErrKind { impl MachineStopType for ConstEvalErrKind { fn diagnostic_message(&self) -> DiagMessage { use ConstEvalErrKind::*; + use rustc_errors::inline_fluent; - use crate::fluent_generated::*; match self { - ConstAccessesMutGlobal => const_eval_const_accesses_mut_global, - ModifiedGlobal => const_eval_modified_global, - Panic { .. } => const_eval_panic, - RecursiveStatic => const_eval_recursive_static, - AssertFailure(x) => x.diagnostic_message(), - WriteThroughImmutablePointer => const_eval_write_through_immutable_pointer, - ConstMakeGlobalPtrAlreadyMadeGlobal { .. } => { - const_eval_const_make_global_ptr_already_made_global + ConstAccessesMutGlobal => "constant accesses mutable global memory".into(), + ModifiedGlobal => { + "modifying a static's initial value from another static's initializer".into() + } + Panic { .. } => inline_fluent!("evaluation panicked: {$msg}"), + RecursiveStatic => { + "encountered static that tried to access itself during initialization".into() + } + AssertFailure(x) => x.diagnostic_message(), + WriteThroughImmutablePointer => { + inline_fluent!( + "writing through a pointer that was derived from a shared (immutable) reference" + ) + } + ConstMakeGlobalPtrAlreadyMadeGlobal { .. } => { + inline_fluent!( + "attempting to call `const_make_global` twice on the same allocation {$alloc}" + ) + } + ConstMakeGlobalPtrIsNonHeap(_) => { + inline_fluent!( + "pointer passed to `const_make_global` does not point to a heap allocation: {$ptr}" + ) + } + ConstMakeGlobalWithDanglingPtr(_) => { + inline_fluent!("pointer passed to `const_make_global` is dangling: {$ptr}") + } + ConstMakeGlobalWithOffset(_) => { + inline_fluent!( + "making {$ptr} global which does not point to the beginning of an object" + ) } - ConstMakeGlobalPtrIsNonHeap(_) => const_eval_const_make_global_ptr_is_non_heap, - ConstMakeGlobalWithDanglingPtr(_) => const_eval_const_make_global_with_dangling_ptr, - ConstMakeGlobalWithOffset(_) => const_eval_const_make_global_with_offset, } } fn add_args(self: Box, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)) { diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 5383ab3547af..9f39750d8125 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -2,7 +2,7 @@ use std::sync::atomic::Ordering::Relaxed; use either::{Left, Right}; use rustc_abi::{self as abi, BackendRepr}; -use rustc_errors::E0080; +use rustc_errors::{E0080, inline_fluent}; use rustc_hir::def::DefKind; use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo, ReportedErrorInfo}; use rustc_middle::mir::{self, ConstAlloc, ConstValue}; @@ -394,7 +394,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( typing_env: ty::TypingEnv<'tcx>, ) -> Result { let def = cid.instance.def.def_id(); - // #[type_const] don't have bodys + // `type const` don't have bodys debug_assert!(!tcx.is_type_const(def), "CTFE tried to evaluate type-const: {:?}", def); let is_static = tcx.is_static(def); @@ -467,7 +467,15 @@ fn report_eval_error<'tcx>( let num_frames = frames.len(); // FIXME(oli-obk): figure out how to use structured diagnostics again. diag.code(E0080); - diag.span_label(span, crate::fluent_generated::const_eval_error); + diag.span_label( + span, + inline_fluent!( + "evaluation of `{$instance}` failed {$num_frames -> + [0] here + *[other] inside this call +}" + ), + ); for frame in frames { diag.subdiagnostic(frame); } @@ -506,8 +514,8 @@ fn report_validation_error<'tcx>( move |diag, span, frames| { // FIXME(oli-obk): figure out how to use structured diagnostics again. diag.code(E0080); - diag.span_label(span, crate::fluent_generated::const_eval_validation_failure); - diag.note(crate::fluent_generated::const_eval_validation_failure_note); + diag.span_label(span, "it is undefined behavior to use this value"); + diag.note("the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior."); for frame in frames { diag.subdiagnostic(frame); } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 719187b99012..da4f97db1c59 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -5,6 +5,7 @@ use std::hash::Hash; use rustc_abi::{Align, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; +use rustc_errors::inline_fluent; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem}; use rustc_middle::mir::AssertMessage; @@ -19,7 +20,6 @@ use tracing::debug; use super::error::*; use crate::errors::{LongRunning, LongRunningWarn}; -use crate::fluent_generated as fluent; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, RangeSet, Scalar, @@ -489,7 +489,13 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { let align = match Align::from_bytes(align) { Ok(a) => a, Err(err) => throw_ub_custom!( - fluent::const_eval_invalid_align_details, + inline_fluent!( + "invalid align passed to `{$name}`: {$align} is {$err_kind -> + [not_power_of_two] not a power of 2 + [too_large] too large + *[other] {\"\"} +}" + ), name = "const_allocate", err_kind = err.diag_ident(), align = err.align() @@ -513,7 +519,13 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { let align = match Align::from_bytes(align) { Ok(a) => a, Err(err) => throw_ub_custom!( - fluent::const_eval_invalid_align_details, + inline_fluent!( + "invalid align passed to `{$name}`: {$align} is {$err_kind -> + [not_power_of_two] not a power of 2 + [too_large] too large + *[other] {\"\"} +}" + ), name = "const_deallocate", err_kind = err.diag_ident(), align = err.align() diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index f8881f0968bb..0fd70d784d4f 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -1,4 +1,8 @@ -use rustc_abi::FieldIdx; +mod adt; + +use std::borrow::Cow; + +use rustc_abi::{FieldIdx, VariantIdx}; use rustc_ast::Mutability; use rustc_hir::LangItem; use rustc_middle::span_bug; @@ -8,11 +12,58 @@ use rustc_span::{Symbol, sym}; use crate::const_eval::CompileTimeMachine; use crate::interpret::{ - CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Scalar, Writeable, - interp_ok, + CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Projectable, Scalar, + Writeable, interp_ok, }; impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { + /// Equivalent to `project_downcast`, but identifies the variant by name instead of index. + fn downcast<'a>( + &self, + place: &(impl Writeable<'tcx, CtfeProvenance> + 'a), + name: Symbol, + ) -> InterpResult<'tcx, (VariantIdx, impl Writeable<'tcx, CtfeProvenance> + 'a)> { + let variants = place.layout().ty.ty_adt_def().unwrap().variants(); + let variant_idx = variants + .iter_enumerated() + .find(|(_idx, var)| var.name == name) + .unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}")) + .0; + + interp_ok((variant_idx, self.project_downcast(place, variant_idx)?)) + } + + // A general method to write an array to a static slice place. + fn allocate_fill_and_write_slice_ptr( + &mut self, + slice_place: impl Writeable<'tcx, CtfeProvenance>, + len: u64, + writer: impl Fn(&mut Self, /* index */ u64, MPlaceTy<'tcx>) -> InterpResult<'tcx>, + ) -> InterpResult<'tcx> { + // Array element type + let field_ty = slice_place + .layout() + .ty + .builtin_deref(false) + .unwrap() + .sequence_element_type(self.tcx.tcx); + + // Allocate an array + let array_layout = self.layout_of(Ty::new_array(self.tcx.tcx, field_ty, len))?; + let array_place = self.allocate(array_layout, MemoryKind::Stack)?; + + // Fill the array fields + let mut field_places = self.project_array_fields(&array_place)?; + while let Some((i, place)) = field_places.next(self)? { + writer(self, i, place)?; + } + + // Write the slice pointing to the array + let array_place = array_place.map_provenance(CtfeProvenance::as_immutable); + let ptr = Immediate::new_slice(array_place.ptr(), len, self); + self.write_immediate(ptr, &slice_place) + } + /// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place. pub(crate) fn write_type_info( &mut self, @@ -26,22 +77,13 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { // Fill all fields of the `TypeInfo` struct. for (idx, field) in ty_struct.fields.iter_enumerated() { let field_dest = self.project_field(dest, idx)?; - let downcast = |name: Symbol| { - let variants = field_dest.layout().ty.ty_adt_def().unwrap().variants(); - let variant_id = variants - .iter_enumerated() - .find(|(_idx, var)| var.name == name) - .unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}")) - .0; - - interp_ok((variant_id, self.project_downcast(&field_dest, variant_id)?)) - }; let ptr_bit_width = || self.tcx.data_layout.pointer_size().bits(); match field.name { sym::kind => { let variant_index = match ty.kind() { ty::Tuple(fields) => { - let (variant, variant_place) = downcast(sym::Tuple)?; + let (variant, variant_place) = + self.downcast(&field_dest, sym::Tuple)?; // project to the single tuple variant field of `type_info::Tuple` struct type let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?; assert_eq!( @@ -55,11 +97,12 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { .fields .len() ); - self.write_tuple_fields(tuple_place, fields, ty)?; + self.write_tuple_type_info(tuple_place, fields, ty)?; variant } ty::Array(ty, len) => { - let (variant, variant_place) = downcast(sym::Array)?; + let (variant, variant_place) = + self.downcast(&field_dest, sym::Array)?; let array_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_array_type_info(array_place, *ty, *len)?; @@ -67,23 +110,29 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { variant } ty::Slice(ty) => { - let (variant, variant_place) = downcast(sym::Slice)?; + let (variant, variant_place) = + self.downcast(&field_dest, sym::Slice)?; let slice_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_slice_type_info(slice_place, *ty)?; variant } + ty::Adt(adt_def, generics) => { + self.write_adt_type_info(&field_dest, (ty, *adt_def), generics)? + } ty::Bool => { - let (variant, _variant_place) = downcast(sym::Bool)?; + let (variant, _variant_place) = + self.downcast(&field_dest, sym::Bool)?; variant } ty::Char => { - let (variant, _variant_place) = downcast(sym::Char)?; + let (variant, _variant_place) = + self.downcast(&field_dest, sym::Char)?; variant } ty::Int(int_ty) => { - let (variant, variant_place) = downcast(sym::Int)?; + let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_int_type_info( place, @@ -93,7 +142,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { variant } ty::Uint(uint_ty) => { - let (variant, variant_place) = downcast(sym::Int)?; + let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_int_type_info( place, @@ -103,17 +152,19 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { variant } ty::Float(float_ty) => { - let (variant, variant_place) = downcast(sym::Float)?; + let (variant, variant_place) = + self.downcast(&field_dest, sym::Float)?; let place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_float_type_info(place, float_ty.bit_width())?; variant } ty::Str => { - let (variant, _variant_place) = downcast(sym::Str)?; + let (variant, _variant_place) = self.downcast(&field_dest, sym::Str)?; variant } ty::Ref(_, ty, mutability) => { - let (variant, variant_place) = downcast(sym::Reference)?; + let (variant, variant_place) = + self.downcast(&field_dest, sym::Reference)?; let reference_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_reference_type_info(reference_place, *ty, *mutability)?; @@ -121,7 +172,8 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { variant } ty::RawPtr(ty, mutability) => { - let (variant, variant_place) = downcast(sym::Pointer)?; + let (variant, variant_place) = + self.downcast(&field_dest, sym::Pointer)?; let pointer_place = self.project_field(&variant_place, FieldIdx::ZERO)?; @@ -130,13 +182,13 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { variant } ty::Dynamic(predicates, region) => { - let (variant, variant_place) = downcast(sym::DynTrait)?; + let (variant, variant_place) = + self.downcast(&field_dest, sym::DynTrait)?; let dyn_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?; variant } - ty::Adt(_, _) - | ty::Foreign(_) + ty::Foreign(_) | ty::Pat(_, _) | ty::FnDef(..) | ty::FnPtr(..) @@ -151,14 +203,14 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(..) - | ty::Error(_) => downcast(sym::Other)?.0, + | ty::Error(_) => self.downcast(&field_dest, sym::Other)?.0, }; self.write_discriminant(variant_index, &field_dest)? } sym::size => { let layout = self.layout_of(ty)?; let variant_index = if layout.is_sized() { - let (variant, variant_place) = downcast(sym::Some)?; + let (variant, variant_place) = self.downcast(&field_dest, sym::Some)?; let size_field_place = self.project_field(&variant_place, FieldIdx::ZERO)?; self.write_scalar( @@ -168,7 +220,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { )?; variant } else { - downcast(sym::None)?.0 + self.downcast(&field_dest, sym::None)?.0 }; self.write_discriminant(variant_index, &field_dest)?; } @@ -179,46 +231,12 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { interp_ok(()) } - pub(crate) fn write_tuple_fields( - &mut self, - tuple_place: impl Writeable<'tcx, CtfeProvenance>, - fields: &[Ty<'tcx>], - tuple_ty: Ty<'tcx>, - ) -> InterpResult<'tcx> { - // project into the `type_info::Tuple::fields` field - let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?; - // get the `type_info::Field` type from `fields: &[Field]` - let field_type = fields_slice_place - .layout() - .ty - .builtin_deref(false) - .unwrap() - .sequence_element_type(self.tcx.tcx); - // Create an array with as many elements as the number of fields in the inspected tuple - let fields_layout = - self.layout_of(Ty::new_array(self.tcx.tcx, field_type, fields.len() as u64))?; - let fields_place = self.allocate(fields_layout, MemoryKind::Stack)?; - let mut fields_places = self.project_array_fields(&fields_place)?; - - let tuple_layout = self.layout_of(tuple_ty)?; - - while let Some((i, place)) = fields_places.next(self)? { - let field_ty = fields[i as usize]; - self.write_field(field_ty, place, tuple_layout, i)?; - } - - let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable); - - let ptr = Immediate::new_slice(fields_place.ptr(), fields.len() as u64, self); - - self.write_immediate(ptr, &fields_slice_place) - } - fn write_field( &mut self, field_ty: Ty<'tcx>, place: MPlaceTy<'tcx>, layout: TyAndLayout<'tcx>, + name: Option, idx: u64, ) -> InterpResult<'tcx> { for (field_idx, field_ty_field) in @@ -226,7 +244,19 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { { let field_place = self.project_field(&place, field_idx)?; match field_ty_field.name { - sym::ty => self.write_type_id(field_ty, &field_place)?, + sym::name => { + let name = match name.as_ref() { + Some(name) => Cow::Borrowed(name.as_str()), + None => Cow::Owned(idx.to_string()), // For tuples + }; + let name_place = self.allocate_str_dedup(&name)?; + let ptr = self.mplace_to_ref(&name_place)?; + self.write_immediate(*ptr, &field_place)? + } + sym::ty => { + let field_ty = self.tcx.erase_and_anonymize_regions(field_ty); + self.write_type_id(field_ty, &field_place)? + } sym::offset => { let offset = layout.fields.offset(idx as usize); self.write_scalar( @@ -242,6 +272,24 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { interp_ok(()) } + pub(crate) fn write_tuple_type_info( + &mut self, + tuple_place: impl Writeable<'tcx, CtfeProvenance>, + fields: &[Ty<'tcx>], + tuple_ty: Ty<'tcx>, + ) -> InterpResult<'tcx> { + let tuple_layout = self.layout_of(tuple_ty)?; + let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?; + self.allocate_fill_and_write_slice_ptr( + fields_slice_place, + fields.len() as u64, + |this, i, place| { + let field_ty = fields[i as usize]; + this.write_field(field_ty, place, tuple_layout, None, i) + }, + ) + } + pub(crate) fn write_array_type_info( &mut self, place: impl Writeable<'tcx, CtfeProvenance>, diff --git a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs new file mode 100644 index 000000000000..60f7b95e799a --- /dev/null +++ b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs @@ -0,0 +1,276 @@ +use rustc_abi::{FieldIdx, VariantIdx}; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::{ + AdtDef, AdtKind, Const, ConstKind, GenericArgKind, GenericArgs, Region, Ty, VariantDef, +}; +use rustc_middle::{bug, span_bug}; +use rustc_span::sym; + +use crate::const_eval::CompileTimeMachine; +use crate::interpret::{ + CtfeProvenance, InterpCx, InterpResult, MPlaceTy, Projectable, Scalar, Writeable, interp_ok, +}; + +impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { + // FIXME(type_info): No semver considerations for now + pub(crate) fn write_adt_type_info( + &mut self, + place: &impl Writeable<'tcx, CtfeProvenance>, + adt: (Ty<'tcx>, AdtDef<'tcx>), + generics: &'tcx GenericArgs<'tcx>, + ) -> InterpResult<'tcx, VariantIdx> { + let (adt_ty, adt_def) = adt; + let variant_idx = match adt_def.adt_kind() { + AdtKind::Struct => { + let (variant, variant_place) = self.downcast(place, sym::Struct)?; + let place = self.project_field(&variant_place, FieldIdx::ZERO)?; + self.write_struct_type_info( + place, + (adt_ty, adt_def.variant(VariantIdx::ZERO)), + generics, + )?; + variant + } + AdtKind::Union => { + let (variant, variant_place) = self.downcast(place, sym::Union)?; + let place = self.project_field(&variant_place, FieldIdx::ZERO)?; + self.write_union_type_info( + place, + (adt_ty, adt_def.variant(VariantIdx::ZERO)), + generics, + )?; + variant + } + AdtKind::Enum => { + let (variant, variant_place) = self.downcast(place, sym::Enum)?; + let place = self.project_field(&variant_place, FieldIdx::ZERO)?; + self.write_enum_type_info(place, adt, generics)?; + variant + } + }; + interp_ok(variant_idx) + } + + pub(crate) fn write_struct_type_info( + &mut self, + place: impl Writeable<'tcx, CtfeProvenance>, + struct_: (Ty<'tcx>, &'tcx VariantDef), + generics: &'tcx GenericArgs<'tcx>, + ) -> InterpResult<'tcx> { + let (struct_ty, struct_def) = struct_; + let struct_layout = self.layout_of(struct_ty)?; + + for (field_idx, field) in + place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() + { + let field_place = self.project_field(&place, field_idx)?; + + match field.name { + sym::generics => self.write_generics(field_place, generics)?, + sym::fields => { + self.write_variant_fields(field_place, struct_def, struct_layout, generics)? + } + sym::non_exhaustive => { + let is_non_exhaustive = struct_def.is_field_list_non_exhaustive(); + self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)? + } + other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), + } + } + + interp_ok(()) + } + + pub(crate) fn write_union_type_info( + &mut self, + place: impl Writeable<'tcx, CtfeProvenance>, + union_: (Ty<'tcx>, &'tcx VariantDef), + generics: &'tcx GenericArgs<'tcx>, + ) -> InterpResult<'tcx> { + let (union_ty, union_def) = union_; + let union_layout = self.layout_of(union_ty)?; + + for (field_idx, field) in + place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() + { + let field_place = self.project_field(&place, field_idx)?; + + match field.name { + sym::generics => self.write_generics(field_place, generics)?, + sym::fields => { + self.write_variant_fields(field_place, union_def, union_layout, generics)? + } + sym::non_exhaustive => { + let is_non_exhaustive = union_def.is_field_list_non_exhaustive(); + self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)? + } + other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), + } + } + + interp_ok(()) + } + + pub(crate) fn write_enum_type_info( + &mut self, + place: impl Writeable<'tcx, CtfeProvenance>, + enum_: (Ty<'tcx>, AdtDef<'tcx>), + generics: &'tcx GenericArgs<'tcx>, + ) -> InterpResult<'tcx> { + let (enum_ty, enum_def) = enum_; + let enum_layout = self.layout_of(enum_ty)?; + + for (field_idx, field) in + place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() + { + let field_place = self.project_field(&place, field_idx)?; + + match field.name { + sym::generics => self.write_generics(field_place, generics)?, + sym::variants => { + self.allocate_fill_and_write_slice_ptr( + field_place, + enum_def.variants().len() as u64, + |this, i, place| { + let variant_idx = VariantIdx::from_usize(i as usize); + let variant_def = &enum_def.variants()[variant_idx]; + let variant_layout = enum_layout.for_variant(this, variant_idx); + this.write_enum_variant(place, (variant_layout, &variant_def), generics) + }, + )?; + } + sym::non_exhaustive => { + let is_non_exhaustive = enum_def.is_variant_list_non_exhaustive(); + self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)? + } + other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), + } + } + + interp_ok(()) + } + + fn write_enum_variant( + &mut self, + place: impl Writeable<'tcx, CtfeProvenance>, + variant: (TyAndLayout<'tcx>, &'tcx VariantDef), + generics: &'tcx GenericArgs<'tcx>, + ) -> InterpResult<'tcx> { + let (variant_layout, variant_def) = variant; + + for (field_idx, field_def) in + place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() + { + let field_place = self.project_field(&place, field_idx)?; + match field_def.name { + sym::name => { + let name_place = self.allocate_str_dedup(variant_def.name.as_str())?; + let ptr = self.mplace_to_ref(&name_place)?; + self.write_immediate(*ptr, &field_place)? + } + sym::fields => { + self.write_variant_fields(field_place, &variant_def, variant_layout, generics)? + } + sym::non_exhaustive => { + let is_non_exhaustive = variant_def.is_field_list_non_exhaustive(); + self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)? + } + other => span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"), + } + } + interp_ok(()) + } + + // Write fields for struct, enum variants + fn write_variant_fields( + &mut self, + place: impl Writeable<'tcx, CtfeProvenance>, + variant_def: &'tcx VariantDef, + variant_layout: TyAndLayout<'tcx>, + generics: &'tcx GenericArgs<'tcx>, + ) -> InterpResult<'tcx> { + self.allocate_fill_and_write_slice_ptr( + place, + variant_def.fields.len() as u64, + |this, i, place| { + let field_def = &variant_def.fields[FieldIdx::from_usize(i as usize)]; + let field_ty = field_def.ty(*this.tcx, generics); + this.write_field(field_ty, place, variant_layout, Some(field_def.name), i) + }, + ) + } + + fn write_generics( + &mut self, + place: impl Writeable<'tcx, CtfeProvenance>, + generics: &'tcx GenericArgs<'tcx>, + ) -> InterpResult<'tcx> { + self.allocate_fill_and_write_slice_ptr(place, generics.len() as u64, |this, i, place| { + match generics[i as usize].kind() { + GenericArgKind::Lifetime(region) => this.write_generic_lifetime(region, place), + GenericArgKind::Type(ty) => this.write_generic_type(ty, place), + GenericArgKind::Const(c) => this.write_generic_const(c, place), + } + }) + } + + fn write_generic_lifetime( + &mut self, + _region: Region<'tcx>, + place: MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let (variant_idx, _) = self.downcast(&place, sym::Lifetime)?; + self.write_discriminant(variant_idx, &place)?; + interp_ok(()) + } + + fn write_generic_type(&mut self, ty: Ty<'tcx>, place: MPlaceTy<'tcx>) -> InterpResult<'tcx> { + let (variant_idx, variant_place) = self.downcast(&place, sym::Type)?; + let generic_type_place = self.project_field(&variant_place, FieldIdx::ZERO)?; + + for (field_idx, field_def) in generic_type_place + .layout() + .ty + .ty_adt_def() + .unwrap() + .non_enum_variant() + .fields + .iter_enumerated() + { + let field_place = self.project_field(&generic_type_place, field_idx)?; + match field_def.name { + sym::ty => self.write_type_id(ty, &field_place)?, + other => span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"), + } + } + + self.write_discriminant(variant_idx, &place)?; + interp_ok(()) + } + + fn write_generic_const(&mut self, c: Const<'tcx>, place: MPlaceTy<'tcx>) -> InterpResult<'tcx> { + let ConstKind::Value(c) = c.kind() else { bug!("expected a computed const, got {c:?}") }; + + let (variant_idx, variant_place) = self.downcast(&place, sym::Const)?; + let const_place = self.project_field(&variant_place, FieldIdx::ZERO)?; + + for (field_idx, field_def) in const_place + .layout() + .ty + .ty_adt_def() + .unwrap() + .non_enum_variant() + .fields + .iter_enumerated() + { + let field_place = self.project_field(&const_place, field_idx)?; + match field_def.name { + sym::ty => self.write_type_id(c.ty, &field_place)?, + other => span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"), + } + } + + self.write_discriminant(variant_idx, &place)?; + interp_ok(()) + } +} diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 50f5448ec20a..a673e0cb1efb 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -5,7 +5,8 @@ use either::Either; use rustc_abi::WrappingRange; use rustc_errors::codes::*; use rustc_errors::{ - Diag, DiagArgValue, DiagMessage, Diagnostic, EmissionGuarantee, Level, MultiSpan, Subdiagnostic, + Diag, DiagArgValue, DiagMessage, Diagnostic, EmissionGuarantee, Level, MultiSpan, + Subdiagnostic, inline_fluent, }; use rustc_hir::ConstContext; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; @@ -17,11 +18,18 @@ use rustc_middle::mir::interpret::{ use rustc_middle::ty::{self, Mutability, Ty}; use rustc_span::{Span, Symbol}; -use crate::fluent_generated as fluent; use crate::interpret::InternKind; #[derive(Diagnostic)] -#[diag(const_eval_dangling_ptr_in_final)] +#[diag( + r#"encountered dangling pointer in final value of {$kind -> + [static] static + [static_mut] mutable static + [const] constant + [promoted] promoted + *[other] {""} +}"# +)] pub(crate) struct DanglingPtrInFinal { #[primary_span] pub span: Span, @@ -29,14 +37,24 @@ pub(crate) struct DanglingPtrInFinal { } #[derive(Diagnostic)] -#[diag(const_eval_nested_static_in_thread_local)] +#[diag( + "#[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead" +)] pub(crate) struct NestedStaticInThreadLocal { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(const_eval_mutable_ptr_in_final)] +#[diag( + r#"encountered mutable pointer in final value of {$kind -> + [static] static + [static_mut] mutable static + [const] constant + [promoted] promoted + *[other] {""} +}"# +)] pub(crate) struct MutablePtrInFinal { #[primary_span] pub span: Span, @@ -44,16 +62,28 @@ pub(crate) struct MutablePtrInFinal { } #[derive(Diagnostic)] -#[diag(const_eval_const_heap_ptr_in_final)] -#[note] +#[diag("encountered `const_allocate` pointer in final value that was not made global")] +#[note( + "use `const_make_global` to turn allocated pointers into immutable globals before returning" +)] pub(crate) struct ConstHeapPtrInFinal { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(const_eval_partial_pointer_in_final)] -#[note] +#[diag( + r#"encountered partial pointer in final value of {$kind -> + [static] static + [static_mut] mutable static + [const] constant + [promoted] promoted + *[other] {""} +}"# +)] +#[note( + "while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value" +)] pub(crate) struct PartialPtrInFinal { #[primary_span] pub span: Span, @@ -61,17 +91,24 @@ pub(crate) struct PartialPtrInFinal { } #[derive(Diagnostic)] -#[diag(const_eval_unstable_in_stable_exposed)] +#[diag( + "const function that might be (indirectly) exposed to stable cannot use `#[feature({$gate})]`" +)] pub(crate) struct UnstableInStableExposed { pub gate: String, #[primary_span] pub span: Span, - #[help(const_eval_is_function_call)] + #[help( + "mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features" + )] pub is_function_call: bool, /// Need to duplicate the field so that fluent also provides it as a variable... pub is_function_call2: bool, #[suggestion( - const_eval_unstable_sugg, + "if the {$is_function_call2 -> + [true] caller + *[false] function + } is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]`", code = "#[rustc_const_unstable(feature = \"...\", issue = \"...\")]\n", applicability = "has-placeholders" )] @@ -79,38 +116,47 @@ pub(crate) struct UnstableInStableExposed { } #[derive(Diagnostic)] -#[diag(const_eval_thread_local_access, code = E0625)] +#[diag("thread-local statics cannot be accessed at compile-time", code = E0625)] pub(crate) struct ThreadLocalAccessErr { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(const_eval_raw_ptr_to_int)] -#[note] -#[note(const_eval_note2)] +#[diag("pointers cannot be cast to integers during const eval")] +#[note("at compile-time, pointers do not have an integer value")] +#[note( + "avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior" +)] pub(crate) struct RawPtrToIntErr { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(const_eval_raw_ptr_comparison)] -#[note] +#[diag("pointers cannot be reliably compared during const eval")] +#[note("see issue #53020 for more information")] pub(crate) struct RawPtrComparisonErr { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(const_eval_panic_non_str)] +#[diag("argument to `panic!()` in a const context must have type `&str`")] pub(crate) struct PanicNonStrErr { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(const_eval_unallowed_fn_pointer_call)] +#[diag( + r#"function pointer calls are not allowed in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"# +)] pub(crate) struct UnallowedFnPointerCall { #[primary_span] pub span: Span, @@ -118,7 +164,7 @@ pub(crate) struct UnallowedFnPointerCall { } #[derive(Diagnostic)] -#[diag(const_eval_unstable_const_fn)] +#[diag("`{$def_path}` is not yet stable as a const fn")] pub(crate) struct UnstableConstFn { #[primary_span] pub span: Span, @@ -126,7 +172,7 @@ pub(crate) struct UnstableConstFn { } #[derive(Diagnostic)] -#[diag(const_eval_unstable_const_trait)] +#[diag("`{$def_path}` is not yet stable as a const trait")] pub(crate) struct UnstableConstTrait { #[primary_span] pub span: Span, @@ -134,14 +180,14 @@ pub(crate) struct UnstableConstTrait { } #[derive(Diagnostic)] -#[diag(const_eval_unstable_intrinsic)] +#[diag("`{$name}` is not yet stable as a const intrinsic")] pub(crate) struct UnstableIntrinsic { #[primary_span] pub span: Span, pub name: Symbol, pub feature: Symbol, #[suggestion( - const_eval_unstable_intrinsic_suggestion, + "add `#![feature({$feature})]` to the crate attributes to enable", code = "#![feature({feature})]\n", applicability = "machine-applicable" )] @@ -149,8 +195,10 @@ pub(crate) struct UnstableIntrinsic { } #[derive(Diagnostic)] -#[diag(const_eval_unmarked_const_item_exposed)] -#[help] +#[diag("`{$def_path}` cannot be (indirectly) exposed to stable")] +#[help( + "either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]`" +)] pub(crate) struct UnmarkedConstItemExposed { #[primary_span] pub span: Span, @@ -158,8 +206,10 @@ pub(crate) struct UnmarkedConstItemExposed { } #[derive(Diagnostic)] -#[diag(const_eval_unmarked_intrinsic_exposed)] -#[help] +#[diag("intrinsic `{$def_path}` cannot be (indirectly) exposed to stable")] +#[help( + "mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_intrinsic_const_stable_indirect]` (but this requires team approval)" +)] pub(crate) struct UnmarkedIntrinsicExposed { #[primary_span] pub span: Span, @@ -167,19 +217,31 @@ pub(crate) struct UnmarkedIntrinsicExposed { } #[derive(Diagnostic)] -#[diag(const_eval_mutable_borrow_escaping, code = E0764)] -#[note] -#[note(const_eval_note2)] -#[help] +#[diag("mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed", code = E0764)] +#[note( + "temporaries in constants and statics can have their lifetime extended until the end of the program" +)] +#[note("to avoid accidentally creating global mutable state, such temporaries must be immutable")] +#[help( + "if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`" +)] pub(crate) struct MutableBorrowEscaping { #[primary_span] - #[label] + #[label("this mutable borrow refers to such a temporary")] pub span: Span, pub kind: ConstContext, } #[derive(Diagnostic)] -#[diag(const_eval_non_const_fmt_macro_call, code = E0015)] +#[diag( + r#"cannot call {$non_or_conditionally}-const formatting macro in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, + code = E0015, +)] pub(crate) struct NonConstFmtMacroCall { #[primary_span] pub span: Span, @@ -188,7 +250,12 @@ pub(crate) struct NonConstFmtMacroCall { } #[derive(Diagnostic)] -#[diag(const_eval_non_const_fn_call, code = E0015)] +#[diag(r#"cannot call {$non_or_conditionally}-const {$def_descr} `{$def_path_str}` in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0015)] pub(crate) struct NonConstFnCall { #[primary_span] pub span: Span, @@ -199,7 +266,14 @@ pub(crate) struct NonConstFnCall { } #[derive(Diagnostic)] -#[diag(const_eval_non_const_intrinsic)] +#[diag( + r#"cannot call non-const intrinsic `{$name}` in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"# +)] pub(crate) struct NonConstIntrinsic { #[primary_span] pub span: Span, @@ -208,7 +282,7 @@ pub(crate) struct NonConstIntrinsic { } #[derive(Diagnostic)] -#[diag(const_eval_unallowed_op_in_const_context)] +#[diag("{$msg}")] pub(crate) struct UnallowedOpInConstContext { #[primary_span] pub span: Span, @@ -216,18 +290,37 @@ pub(crate) struct UnallowedOpInConstContext { } #[derive(Diagnostic)] -#[diag(const_eval_unallowed_heap_allocations, code = E0010)] +#[diag(r#"allocations are not allowed in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0010)] pub(crate) struct UnallowedHeapAllocations { #[primary_span] - #[label] + #[label( + r#"allocation not allowed in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} + }s"# + )] pub span: Span, pub kind: ConstContext, - #[note(const_eval_teach_note)] + #[note( + "the runtime heap is not yet available at compile-time, so no runtime heap allocations can be created" + )] pub teach: bool, } #[derive(Diagnostic)] -#[diag(const_eval_unallowed_inline_asm, code = E0015)] +#[diag(r#"inline assembly is not allowed in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0015)] pub(crate) struct UnallowedInlineAsm { #[primary_span] pub span: Span, @@ -235,39 +328,46 @@ pub(crate) struct UnallowedInlineAsm { } #[derive(Diagnostic)] -#[diag(const_eval_interior_mutable_borrow_escaping, code = E0492)] -#[note] -#[note(const_eval_note2)] -#[help] +#[diag("interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed", code = E0492)] +#[note( + "temporaries in constants and statics can have their lifetime extended until the end of the program" +)] +#[note("to avoid accidentally creating global mutable state, such temporaries must be immutable")] +#[help( + "if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`" +)] pub(crate) struct InteriorMutableBorrowEscaping { #[primary_span] - #[label] + #[label("this borrow of an interior mutable value refers to such a temporary")] pub span: Span, pub kind: ConstContext, } #[derive(LintDiagnostic)] -#[diag(const_eval_long_running)] -#[note] +#[diag("constant evaluation is taking a long time")] +#[note( + "this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval. + If your compilation actually takes a long time, you can safely allow the lint" +)] pub struct LongRunning { - #[help] + #[help("the constant being evaluated")] pub item_span: Span, } #[derive(Diagnostic)] -#[diag(const_eval_long_running)] +#[diag("constant evaluation is taking a long time")] pub struct LongRunningWarn { #[primary_span] - #[label] + #[label("the const evaluator is currently interpreting this expression")] pub span: Span, - #[help] + #[help("the constant being evaluated")] pub item_span: Span, // Used for evading `-Z deduplicate-diagnostics`. pub force_duplicate: usize, } #[derive(Subdiagnostic)] -#[note(const_eval_non_const_impl)] +#[note("impl defined here, but it is not `const`")] pub(crate) struct NonConstImplNote { #[primary_span] pub span: Span, @@ -289,9 +389,21 @@ impl Subdiagnostic for FrameNote { diag.arg("instance", self.instance); let mut span: MultiSpan = self.span.into(); if self.has_label && !self.span.is_dummy() { - span.push_span_label(self.span, fluent::const_eval_frame_note_last); + span.push_span_label(self.span, inline_fluent!("the failure occurred here")); } - let msg = diag.eagerly_translate(fluent::const_eval_frame_note); + let msg = diag.eagerly_translate(inline_fluent!( + r#"{$times -> + [0] {const_eval_frame_note_inner} + *[other] [... {$times} additional calls {const_eval_frame_note_inner} ...] +} + +const_eval_frame_note_inner = inside {$where_ -> + [closure] closure + [instance] `{$instance}` + *[other] {""} +} +"# + )); diag.remove_arg("times"); diag.remove_arg("where_"); diag.remove_arg("instance"); @@ -300,7 +412,7 @@ impl Subdiagnostic for FrameNote { } #[derive(Subdiagnostic)] -#[note(const_eval_raw_bytes)] +#[note(r#"the raw bytes of the constant (size: {$size}, align: {$align}) {"{"}{$bytes}{"}"}"#)] pub struct RawBytesNote { pub size: u64, pub align: u64, @@ -310,8 +422,15 @@ pub struct RawBytesNote { // FIXME(fee1-dead) do not use stringly typed `ConstContext` #[derive(Diagnostic)] -#[diag(const_eval_non_const_match_eq, code = E0015)] -#[note] +#[diag( + r#"cannot match on `{$ty}` in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"# +)] +#[note("`{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es")] pub struct NonConstMatchEq<'tcx> { #[primary_span] pub span: Span, @@ -321,7 +440,12 @@ pub struct NonConstMatchEq<'tcx> { } #[derive(Diagnostic)] -#[diag(const_eval_non_const_for_loop_into_iter, code = E0015)] +#[diag(r#"cannot use `for` loop on `{$ty}` in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0015)] pub struct NonConstForLoopIntoIter<'tcx> { #[primary_span] pub span: Span, @@ -331,7 +455,12 @@ pub struct NonConstForLoopIntoIter<'tcx> { } #[derive(Diagnostic)] -#[diag(const_eval_non_const_question_branch, code = E0015)] +#[diag(r#"`?` is not allowed on `{$ty}` in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0015)] pub struct NonConstQuestionBranch<'tcx> { #[primary_span] pub span: Span, @@ -341,7 +470,12 @@ pub struct NonConstQuestionBranch<'tcx> { } #[derive(Diagnostic)] -#[diag(const_eval_non_const_question_from_residual, code = E0015)] +#[diag(r#"`?` is not allowed on `{$ty}` in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0015)] pub struct NonConstQuestionFromResidual<'tcx> { #[primary_span] pub span: Span, @@ -351,7 +485,12 @@ pub struct NonConstQuestionFromResidual<'tcx> { } #[derive(Diagnostic)] -#[diag(const_eval_non_const_try_block_from_output, code = E0015)] +#[diag(r#"`try` block cannot convert `{$ty}` to the result in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0015)] pub struct NonConstTryBlockFromOutput<'tcx> { #[primary_span] pub span: Span, @@ -361,7 +500,12 @@ pub struct NonConstTryBlockFromOutput<'tcx> { } #[derive(Diagnostic)] -#[diag(const_eval_non_const_await, code = E0015)] +#[diag(r#"cannot convert `{$ty}` into a future in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0015)] pub struct NonConstAwait<'tcx> { #[primary_span] pub span: Span, @@ -371,7 +515,12 @@ pub struct NonConstAwait<'tcx> { } #[derive(Diagnostic)] -#[diag(const_eval_non_const_closure, code = E0015)] +#[diag(r#"cannot call {$non_or_conditionally}-const closure in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0015)] pub struct NonConstClosure { #[primary_span] pub span: Span, @@ -383,19 +532,33 @@ pub struct NonConstClosure { #[derive(Subdiagnostic)] pub enum NonConstClosureNote { - #[note(const_eval_closure_fndef_not_const)] + #[note("function defined here, but it is not `const`")] FnDef { #[primary_span] span: Span, }, - #[note(const_eval_fn_ptr_call)] + #[note( + r#"function pointers need an RFC before allowed to be called in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} + }s"# + )] FnPtr, - #[note(const_eval_closure_call)] + #[note( + r#"closures need an RFC before allowed to be called in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} + }s"# + )] Closure, } #[derive(Subdiagnostic)] -#[multipart_suggestion(const_eval_consider_dereferencing, applicability = "machine-applicable")] +#[multipart_suggestion("consider dereferencing here", applicability = "machine-applicable")] pub struct ConsiderDereferencing { pub deref: String, #[suggestion_part(code = "{deref}")] @@ -405,7 +568,12 @@ pub struct ConsiderDereferencing { } #[derive(Diagnostic)] -#[diag(const_eval_non_const_operator, code = E0015)] +#[diag(r#"cannot call {$non_or_conditionally}-const operator in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0015)] pub struct NonConstOperator { #[primary_span] pub span: Span, @@ -416,28 +584,40 @@ pub struct NonConstOperator { } #[derive(Diagnostic)] -#[diag(const_eval_non_const_deref_coercion, code = E0015)] -#[note] +#[diag(r#"cannot perform {$non_or_conditionally}-const deref coercion on `{$ty}` in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0015)] +#[note("attempting to deref into `{$target_ty}`")] pub struct NonConstDerefCoercion<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, pub target_ty: Ty<'tcx>, - #[note(const_eval_target_note)] + #[note("deref defined here")] pub deref_target: Option, pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_live_drop, code = E0493)] +#[diag("destructor of `{$dropped_ty}` cannot be evaluated at compile-time", code = E0493)] pub struct LiveDrop<'tcx> { #[primary_span] - #[label] + #[label( + r#"the destructor for this type cannot be evaluated in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} + }s"# + )] pub span: Span, pub kind: ConstContext, pub dropped_ty: Ty<'tcx>, - #[label(const_eval_dropped_at_label)] + #[label("value is dropped here")] pub dropped_at: Span, } @@ -466,51 +646,128 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { fn diagnostic_message(&self) -> DiagMessage { use UndefinedBehaviorInfo::*; - use crate::fluent_generated::*; match self { Ub(msg) => msg.clone().into(), Custom(x) => (x.msg)(), ValidationError(e) => e.diagnostic_message(), - Unreachable => const_eval_unreachable, - BoundsCheckFailed { .. } => const_eval_bounds_check_failed, - DivisionByZero => const_eval_division_by_zero, - RemainderByZero => const_eval_remainder_by_zero, - DivisionOverflow => const_eval_division_overflow, - RemainderOverflow => const_eval_remainder_overflow, - PointerArithOverflow => const_eval_pointer_arithmetic_overflow, - ArithOverflow { .. } => const_eval_overflow_arith, - ShiftOverflow { .. } => const_eval_overflow_shift, - InvalidMeta(InvalidMetaKind::SliceTooBig) => const_eval_invalid_meta_slice, - InvalidMeta(InvalidMetaKind::TooBig) => const_eval_invalid_meta, - UnterminatedCString(_) => const_eval_unterminated_c_string, - PointerUseAfterFree(_, _) => const_eval_pointer_use_after_free, - PointerOutOfBounds { .. } => const_eval_pointer_out_of_bounds, - DanglingIntPointer { addr: 0, .. } => const_eval_dangling_null_pointer, - DanglingIntPointer { .. } => const_eval_dangling_int_pointer, - AlignmentCheckFailed { .. } => const_eval_alignment_check_failed, - WriteToReadOnly(_) => const_eval_write_to_read_only, - DerefFunctionPointer(_) => const_eval_deref_function_pointer, - DerefVTablePointer(_) => const_eval_deref_vtable_pointer, - DerefTypeIdPointer(_) => const_eval_deref_typeid_pointer, - InvalidBool(_) => const_eval_invalid_bool, - InvalidChar(_) => const_eval_invalid_char, - InvalidTag(_) => const_eval_invalid_tag, - InvalidFunctionPointer(_) => const_eval_invalid_function_pointer, - InvalidVTablePointer(_) => const_eval_invalid_vtable_pointer, - InvalidVTableTrait { .. } => const_eval_invalid_vtable_trait, - InvalidStr(_) => const_eval_invalid_str, - InvalidUninitBytes(None) => const_eval_invalid_uninit_bytes_unknown, - InvalidUninitBytes(Some(_)) => const_eval_invalid_uninit_bytes, - DeadLocal => const_eval_dead_local, - ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch, - UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written, - UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read, - InvalidNichedEnumVariantWritten { .. } => { - const_eval_invalid_niched_enum_variant_written + Unreachable => "entering unreachable code".into(), + BoundsCheckFailed { .. } => inline_fluent!("indexing out of bounds: the len is {$len} but the index is {$index}"), + DivisionByZero => "dividing by zero".into(), + RemainderByZero => "calculating the remainder with a divisor of zero".into(), + DivisionOverflow => "overflow in signed division (dividing MIN by -1)".into(), + RemainderOverflow => "overflow in signed remainder (dividing MIN by -1)".into(), + PointerArithOverflow => "overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`".into(), + ArithOverflow { .. } => inline_fluent!("arithmetic overflow in `{$intrinsic}`"), + ShiftOverflow { .. } => inline_fluent!("overflowing shift by {$shift_amount} in `{$intrinsic}`"), + InvalidMeta(InvalidMetaKind::SliceTooBig) => "invalid metadata in wide pointer: slice is bigger than largest supported object".into(), + InvalidMeta(InvalidMetaKind::TooBig) => "invalid metadata in wide pointer: total size is bigger than largest supported object".into(), + UnterminatedCString(_) => "reading a null-terminated string starting at {$pointer} with no null found before end of allocation".into(), + PointerUseAfterFree(_, _) => inline_fluent!("{$operation -> + [MemoryAccess] memory access failed + [InboundsPointerArithmetic] in-bounds pointer arithmetic failed + *[Dereferenceable] pointer not dereferenceable +}: {$alloc_id} has been freed, so this pointer is dangling"), + PointerOutOfBounds { .. } => inline_fluent!("{$operation -> + [MemoryAccess] memory access failed + [InboundsPointerArithmetic] in-bounds pointer arithmetic failed + *[Dereferenceable] pointer not dereferenceable +}: {$operation -> + [MemoryAccess] attempting to access {$inbounds_size -> + [1] 1 byte + *[x] {$inbounds_size} bytes + } + [InboundsPointerArithmetic] attempting to offset pointer by {$inbounds_size -> + [1] 1 byte + *[x] {$inbounds_size} bytes + } + *[Dereferenceable] pointer must {$inbounds_size -> + [0] point to some allocation + [1] be dereferenceable for 1 byte + *[x] be dereferenceable for {$inbounds_size} bytes + } +}, but got {$pointer} which {$ptr_offset_is_neg -> + [true] points to before the beginning of the allocation + *[false] {$inbounds_size_is_neg -> + [false] {$alloc_size_minus_ptr_offset -> + [0] is at or beyond the end of the allocation of size {$alloc_size -> + [1] 1 byte + *[x] {$alloc_size} bytes } - AbiMismatchArgument { .. } => const_eval_incompatible_arg_types, - AbiMismatchReturn { .. } => const_eval_incompatible_return_types, + [1] is only 1 byte from the end of the allocation + *[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation + } + *[true] {$ptr_offset_abs -> + [0] is at the beginning of the allocation + *[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation + } + } +} +"), + DanglingIntPointer { addr: 0, .. } => inline_fluent!("{$operation -> + [MemoryAccess] memory access failed + [InboundsPointerArithmetic] in-bounds pointer arithmetic failed + *[Dereferenceable] pointer not dereferenceable +}: {$operation -> + [MemoryAccess] attempting to access {$inbounds_size -> + [1] 1 byte + *[x] {$inbounds_size} bytes + } + [InboundsPointerArithmetic] attempting to offset pointer by {$inbounds_size -> + [1] 1 byte + *[x] {$inbounds_size} bytes + } + *[Dereferenceable] pointer must {$inbounds_size -> + [0] point to some allocation + [1] be dereferenceable for 1 byte + *[x] be dereferenceable for {$inbounds_size} bytes + } +}, but got null pointer"), + DanglingIntPointer { .. } => inline_fluent!("{$operation -> + [MemoryAccess] memory access failed + [InboundsPointerArithmetic] in-bounds pointer arithmetic failed + *[Dereferenceable] pointer not dereferenceable +}: {$operation -> + [MemoryAccess] attempting to access {$inbounds_size -> + [1] 1 byte + *[x] {$inbounds_size} bytes + } + [InboundsPointerArithmetic] attempting to offset pointer by {$inbounds_size -> + [1] 1 byte + *[x] {$inbounds_size} bytes + } + *[Dereferenceable] pointer must {$inbounds_size -> + [0] point to some allocation + [1] be dereferenceable for 1 byte + *[x] be dereferenceable for {$inbounds_size} bytes + } +}, but got {$pointer} which is a dangling pointer (it has no provenance)"), + AlignmentCheckFailed { .. } => inline_fluent!("{$msg -> + [AccessedPtr] accessing memory + *[other] accessing memory based on pointer +} with alignment {$has}, but alignment {$required} is required"), + WriteToReadOnly(_) => inline_fluent!("writing to {$allocation} which is read-only"), + DerefFunctionPointer(_) => inline_fluent!("accessing {$allocation} which contains a function"), + DerefVTablePointer(_) => inline_fluent!("accessing {$allocation} which contains a vtable"), + DerefTypeIdPointer(_) => inline_fluent!("accessing {$allocation} which contains a `TypeId`"), + InvalidBool(_) => inline_fluent!("interpreting an invalid 8-bit value as a bool: 0x{$value}"), + InvalidChar(_) => inline_fluent!("interpreting an invalid 32-bit value as a char: 0x{$value}"), + InvalidTag(_) => inline_fluent!("enum value has invalid tag: {$tag}"), + InvalidFunctionPointer(_) => inline_fluent!("using {$pointer} as function pointer but it does not point to a function"), + InvalidVTablePointer(_) => inline_fluent!("using {$pointer} as vtable pointer but it does not point to a vtable"), + InvalidVTableTrait { .. } => inline_fluent!("using vtable for `{$vtable_dyn_type}` but `{$expected_dyn_type}` was expected"), + InvalidStr(_) => inline_fluent!("this string is not valid UTF-8: {$err}"), + InvalidUninitBytes(None) => "using uninitialized data, but this operation requires initialized memory".into(), + InvalidUninitBytes(Some(_)) => inline_fluent!("reading memory at {$alloc}{$access}, but memory is uninitialized at {$uninit}, and this operation requires initialized memory"), + DeadLocal => "accessing a dead local variable".into(), + ScalarSizeMismatch(_) => inline_fluent!("scalar size mismatch: expected {$target_size} bytes but got {$data_size} bytes instead"), + UninhabitedEnumVariantWritten(_) => "writing discriminant of an uninhabited enum variant".into(), + UninhabitedEnumVariantRead(_) => "read discriminant of an uninhabited enum variant".into(), + InvalidNichedEnumVariantWritten { .. } => { + inline_fluent!("trying to set discriminant of a {$ty} to the niched variant, but the value does not match") + } + AbiMismatchArgument { .. } => inline_fluent!("calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty}"), + AbiMismatchReturn { .. } => inline_fluent!("calling a function with return type {$callee_ty} passing return place of type {$caller_ty}"), } } @@ -653,92 +910,189 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { fn diagnostic_message(&self) -> DiagMessage { use rustc_middle::mir::interpret::ValidationErrorKind::*; - use crate::fluent_generated::*; match self.kind { PtrToUninhabited { ptr_kind: PointerKind::Box, .. } => { - const_eval_validation_box_to_uninhabited + inline_fluent!( + "{$front_matter}: encountered a box pointing to uninhabited type {$ty}" + ) } PtrToUninhabited { ptr_kind: PointerKind::Ref(_), .. } => { - const_eval_validation_ref_to_uninhabited + inline_fluent!( + "{$front_matter}: encountered a reference pointing to uninhabited type {$ty}" + ) } - PointerAsInt { .. } => const_eval_validation_pointer_as_int, - PartialPointer => const_eval_validation_partial_pointer, - MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable, - NullFnPtr { .. } => const_eval_validation_null_fn_ptr, - NeverVal => const_eval_validation_never_val, - NonnullPtrMaybeNull { .. } => const_eval_validation_nonnull_ptr_out_of_range, - PtrOutOfRange { .. } => const_eval_validation_ptr_out_of_range, - OutOfRange { .. } => const_eval_validation_out_of_range, - UnsafeCellInImmutable => const_eval_validation_unsafe_cell, - UninhabitedVal { .. } => const_eval_validation_uninhabited_val, - InvalidEnumTag { .. } => const_eval_validation_invalid_enum_tag, - UninhabitedEnumVariant => const_eval_validation_uninhabited_enum_variant, - Uninit { .. } => const_eval_validation_uninit, - InvalidVTablePtr { .. } => const_eval_validation_invalid_vtable_ptr, - InvalidMetaWrongTrait { .. } => const_eval_validation_invalid_vtable_trait, + PointerAsInt { .. } => { + inline_fluent!("{$front_matter}: encountered a pointer, but {$expected}") + } + PartialPointer => inline_fluent!( + "{$front_matter}: encountered a partial pointer or a mix of pointers" + ), + MutableRefToImmutable => { + inline_fluent!( + "{$front_matter}: encountered mutable reference or box pointing to read-only memory" + ) + } + NullFnPtr { .. } => { + inline_fluent!( + "{$front_matter}: encountered a {$maybe -> + [true] maybe-null + *[false] null +} function pointer" + ) + } + NeverVal => { + inline_fluent!("{$front_matter}: encountered a value of the never type `!`") + } + NonnullPtrMaybeNull { .. } => { + inline_fluent!( + "{$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero" + ) + } + PtrOutOfRange { .. } => { + inline_fluent!( + "{$front_matter}: encountered a pointer with unknown absolute address, but expected something that is definitely {$in_range}" + ) + } + OutOfRange { .. } => { + inline_fluent!( + "{$front_matter}: encountered {$value}, but expected something {$in_range}" + ) + } + UnsafeCellInImmutable => { + inline_fluent!("{$front_matter}: encountered `UnsafeCell` in read-only memory") + } + UninhabitedVal { .. } => { + inline_fluent!("{$front_matter}: encountered a value of uninhabited type `{$ty}`") + } + InvalidEnumTag { .. } => { + inline_fluent!( + "{$front_matter}: encountered {$value}, but expected a valid enum tag" + ) + } + UninhabitedEnumVariant => { + inline_fluent!("{$front_matter}: encountered an uninhabited enum variant") + } + Uninit { .. } => { + inline_fluent!("{$front_matter}: encountered uninitialized memory, but {$expected}") + } + InvalidVTablePtr { .. } => { + inline_fluent!( + "{$front_matter}: encountered {$value}, but expected a vtable pointer" + ) + } + InvalidMetaWrongTrait { .. } => { + inline_fluent!( + "{$front_matter}: wrong trait in wide pointer vtable: expected `{$expected_dyn_type}`, but encountered `{$vtable_dyn_type}`" + ) + } InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Box } => { - const_eval_validation_invalid_box_slice_meta + inline_fluent!( + "{$front_matter}: encountered invalid box metadata: slice is bigger than largest supported object" + ) } InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Ref(_) } => { - const_eval_validation_invalid_ref_slice_meta + inline_fluent!( + "{$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object" + ) } InvalidMetaTooLarge { ptr_kind: PointerKind::Box } => { - const_eval_validation_invalid_box_meta + inline_fluent!( + "{$front_matter}: encountered invalid box metadata: total size is bigger than largest supported object" + ) } InvalidMetaTooLarge { ptr_kind: PointerKind::Ref(_) } => { - const_eval_validation_invalid_ref_meta + inline_fluent!( + "{$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object" + ) } UnalignedPtr { ptr_kind: PointerKind::Ref(_), .. } => { - const_eval_validation_unaligned_ref + inline_fluent!( + "{$front_matter}: encountered an unaligned reference (required {$required_bytes} byte alignment but found {$found_bytes})" + ) + } + UnalignedPtr { ptr_kind: PointerKind::Box, .. } => { + inline_fluent!( + "{$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes})" + ) } - UnalignedPtr { ptr_kind: PointerKind::Box, .. } => const_eval_validation_unaligned_box, - NullPtr { ptr_kind: PointerKind::Box, .. } => const_eval_validation_null_box, - NullPtr { ptr_kind: PointerKind::Ref(_), .. } => const_eval_validation_null_ref, + NullPtr { ptr_kind: PointerKind::Box, .. } => { + inline_fluent!( + "{$front_matter}: encountered a {$maybe -> + [true] maybe-null + *[false] null +} box" + ) + } + NullPtr { ptr_kind: PointerKind::Ref(_), .. } => { + inline_fluent!( + "{$front_matter}: encountered a {$maybe -> + [true] maybe-null + *[false] null +} reference" + ) + } DanglingPtrNoProvenance { ptr_kind: PointerKind::Box, .. } => { - const_eval_validation_dangling_box_no_provenance + inline_fluent!( + "{$front_matter}: encountered a dangling box ({$pointer} has no provenance)" + ) } DanglingPtrNoProvenance { ptr_kind: PointerKind::Ref(_), .. } => { - const_eval_validation_dangling_ref_no_provenance + inline_fluent!( + "{$front_matter}: encountered a dangling reference ({$pointer} has no provenance)" + ) } DanglingPtrOutOfBounds { ptr_kind: PointerKind::Box } => { - const_eval_validation_dangling_box_out_of_bounds + inline_fluent!( + "{$front_matter}: encountered a dangling box (going beyond the bounds of its allocation)" + ) } DanglingPtrOutOfBounds { ptr_kind: PointerKind::Ref(_) } => { - const_eval_validation_dangling_ref_out_of_bounds + inline_fluent!( + "{$front_matter}: encountered a dangling reference (going beyond the bounds of its allocation)" + ) } DanglingPtrUseAfterFree { ptr_kind: PointerKind::Box } => { - const_eval_validation_dangling_box_use_after_free + inline_fluent!("{$front_matter}: encountered a dangling box (use-after-free)") } DanglingPtrUseAfterFree { ptr_kind: PointerKind::Ref(_) } => { - const_eval_validation_dangling_ref_use_after_free + inline_fluent!("{$front_matter}: encountered a dangling reference (use-after-free)") + } + InvalidBool { .. } => { + inline_fluent!("{$front_matter}: encountered {$value}, but expected a boolean") + } + InvalidChar { .. } => { + inline_fluent!( + "{$front_matter}: encountered {$value}, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)" + ) + } + InvalidFnPtr { .. } => { + inline_fluent!( + "{$front_matter}: encountered {$value}, but expected a function pointer" + ) } - InvalidBool { .. } => const_eval_validation_invalid_bool, - InvalidChar { .. } => const_eval_validation_invalid_char, - InvalidFnPtr { .. } => const_eval_validation_invalid_fn_ptr, } } fn add_args(self, err: &mut Diag<'_, G>) { + use rustc_errors::inline_fluent; use rustc_middle::mir::interpret::ValidationErrorKind::*; - use crate::fluent_generated as fluent; - if let PointerAsInt { .. } | PartialPointer = self.kind { - err.help(fluent::const_eval_ptr_as_bytes_1); - err.help(fluent::const_eval_ptr_as_bytes_2); + err.help(inline_fluent!("this code performed an operation that depends on the underlying bytes representing a pointer")); + err.help(inline_fluent!("the absolute address of a pointer is not known at compile-time, so such operations are not supported")); } let message = if let Some(path) = self.path { err.dcx.eagerly_translate_to_string( - fluent::const_eval_validation_front_matter_invalid_value_with_path, + inline_fluent!("constructing invalid value at {$path}"), [("path".into(), DiagArgValue::Str(path.into()))].iter().map(|(a, b)| (a, b)), ) } else { err.dcx.eagerly_translate_to_string( - fluent::const_eval_validation_front_matter_invalid_value, + inline_fluent!("constructing invalid value"), [].into_iter(), ) }; @@ -753,17 +1107,17 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { let WrappingRange { start: lo, end: hi } = r; assert!(hi <= max_hi); let msg = if lo > hi { - fluent::const_eval_range_wrapping + inline_fluent!("less or equal to {$hi}, or greater or equal to {$lo}") } else if lo == hi { - fluent::const_eval_range_singular + inline_fluent!("equal to {$lo}") } else if lo == 0 { assert!(hi < max_hi, "should not be printing if the range covers everything"); - fluent::const_eval_range_upper + inline_fluent!("less or equal to {$hi}") } else if hi == max_hi { assert!(lo > 0, "should not be printing if the range covers everything"); - fluent::const_eval_range_lower + inline_fluent!("greater or equal to {$lo}") } else { - fluent::const_eval_range + inline_fluent!("in the range {$lo}..={$hi}") }; let args = [ @@ -781,17 +1135,17 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { } PointerAsInt { expected } | Uninit { expected } => { let msg = match expected { - ExpectedKind::Reference => fluent::const_eval_validation_expected_ref, - ExpectedKind::Box => fluent::const_eval_validation_expected_box, - ExpectedKind::RawPtr => fluent::const_eval_validation_expected_raw_ptr, - ExpectedKind::InitScalar => fluent::const_eval_validation_expected_init_scalar, - ExpectedKind::Bool => fluent::const_eval_validation_expected_bool, - ExpectedKind::Char => fluent::const_eval_validation_expected_char, - ExpectedKind::Float => fluent::const_eval_validation_expected_float, - ExpectedKind::Int => fluent::const_eval_validation_expected_int, - ExpectedKind::FnPtr => fluent::const_eval_validation_expected_fn_ptr, - ExpectedKind::EnumTag => fluent::const_eval_validation_expected_enum_tag, - ExpectedKind::Str => fluent::const_eval_validation_expected_str, + ExpectedKind::Reference => inline_fluent!("expected a reference"), + ExpectedKind::Box => inline_fluent!("expected a box"), + ExpectedKind::RawPtr => inline_fluent!("expected a raw pointer"), + ExpectedKind::InitScalar => inline_fluent!("expected initialized scalar value"), + ExpectedKind::Bool => inline_fluent!("expected a boolean"), + ExpectedKind::Char => inline_fluent!("expected a unicode scalar value"), + ExpectedKind::Float => inline_fluent!("expected a floating point number"), + ExpectedKind::Int => inline_fluent!("expected an integer"), + ExpectedKind::FnPtr => inline_fluent!("expected a function pointer"), + ExpectedKind::EnumTag => inline_fluent!("expected a valid enum tag"), + ExpectedKind::Str => inline_fluent!("expected a string"), }; let msg = err.dcx.eagerly_translate_to_string(msg, [].into_iter()); err.arg("expected", msg); @@ -838,25 +1192,32 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { impl ReportErrorExt for UnsupportedOpInfo { fn diagnostic_message(&self) -> DiagMessage { - use crate::fluent_generated::*; match self { UnsupportedOpInfo::Unsupported(s) => s.clone().into(), - UnsupportedOpInfo::ExternTypeField => const_eval_extern_type_field, - UnsupportedOpInfo::UnsizedLocal => const_eval_unsized_local, - UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_read, - UnsupportedOpInfo::ReadPointerAsInt(_) => const_eval_read_pointer_as_int, - UnsupportedOpInfo::ThreadLocalStatic(_) => const_eval_thread_local_static, - UnsupportedOpInfo::ExternStatic(_) => const_eval_extern_static, + UnsupportedOpInfo::ExternTypeField => { + "`extern type` field does not have a known offset".into() + } + UnsupportedOpInfo::UnsizedLocal => "unsized locals are not supported".into(), + UnsupportedOpInfo::ReadPartialPointer(_) => { + inline_fluent!("unable to read parts of a pointer from memory at {$ptr}") + } + UnsupportedOpInfo::ReadPointerAsInt(_) => "unable to turn pointer into integer".into(), + UnsupportedOpInfo::ThreadLocalStatic(_) => { + inline_fluent!("cannot access thread local static `{$did}`") + } + UnsupportedOpInfo::ExternStatic(_) => { + inline_fluent!("cannot access extern static `{$did}`") + } } + .into() } fn add_args(self, diag: &mut Diag<'_, G>) { use UnsupportedOpInfo::*; - use crate::fluent_generated::*; if let ReadPointerAsInt(_) | ReadPartialPointer(_) = self { - diag.help(const_eval_ptr_as_bytes_1); - diag.help(const_eval_ptr_as_bytes_2); + diag.help("this code performed an operation that depends on the underlying bytes representing a pointer"); + diag.help("the absolute address of a pointer is not known at compile-time, so such operations are not supported"); } match self { // `ReadPointerAsInt(Some(info))` is never printed anyway, it only serves as an error to @@ -901,10 +1262,12 @@ impl<'tcx> ReportErrorExt for InterpErrorKind<'tcx> { impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { fn diagnostic_message(&self) -> DiagMessage { - use crate::fluent_generated::*; match self { - InvalidProgramInfo::TooGeneric => const_eval_too_generic, - InvalidProgramInfo::AlreadyReported(_) => const_eval_already_reported, + InvalidProgramInfo::TooGeneric => "encountered overly generic constant".into(), + InvalidProgramInfo::AlreadyReported(_) => { + "an error has already been reported elsewhere (this should not usually be printed)" + .into() + } InvalidProgramInfo::Layout(e) => e.diagnostic_message(), } } @@ -926,13 +1289,19 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { impl ReportErrorExt for ResourceExhaustionInfo { fn diagnostic_message(&self) -> DiagMessage { - use crate::fluent_generated::*; match self { - ResourceExhaustionInfo::StackFrameLimitReached => const_eval_stack_frame_limit_reached, - ResourceExhaustionInfo::MemoryExhausted => const_eval_memory_exhausted, - ResourceExhaustionInfo::AddressSpaceFull => const_eval_address_space_full, - ResourceExhaustionInfo::Interrupted => const_eval_interrupted, + ResourceExhaustionInfo::StackFrameLimitReached => { + "reached the configured maximum number of stack frames" + } + ResourceExhaustionInfo::MemoryExhausted => { + "tried to allocate more memory than available to compiler" + } + ResourceExhaustionInfo::AddressSpaceFull => { + "there are no more free addresses in the address space" + } + ResourceExhaustionInfo::Interrupted => "compilation was interrupted", } + .into() } fn add_args(self, _: &mut Diag<'_, G>) {} } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 9c8ca44c5e8f..ffa7b09205d5 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -1,10 +1,12 @@ //! Manages calling a concrete function (with known MIR body) with argument passing, //! and returning the return value to the caller. + use std::borrow::Cow; use either::{Left, Right}; use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx}; use rustc_data_structures::assert_matches; +use rustc_errors::inline_fluent; use rustc_hir::def_id::DefId; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef}; @@ -19,8 +21,8 @@ use super::{ Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo, interp_ok, throw_ub, throw_ub_custom, throw_unsup_format, }; +use crate::enter_trace_span; use crate::interpret::EnteredTraceSpan; -use crate::{enter_trace_span, fluent_generated as fluent}; /// An argument passed to a function. #[derive(Clone, Debug)] @@ -89,6 +91,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let (_, field) = layout.non_1zst_field(self).unwrap(); self.unfold_transparent(field, may_unfold) } + ty::Pat(base, _) => self.layout_of(*base).expect( + "if the layout of a pattern type could be computed, so can the layout of its base", + ), // Not a transparent type, no further unfolding. _ => layout, } @@ -292,7 +297,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // Find next caller arg. let Some((caller_arg, caller_abi)) = caller_args.next() else { - throw_ub_custom!(fluent::const_eval_not_enough_caller_args); + throw_ub_custom!(inline_fluent!( + "calling a function with fewer arguments than it requires" + )); }; assert_eq!(caller_arg.layout().layout, caller_abi.layout.layout); // Sadly we cannot assert that `caller_arg.layout().ty` and `caller_abi.layout.ty` are @@ -359,7 +366,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if caller_fn_abi.conv != callee_fn_abi.conv { throw_ub_custom!( - fluent::const_eval_incompatible_calling_conventions, + rustc_errors::inline_fluent!( + "calling a function with calling convention \"{$callee_conv}\" using calling convention \"{$caller_conv}\"" + ), callee_conv = format!("{}", callee_fn_abi.conv), caller_conv = format!("{}", caller_fn_abi.conv), ) @@ -490,7 +499,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { "mismatch between callee ABI and callee body arguments" ); if caller_args.next().is_some() { - throw_ub_custom!(fluent::const_eval_too_many_caller_args); + throw_ub_custom!(inline_fluent!( + "calling a function with more arguments than it expected" + )); } // Don't forget to check the return type! if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? { @@ -690,7 +701,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let vtable_entries = self.vtable_entries(receiver_trait.principal(), dyn_ty); let Some(ty::VtblEntry::Method(fn_inst)) = vtable_entries.get(idx).copied() else { // FIXME(fee1-dead) these could be variants of the UB info enum instead of this - throw_ub_custom!(fluent::const_eval_dyn_call_not_a_method); + throw_ub_custom!(inline_fluent!( + "`dyn` call trying to call something that is not a method" + )); }; trace!("Virtual call dispatches to {fn_inst:#?}"); // We can also do the lookup based on `def_id` and `dyn_ty`, and check that that @@ -887,7 +900,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } ); if unwinding && self.frame_idx() == 0 { - throw_ub_custom!(fluent::const_eval_unwind_past_top); + throw_ub_custom!(inline_fluent!("unwinding past the topmost frame of the stack")); } // Get out the return value. Must happen *before* the frame is popped as we have to get the diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 43de2e7f078a..44ca20ab4c03 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -2,6 +2,7 @@ use rustc_abi::{FieldIdx, Integer}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::{Float, FloatConvert}; use rustc_data_structures::assert_matches; +use rustc_errors::inline_fluent; use rustc_middle::mir::CastKind; use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; use rustc_middle::ty::adjustment::PointerCoercion; @@ -15,8 +16,8 @@ use super::{ FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, err_inval, interp_ok, throw_ub, throw_ub_custom, }; +use crate::enter_trace_span; use crate::interpret::Writeable; -use crate::{enter_trace_span, fluent_generated as fluent}; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn cast( @@ -138,7 +139,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert_eq!(cast_ty, dest.layout.ty); // we otherwise ignore `cast_ty` enirely... if src.layout.size != dest.layout.size { throw_ub_custom!( - fluent::const_eval_invalid_transmute, + inline_fluent!( + "transmuting from {$src_bytes}-byte type to {$dest_bytes}-byte type: `{$src}` -> `{$dest}`" + ), src_bytes = src.layout.size.bytes(), dest_bytes = dest.layout.size.bytes(), src = src.layout.ty, diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index b72a50a0bfce..8c9a79abe48d 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1,7 +1,7 @@ use either::{Left, Right}; use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; use rustc_data_structures::debug_assert_matches; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::{DiagCtxtHandle, inline_fluent}; use rustc_hir::def_id::DefId; use rustc_hir::limit::Limit; use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; @@ -23,7 +23,7 @@ use super::{ MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, err_inval, interp_ok, throw_inval, throw_ub, throw_ub_custom, }; -use crate::{ReportErrorExt, enter_trace_span, fluent_generated as fluent, util}; +use crate::{ReportErrorExt, enter_trace_span, util}; pub struct InterpCx<'tcx, M: Machine<'tcx>> { /// Stores the `Machine` instance. @@ -555,7 +555,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { mir::UnwindAction::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }), mir::UnwindAction::Continue => Right(self.frame_mut().body.span), mir::UnwindAction::Unreachable => { - throw_ub_custom!(fluent::const_eval_unreachable_unwind); + throw_ub_custom!(inline_fluent!( + "unwinding past a stack frame that does not allow unwinding" + )); } mir::UnwindAction::Terminate(reason) => { self.frame_mut().loc = Right(self.frame_mut().body.span); diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index e526f6120689..49038315b546 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -7,12 +7,13 @@ mod simd; use rustc_abi::{FIRST_VARIANT, FieldIdx, HasDataLayout, Size, VariantIdx}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_data_structures::assert_matches; +use rustc_errors::inline_fluent; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{FloatTy, PolyExistentialPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{FloatTy, PolyExistentialPredicate, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug, ty}; use rustc_span::{Symbol, sym}; use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; @@ -25,7 +26,6 @@ use super::{ PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format, }; -use crate::fluent_generated as fluent; use crate::interpret::Writeable; #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -73,6 +73,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ty: Ty<'tcx>, dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> { + debug_assert!( + !ty.has_erasable_regions(), + "type {ty:?} has regions that need erasing before writing a TypeId", + ); + let tcx = self.tcx; let type_id_hash = tcx.type_id_hash(ty).as_u128(); let op = self.const_val_to_op( @@ -438,7 +443,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => { // Not into the same allocation -- this is UB. throw_ub_custom!( - fluent::const_eval_offset_from_different_allocations, + inline_fluent!( + "`{$name}` called on two different pointers that are not both derived from the same allocation" + ), name = intrinsic_name, ); } @@ -459,7 +466,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // a < b if intrinsic_name == sym::ptr_offset_from_unsigned { throw_ub_custom!( - fluent::const_eval_offset_from_unsigned_overflow, + inline_fluent!("`ptr_offset_from_unsigned` called when first pointer has smaller {$is_addr -> + [true] address + *[false] offset +} than second: {$a_offset} < {$b_offset}"), a_offset = a_offset, b_offset = b_offset, is_addr = is_addr, @@ -471,7 +481,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let dist = val.to_target_isize(self)?; if dist >= 0 || i128::from(dist) == self.pointer_size().signed_int_min() { throw_ub_custom!( - fluent::const_eval_offset_from_underflow, + inline_fluent!( + "`{$name}` called when first pointer is too far before second" + ), name = intrinsic_name, ); } @@ -483,7 +495,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // because they were more than isize::MAX apart. if dist < 0 { throw_ub_custom!( - fluent::const_eval_offset_from_overflow, + inline_fluent!( + "`{$name}` called when first pointer is too far ahead of second" + ), name = intrinsic_name, ); } @@ -502,12 +516,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { && a_alloc_id == b_alloc_id { err_ub_custom!( - fluent::const_eval_offset_from_out_of_bounds, + inline_fluent!("`{$name}` called on two different pointers where the memory range between them is not in-bounds of an allocation"), name = intrinsic_name, ) } else { err_ub_custom!( - fluent::const_eval_offset_from_different_allocations, + inline_fluent!("`{$name}` called on two different pointers that are not both derived from the same allocation"), name = intrinsic_name, ) } @@ -522,7 +536,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .map_err_kind(|_| { // Make the error more specific. err_ub_custom!( - fluent::const_eval_offset_from_different_allocations, + inline_fluent!("`{$name}` called on two different pointers that are not both derived from the same allocation"), name = intrinsic_name, ) })?; @@ -752,7 +766,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let op = self.eval_operand(op, None)?; let cond = self.read_scalar(&op)?.to_bool()?; if !cond { - throw_ub_custom!(fluent::const_eval_assume_false); + throw_ub_custom!(inline_fluent!("`assume` called with `false`")); } interp_ok(()) } @@ -782,7 +796,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let bits_out = match name { sym::ctpop => u128::from(bits.count_ones()), sym::ctlz_nonzero | sym::cttz_nonzero if bits == 0 => { - throw_ub_custom!(fluent::const_eval_call_nonzero_intrinsic, name = name,); + throw_ub_custom!(inline_fluent!("`{$name}` called on 0"), name = name,); } sym::ctlz | sym::ctlz_nonzero => u128::from(bits.leading_zeros()) - extra, sym::cttz | sym::cttz_nonzero => u128::from((bits << extra).trailing_zeros()) - extra, @@ -815,7 +829,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // sign does not matter for 0 test, so `to_bits` is fine if rem.to_scalar().to_bits(a.layout.size)? != 0 { throw_ub_custom!( - fluent::const_eval_exact_div_has_remainder, + inline_fluent!("exact_div: {$a} cannot be divided by {$b} without remainder"), a = format!("{a}"), b = format!("{b}") ) @@ -900,7 +914,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let size = self.compute_size_in_bytes(size, count).ok_or_else(|| { err_ub_custom!( - fluent::const_eval_size_overflow, + inline_fluent!("overflow computing total size of `{$name}`"), name = if nonoverlapping { "copy_nonoverlapping" } else { "copy" } ) })?; @@ -963,9 +977,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max), // but no actual allocation can be big enough for the difference to be noticeable. - let len = self - .compute_size_in_bytes(layout.size, count) - .ok_or_else(|| err_ub_custom!(fluent::const_eval_size_overflow, name = name))?; + let len = self.compute_size_in_bytes(layout.size, count).ok_or_else(|| { + err_ub_custom!( + inline_fluent!("overflow computing total size of `{$name}`"), + name = name + ) + })?; let bytes = std::iter::repeat_n(byte, len.bytes_usize()); self.write_bytes_ptr(dst, bytes) diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index a6c8b28cce9f..28dae2ef3b8b 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -15,6 +15,7 @@ use rustc_abi::{Align, HasDataLayout, Size}; use rustc_ast::Mutability; use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_errors::inline_fluent; use rustc_middle::mir::display_allocation; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_middle::{bug, throw_ub_format}; @@ -27,7 +28,6 @@ use super::{ err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; use crate::const_eval::ConstEvalErrKind; -use crate::fluent_generated as fluent; #[derive(Debug, PartialEq, Copy, Clone)] pub enum MemoryKind { @@ -291,7 +291,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?; if offset.bytes() != 0 { throw_ub_custom!( - fluent::const_eval_realloc_or_alloc_with_offset, + inline_fluent!( + "{$kind -> + [dealloc] deallocating + [realloc] reallocating + *[other] {\"\"} +} {$ptr} which does not point to the beginning of an object" + ), ptr = format!("{ptr:?}"), kind = "realloc" ); @@ -371,7 +377,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if offset.bytes() != 0 { throw_ub_custom!( - fluent::const_eval_realloc_or_alloc_with_offset, + inline_fluent!( + "{$kind -> + [dealloc] deallocating + [realloc] reallocating + *[other] {\"\"} +} {$ptr} which does not point to the beginning of an object" + ), ptr = format!("{ptr:?}"), kind = "dealloc", ); @@ -382,28 +394,56 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return Err(match self.tcx.try_get_global_alloc(alloc_id) { Some(GlobalAlloc::Function { .. }) => { err_ub_custom!( - fluent::const_eval_invalid_dealloc, + inline_fluent!( + "deallocating {$alloc_id}, which is {$kind -> + [fn] a function + [vtable] a vtable + [static_mem] static memory + *[other] {\"\"} +}" + ), alloc_id = alloc_id, kind = "fn", ) } Some(GlobalAlloc::VTable(..)) => { err_ub_custom!( - fluent::const_eval_invalid_dealloc, + inline_fluent!( + "deallocating {$alloc_id}, which is {$kind -> + [fn] a function + [vtable] a vtable + [static_mem] static memory + *[other] {\"\"} +}" + ), alloc_id = alloc_id, kind = "vtable", ) } Some(GlobalAlloc::TypeId { .. }) => { err_ub_custom!( - fluent::const_eval_invalid_dealloc, + inline_fluent!( + "deallocating {$alloc_id}, which is {$kind -> + [fn] a function + [vtable] a vtable + [static_mem] static memory + *[other] {\"\"} +}" + ), alloc_id = alloc_id, kind = "typeid", ) } Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { err_ub_custom!( - fluent::const_eval_invalid_dealloc, + inline_fluent!( + "deallocating {$alloc_id}, which is {$kind -> + [fn] a function + [vtable] a vtable + [static_mem] static memory + *[other] {\"\"} +}" + ), alloc_id = alloc_id, kind = "static_mem" ) @@ -414,11 +454,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; if alloc.mutability.is_not() { - throw_ub_custom!(fluent::const_eval_dealloc_immutable, alloc = alloc_id,); + throw_ub_custom!( + inline_fluent!("deallocating immutable allocation {$alloc}"), + alloc = alloc_id, + ); } if alloc_kind != kind { throw_ub_custom!( - fluent::const_eval_dealloc_kind_mismatch, + inline_fluent!( + "deallocating {$alloc}, which is {$alloc_kind} memory, using {$kind} deallocation operation" + ), alloc = alloc_id, alloc_kind = format!("{alloc_kind}"), kind = format!("{kind}"), @@ -427,7 +472,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some((size, align)) = old_size_and_align { if size != alloc.size() || align != alloc.align { throw_ub_custom!( - fluent::const_eval_dealloc_incorrect_layout, + inline_fluent!( + "incorrect layout on deallocation: {$alloc} has size {$size} and alignment {$align}, but gave size {$size_found} and alignment {$align_found}" + ), alloc = alloc_id, size = alloc.size().bytes(), align = alloc.align.bytes(), @@ -1546,7 +1593,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if (src_offset <= dest_offset && src_offset + size > dest_offset) || (dest_offset <= src_offset && dest_offset + size > src_offset) { - throw_ub_custom!(fluent::const_eval_copy_nonoverlapping_overlapping); + throw_ub_custom!(inline_fluent!( + "`copy_nonoverlapping` called on overlapping ranges" + )); } } } diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 1266e15c682f..ead1ee9bf85e 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -1,6 +1,6 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(array_try_map)] -#![feature(assert_matches)] #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] @@ -26,8 +26,6 @@ use rustc_middle::util::Providers; pub use self::errors::ReportErrorExt; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub fn provide(providers: &mut Providers) { const_eval::provide(&mut providers.queries); providers.queries.tag_for_variant = const_eval::tag_for_variant_provider; diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 4467a2811181..a56b5f5fdc7d 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -10,16 +10,16 @@ #![allow(internal_features)] #![allow(rustc::default_hash_types)] #![allow(rustc::potential_query_instability)] +#![cfg_attr(bootstrap, feature(assert_matches))] +#![cfg_attr(bootstrap, feature(cold_path))] #![deny(unsafe_op_in_unsafe_fn)] #![feature(allocator_api)] #![feature(ascii_char)] #![feature(ascii_char_variants)] -#![feature(assert_matches)] #![feature(auto_traits)] #![feature(cfg_select)] #![feature(const_default)] #![feature(const_trait_impl)] -#![feature(core_intrinsics)] #![feature(dropck_eyepatch)] #![feature(extend_one)] #![feature(file_buffered)] diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index b04caa69adfb..cab57edb8946 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -85,12 +85,11 @@ use std::borrow::Borrow; use std::collections::hash_map::Entry; use std::error::Error; use std::fmt::Display; -use std::intrinsics::unlikely; use std::path::Path; use std::sync::Arc; use std::sync::atomic::Ordering; use std::time::{Duration, Instant}; -use std::{fs, process}; +use std::{fs, hint, process}; pub use measureme::EventId; use measureme::{EventIdBuilder, Profiler, SerializableString, StringId}; @@ -427,7 +426,8 @@ impl SelfProfilerRef { .unwrap() .increment_query_cache_hit_counters(QueryInvocationId(query_invocation_id.0)); } - if unlikely(profiler_ref.event_filter_mask.contains(EventFilter::QUERY_CACHE_HITS)) { + if profiler_ref.event_filter_mask.contains(EventFilter::QUERY_CACHE_HITS) { + hint::cold_path(); profiler_ref.instant_query_event( |profiler| profiler.query_cache_hit_event_kind, query_invocation_id, @@ -437,7 +437,8 @@ impl SelfProfilerRef { // We check both kinds of query cache hit events at once, to reduce overhead in the // common case (with self-profile disabled). - if unlikely(self.event_filter_mask.intersects(EventFilter::QUERY_CACHE_HIT_COMBINED)) { + if self.event_filter_mask.intersects(EventFilter::QUERY_CACHE_HIT_COMBINED) { + hint::cold_path(); cold_call(self, query_invocation_id); } } diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 3a64c924cc22..9fb4d4352c2f 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -607,7 +607,7 @@ where /// result (for example, using a `Fingerprint` produced while /// hashing `Span`s when a `Fingerprint` without `Span`s is /// being requested) -#[derive(Clone, Hash, Eq, PartialEq, Debug)] +#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] pub struct HashingControls { pub hash_spans: bool, } diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 3881f3c2aa84..31768fe189ae 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -41,7 +41,8 @@ pub use self::freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard}; pub use self::lock::{Lock, LockGuard, Mode}; pub use self::mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; pub use self::parallel::{ - broadcast, join, par_for_each_in, par_map, parallel_guard, scope, spawn, try_par_for_each_in, + broadcast, par_fns, par_for_each_in, par_join, par_map, parallel_guard, spawn, + try_par_for_each_in, }; pub use self::vec::{AppendOnlyIndexVec, AppendOnlyVec}; pub use self::worker_local::{Registry, WorkerLocal}; diff --git a/compiler/rustc_data_structures/src/sync/freeze.rs b/compiler/rustc_data_structures/src/sync/freeze.rs index 6338afb92c34..fb2dcb25556a 100644 --- a/compiler/rustc_data_structures/src/sync/freeze.rs +++ b/compiler/rustc_data_structures/src/sync/freeze.rs @@ -1,5 +1,5 @@ use std::cell::UnsafeCell; -use std::intrinsics::likely; +use std::hint; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::ptr::NonNull; @@ -60,10 +60,11 @@ impl FreezeLock { /// Get the inner value if frozen. #[inline] pub fn get(&self) -> Option<&T> { - if likely(self.frozen.load(Ordering::Acquire)) { + if self.frozen.load(Ordering::Acquire) { // SAFETY: This is frozen so the data cannot be modified. unsafe { Some(&*self.data.get()) } } else { + hint::cold_path(); None } } diff --git a/compiler/rustc_data_structures/src/sync/lock.rs b/compiler/rustc_data_structures/src/sync/lock.rs index a8161c515115..f183af0c0dab 100644 --- a/compiler/rustc_data_structures/src/sync/lock.rs +++ b/compiler/rustc_data_structures/src/sync/lock.rs @@ -1,7 +1,7 @@ //! This module implements a lock which only uses synchronization if `might_be_dyn_thread_safe` is true. //! It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync` traits. -use std::fmt; +use std::{fmt, hint}; #[derive(Clone, Copy, PartialEq)] pub enum Mode { @@ -10,7 +10,6 @@ pub enum Mode { } use std::cell::{Cell, UnsafeCell}; -use std::intrinsics::unlikely; use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::ops::{Deref, DerefMut}; @@ -92,7 +91,8 @@ pub struct Lock { impl Lock { #[inline(always)] pub fn new(inner: T) -> Self { - let (mode, mode_union) = if unlikely(mode::might_be_dyn_thread_safe()) { + let (mode, mode_union) = if mode::might_be_dyn_thread_safe() { + hint::cold_path(); // Create the lock with synchronization enabled using the `RawMutex` type. (Mode::Sync, ModeUnion { sync: ManuallyDrop::new(RawMutex::INIT) }) } else { @@ -150,7 +150,8 @@ impl Lock { unsafe { match mode { Mode::NoSync => { - if unlikely(self.mode_union.no_sync.replace(LOCKED) == LOCKED) { + if self.mode_union.no_sync.replace(LOCKED) == LOCKED { + hint::cold_path(); lock_held() } } diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs index b515c0bee8a6..044767321c27 100644 --- a/compiler/rustc_data_structures/src/sync/parallel.rs +++ b/compiler/rustc_data_structures/src/sync/parallel.rs @@ -56,41 +56,6 @@ where (a.unwrap(), b.unwrap()) } -/// Runs a list of blocks in parallel. The first block is executed immediately on -/// the current thread. Use that for the longest running block. -#[macro_export] -macro_rules! parallel { - (impl $fblock:block [$($c:expr,)*] [$block:expr $(, $rest:expr)*]) => { - parallel!(impl $fblock [$block, $($c,)*] [$($rest),*]) - }; - (impl $fblock:block [$($blocks:expr,)*] []) => { - $crate::sync::parallel_guard(|guard| { - $crate::sync::scope(|s| { - $( - let block = $crate::sync::FromDyn::from(|| $blocks); - s.spawn(move |_| { - guard.run(move || block.into_inner()()); - }); - )* - guard.run(|| $fblock); - }); - }); - }; - ($fblock:block, $($blocks:block),*) => { - if $crate::sync::is_dyn_thread_safe() { - // Reverse the order of the later blocks since Rayon executes them in reverse order - // when using a single thread. This ensures the execution order matches that - // of a single threaded rustc. - parallel!(impl $fblock [] [$($blocks),*]); - } else { - $crate::sync::parallel_guard(|guard| { - guard.run(|| $fblock); - $(guard.run(|| $blocks);)* - }); - } - }; - } - pub fn spawn(func: impl FnOnce() + DynSend + 'static) { if mode::is_dyn_thread_safe() { let func = FromDyn::from(func); @@ -102,18 +67,43 @@ pub fn spawn(func: impl FnOnce() + DynSend + 'static) { } } -// This function only works when `mode::is_dyn_thread_safe()`. -pub fn scope<'scope, OP, R>(op: OP) -> R -where - OP: FnOnce(&rustc_thread_pool::Scope<'scope>) -> R + DynSend, - R: DynSend, -{ - let op = FromDyn::from(op); - rustc_thread_pool::scope(|s| FromDyn::from(op.into_inner()(s))).into_inner() +/// Runs the functions in parallel. +/// +/// The first function is executed immediately on the current thread. +/// Use that for the longest running function for better scheduling. +pub fn par_fns(funcs: &mut [&mut (dyn FnMut() + DynSend)]) { + parallel_guard(|guard: &ParallelGuard| { + if mode::is_dyn_thread_safe() { + let funcs = FromDyn::from(funcs); + rustc_thread_pool::scope(|s| { + let Some((first, rest)) = funcs.into_inner().split_at_mut_checked(1) else { + return; + }; + + // Reverse the order of the later functions since Rayon executes them in reverse + // order when using a single thread. This ensures the execution order matches + // that of a single threaded rustc. + for f in rest.iter_mut().rev() { + let f = FromDyn::from(f); + s.spawn(|_| { + guard.run(|| (f.into_inner())()); + }); + } + + // Run the first function without spawning to + // ensure it executes immediately on this thread. + guard.run(|| first[0]()); + }); + } else { + for f in funcs { + guard.run(|| f()); + } + } + }); } #[inline] -pub fn join(oper_a: A, oper_b: B) -> (RA, RB) +pub fn par_join(oper_a: A, oper_b: B) -> (RA, RB) where A: FnOnce() -> RA + DynSend, B: FnOnce() -> RB + DynSend, diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 39e512be6820..10e1b1987194 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -10,7 +10,6 @@ jiff = { version = "0.2.5", default-features = false, features = ["std"] } rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_borrowck = { path = "../rustc_borrowck" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } rustc_const_eval = { path = "../rustc_const_eval" } rustc_data_structures = { path = "../rustc_data_structures" } @@ -30,13 +29,11 @@ rustc_middle = { path = "../rustc_middle" } rustc_mir_build = { path = "../rustc_mir_build" } rustc_mir_transform = { path = "../rustc_mir_transform" } rustc_parse = { path = "../rustc_parse" } -rustc_passes = { path = "../rustc_passes" } rustc_public = { path = "../rustc_public", features = ["rustc_internal"] } rustc_resolve = { path = "../rustc_resolve" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -rustc_trait_selection = { path = "../rustc_trait_selection" } serde_json = "1.0.59" shlex = "1.0" tracing = { version = "0.1.35" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 2e97b4fed245..a235d3e0aecd 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -34,7 +34,6 @@ use rustc_data_structures::profiling::{ }; pub use rustc_errors::catch_fatal_errors; use rustc_errors::emitter::stderr_destination; -use rustc_errors::registry::Registry; use rustc_errors::translation::Translator; use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, PResult, markdown}; use rustc_feature::find_gated_cfg; @@ -109,22 +108,9 @@ use crate::session_diagnostics::{ }; pub fn default_translator() -> Translator { - Translator::with_fallback_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false) + Translator::new() } -pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ - // tidy-alphabetical-start - rustc_borrowck::DEFAULT_LOCALE_RESOURCE, - rustc_const_eval::DEFAULT_LOCALE_RESOURCE, - rustc_hir_analysis::DEFAULT_LOCALE_RESOURCE, - rustc_lint::DEFAULT_LOCALE_RESOURCE, - rustc_mir_build::DEFAULT_LOCALE_RESOURCE, - rustc_parse::DEFAULT_LOCALE_RESOURCE, - rustc_passes::DEFAULT_LOCALE_RESOURCE, - rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, - // tidy-alphabetical-end -]; - /// Exit status code used for successful compilation and help output. pub const EXIT_SUCCESS: i32 = 0; @@ -185,10 +171,6 @@ impl Callbacks for TimePassesCallbacks { } } -pub fn diagnostics_registry() -> Registry { - Registry::new(rustc_errors::codes::DIAGNOSTICS) -} - /// This is the primary entry point for rustc. pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) { let mut default_early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); @@ -216,7 +198,7 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) let ice_file = ice_path_with_config(Some(&sopts.unstable_opts)).clone(); if let Some(ref code) = matches.opt_str("explain") { - handle_explain(&default_early_dcx, diagnostics_registry(), code, sopts.color); + handle_explain(&default_early_dcx, code, sopts.color); return; } @@ -235,7 +217,6 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) output_dir: odir, ice_file, file_loader: None, - locale_resources: DEFAULT_LOCALE_RESOURCES.to_vec(), lint_caps: Default::default(), psess_created: None, hash_untracked_state: None, @@ -243,7 +224,6 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) override_queries: None, extra_symbols: Vec::new(), make_codegen_backend: None, - registry: diagnostics_registry(), using_internal_features: &USING_INTERNAL_FEATURES, }; @@ -443,12 +423,12 @@ pub enum Compilation { Continue, } -fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, color: ColorConfig) { +fn handle_explain(early_dcx: &EarlyDiagCtxt, code: &str, color: ColorConfig) { // Allow "E0123" or "0123" form. let upper_cased_code = code.to_ascii_uppercase(); if let Ok(code) = upper_cased_code.trim_prefix('E').parse::() && code <= ErrCode::MAX_AS_U32 - && let Ok(description) = registry.try_find_description(ErrCode::from_u32(code)) + && let Ok(description) = rustc_errors::codes::try_find_description(ErrCode::from_u32(code)) { let mut is_in_code_block = false; let mut text = String::new(); @@ -520,7 +500,7 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { }; // Try to print via the pager, pretty output if possible. - let pager_res: Option<()> = try { + let pager_res = try { let mut pager = cmd.stdin(Stdio::piped()).spawn().ok()?; let pager_stdin = pager.stdin.as_mut()?; @@ -1545,7 +1525,7 @@ fn report_ice( extra_info: fn(&DiagCtxt), using_internal_features: &AtomicBool, ) { - let translator = default_translator(); + let translator = Translator::new(); let emitter = Box::new(rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter::new( stderr_destination(rustc_errors::ColorConfig::Auto), diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 565c176645de..97972185ebc4 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -51,7 +51,7 @@ pub(crate) struct RlinkCorruptFile<'a> { } #[derive(Diagnostic)] -#[diag("the compiler unexpectedly panicked. this is a bug.")] +#[diag("the compiler unexpectedly panicked. This is a bug")] pub(crate) struct Ice; #[derive(Diagnostic)] diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index c9e887061305..0b30102eb992 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -192,7 +192,7 @@ pub fn fluent_bundle( Ok(Some(bundle)) } -fn register_functions(bundle: &mut FluentBundle) { +pub fn register_functions(bundle: &mut fluent_bundle::bundle::FluentBundle) { bundle .add_function("STREQ", |positional, _named| match positional { [FluentValue::String(a), FluentValue::String(b)] => format!("{}", (a == b)).into(), @@ -230,50 +230,6 @@ pub fn fallback_fluent_bundle( }))) } -/// Identifier for the Fluent message/attribute corresponding to a diagnostic message. -type FluentId = Cow<'static, str>; - -/// Abstraction over a message in a subdiagnostic (i.e. label, note, help, etc) to support both -/// translatable and non-translatable diagnostic messages. -/// -/// Translatable messages for subdiagnostics are typically attributes attached to a larger Fluent -/// message so messages of this type must be combined with a `DiagMessage` (using -/// `DiagMessage::with_subdiagnostic_message`) before rendering. However, subdiagnostics from -/// the `Subdiagnostic` derive refer to Fluent identifiers directly. -#[rustc_diagnostic_item = "SubdiagMessage"] -pub enum SubdiagMessage { - /// Non-translatable diagnostic message. - Str(Cow<'static, str>), - /// Identifier of a Fluent message. Instances of this variant are generated by the - /// `Subdiagnostic` derive. - FluentIdentifier(FluentId), - /// An inline Fluent message. Instances of this variant are generated by the - /// `Subdiagnostic` derive. - Inline(Cow<'static, str>), - /// Attribute of a Fluent message. Needs to be combined with a Fluent identifier to produce an - /// actual translated message. Instances of this variant are generated by the `fluent_messages` - /// macro. - /// - /// - FluentAttr(FluentId), -} - -impl From for SubdiagMessage { - fn from(s: String) -> Self { - SubdiagMessage::Str(Cow::Owned(s)) - } -} -impl From<&'static str> for SubdiagMessage { - fn from(s: &'static str) -> Self { - SubdiagMessage::Str(Cow::Borrowed(s)) - } -} -impl From> for SubdiagMessage { - fn from(s: Cow<'static, str>) -> Self { - SubdiagMessage::Str(s) - } -} - /// Abstraction over a message in a diagnostic to support both translatable and non-translatable /// diagnostic messages. /// @@ -288,44 +244,15 @@ pub enum DiagMessage { /// are translated when they are added to the parent diagnostic. This is one of the ways /// this variant of `DiagMessage` is produced. Str(Cow<'static, str>), - /// Identifier for a Fluent message (with optional attribute) corresponding to the diagnostic - /// message. Yet to be translated. - /// - /// - /// - FluentIdentifier(FluentId, Option), /// An inline Fluent message, containing the to be translated diagnostic message. Inline(Cow<'static, str>), } impl DiagMessage { - /// Given a `SubdiagMessage` which may contain a Fluent attribute, create a new - /// `DiagMessage` that combines that attribute with the Fluent identifier of `self`. - /// - /// - If the `SubdiagMessage` is non-translatable then return the message as a `DiagMessage`. - /// - If `self` is non-translatable then return `self`'s message. - pub fn with_subdiagnostic_message(&self, sub: SubdiagMessage) -> Self { - let attr = match sub { - SubdiagMessage::Str(s) => return DiagMessage::Str(s), - SubdiagMessage::FluentIdentifier(id) => { - return DiagMessage::FluentIdentifier(id, None); - } - SubdiagMessage::Inline(s) => return DiagMessage::Inline(s), - SubdiagMessage::FluentAttr(attr) => attr, - }; - - match self { - DiagMessage::FluentIdentifier(id, _) => { - DiagMessage::FluentIdentifier(id.clone(), Some(attr)) - } - _ => panic!("Tried to add a subdiagnostic to a message without a fluent identifier"), - } - } - pub fn as_str(&self) -> Option<&str> { match self { DiagMessage::Str(s) => Some(s), - DiagMessage::FluentIdentifier(_, _) | DiagMessage::Inline(_) => None, + DiagMessage::Inline(_) => None, } } } @@ -346,24 +273,6 @@ impl From> for DiagMessage { } } -/// Translating *into* a subdiagnostic message from a diagnostic message is a little strange - but -/// the subdiagnostic functions (e.g. `span_label`) take a `SubdiagMessage` and the -/// subdiagnostic derive refers to typed identifiers that are `DiagMessage`s, so need to be -/// able to convert between these, as much as they'll be converted back into `DiagMessage` -/// using `with_subdiagnostic_message` eventually. Don't use this other than for the derive. -impl From for SubdiagMessage { - fn from(val: DiagMessage) -> Self { - match val { - DiagMessage::Str(s) => SubdiagMessage::Str(s), - DiagMessage::FluentIdentifier(id, None) => SubdiagMessage::FluentIdentifier(id), - // There isn't really a sensible behaviour for this because it loses information but - // this is the most sensible of the behaviours. - DiagMessage::FluentIdentifier(_, Some(attr)) => SubdiagMessage::FluentAttr(attr), - DiagMessage::Inline(s) => SubdiagMessage::Inline(s), - } - } -} - /// A span together with some additional data. #[derive(Clone, Debug)] pub struct SpanLabel { diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 9bb318973958..bdd3266adb66 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -27,7 +27,6 @@ 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::translation::{Translator, to_fluent_args}; use crate::{ CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, Level, MultiSpan, Style, Subdiag, @@ -73,7 +72,7 @@ impl Debug for AnnotateSnippetEmitter { impl Emitter for AnnotateSnippetEmitter { /// The entry point for the diagnostics generation - fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { + fn emit_diagnostic(&mut self, mut diag: DiagInner) { let fluent_args = to_fluent_args(diag.args.iter()); if self.track_diagnostics && diag.span.has_primary_spans() && !diag.span.is_dummy() { diff --git a/compiler/rustc_errors/src/codes.rs b/compiler/rustc_errors/src/codes.rs index 924924f285eb..c1e7b7c184c1 100644 --- a/compiler/rustc_errors/src/codes.rs +++ b/compiler/rustc_errors/src/codes.rs @@ -5,6 +5,9 @@ //! long description text. use std::fmt; +use std::sync::LazyLock; + +use rustc_data_structures::fx::FxHashMap; rustc_index::newtype_index! { #[max = 9999] // Because all error codes have four digits. @@ -27,15 +30,28 @@ macro_rules! define_error_code_constants_and_diagnostics_table { $( pub const ${concat(E, $num)}: $crate::ErrCode = $crate::ErrCode::from_u32($num); )* - pub static DIAGNOSTICS: &[($crate::ErrCode, &str)] = &[ - $( ( - ${concat(E, $num)}, - include_str!( - concat!("../../rustc_error_codes/src/error_codes/E", stringify!($num), ".md") - ) - ), )* - ]; + static DIAGNOSTICS: LazyLock> = LazyLock::new(|| { + [ + $( ( + ${concat(E, $num)}, + include_str!( + concat!("../../rustc_error_codes/src/error_codes/E", stringify!($num), ".md") + ) + ), )* + ] + .iter() + .copied() + .collect() + }); ) } rustc_error_codes::error_codes!(define_error_code_constants_and_diagnostics_table); + +#[derive(Debug)] +pub struct InvalidErrorCode; + +/// Returns `InvalidErrorCode` if the code requested does not exist. +pub fn try_find_description(code: ErrCode) -> Result<&'static str, InvalidErrorCode> { + DIAGNOSTICS.get(&code).copied().ok_or(InvalidErrorCode) +} diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index a9e81354fc6a..087c5e700df0 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -17,8 +17,7 @@ use tracing::debug; use crate::{ CodeSuggestion, DiagCtxtHandle, DiagMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level, - MultiSpan, StashKey, Style, SubdiagMessage, Substitution, SubstitutionPart, SuggestionStyle, - Suggestions, + MultiSpan, StashKey, Style, Substitution, SubstitutionPart, SuggestionStyle, Suggestions, }; pub type DiagArgMap = FxIndexMap; @@ -325,30 +324,8 @@ impl DiagInner { } } - // See comment on `Diag::subdiagnostic_message_to_diagnostic_message`. - pub(crate) fn subdiagnostic_message_to_diagnostic_message( - &self, - attr: impl Into, - ) -> DiagMessage { - let msg = - self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages"); - msg.with_subdiagnostic_message(attr.into()) - } - - pub(crate) fn sub( - &mut self, - level: Level, - message: impl Into, - span: MultiSpan, - ) { - let sub = Subdiag { - level, - messages: vec![( - self.subdiagnostic_message_to_diagnostic_message(message), - Style::NoStyle, - )], - span, - }; + pub(crate) fn sub(&mut self, level: Level, message: impl Into, span: MultiSpan) { + let sub = Subdiag { level, messages: vec![(message.into(), Style::NoStyle)], span }; self.children.push(sub); } @@ -609,9 +586,8 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// the diagnostic was constructed. However, the label span is *not* considered a /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is /// primary. - pub fn span_label(&mut self, span: Span, label: impl Into) -> &mut Self { - let msg = self.subdiagnostic_message_to_diagnostic_message(label); - self.span.push_span_label(span, msg); + pub fn span_label(&mut self, span: Span, label: impl Into) -> &mut Self { + self.span.push_span_label(span, label.into()); self } } @@ -713,7 +689,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_note, /// Add a note attached to this diagnostic. - pub fn note(&mut self, msg: impl Into) -> &mut Self { + pub fn note(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::Note, msg, MultiSpan::new()); self } } @@ -733,7 +709,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { } /// This is like [`Diag::note()`], but it's only printed once. - pub fn note_once(&mut self, msg: impl Into) -> &mut Self { + pub fn note_once(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::OnceNote, msg, MultiSpan::new()); self } @@ -744,7 +720,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn span_note( &mut self, sp: impl Into, - msg: impl Into, + msg: impl Into, ) -> &mut Self { self.sub(Level::Note, msg, sp.into()); self @@ -755,7 +731,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn span_note_once>( &mut self, sp: S, - msg: impl Into, + msg: impl Into, ) -> &mut Self { self.sub(Level::OnceNote, msg, sp.into()); self @@ -763,7 +739,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_warn, /// Add a warning attached to this diagnostic. - pub fn warn(&mut self, msg: impl Into) -> &mut Self { + pub fn warn(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::Warning, msg, MultiSpan::new()); self } } @@ -773,7 +749,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn span_warn>( &mut self, sp: S, - msg: impl Into, + msg: impl Into, ) -> &mut Self { self.sub(Level::Warning, msg, sp.into()); self @@ -781,13 +757,13 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_help, /// Add a help message attached to this diagnostic. - pub fn help(&mut self, msg: impl Into) -> &mut Self { + pub fn help(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::Help, msg, MultiSpan::new()); self } } /// This is like [`Diag::help()`], but it's only printed once. - pub fn help_once(&mut self, msg: impl Into) -> &mut Self { + pub fn help_once(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::OnceHelp, msg, MultiSpan::new()); self } @@ -814,7 +790,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn span_help( &mut self, sp: impl Into, - msg: impl Into, + msg: impl Into, ) -> &mut Self { self.sub(Level::Help, msg, sp.into()); self @@ -866,7 +842,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// In other words, multiple changes need to be applied as part of this suggestion. pub fn multipart_suggestion( &mut self, - msg: impl Into, + msg: impl Into, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self { @@ -882,7 +858,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// In other words, multiple changes need to be applied as part of this suggestion. pub fn multipart_suggestion_verbose( &mut self, - msg: impl Into, + msg: impl Into, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self { @@ -897,7 +873,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// [`Diag::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. pub fn multipart_suggestion_with_style( &mut self, - msg: impl Into, + msg: impl Into, mut suggestion: Vec<(Span, String)>, applicability: Applicability, style: SuggestionStyle, @@ -924,7 +900,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self.push_suggestion(CodeSuggestion { substitutions: vec![Substitution { parts }], - msg: self.subdiagnostic_message_to_diagnostic_message(msg), + msg: msg.into(), style, applicability, }); @@ -939,7 +915,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// improve understandability. pub fn tool_only_multipart_suggestion( &mut self, - msg: impl Into, + msg: impl Into, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self { @@ -972,7 +948,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn span_suggestion( &mut self, sp: Span, - msg: impl Into, + msg: impl Into, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self { @@ -990,7 +966,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn span_suggestion_with_style( &mut self, sp: Span, - msg: impl Into, + msg: impl Into, suggestion: impl ToString, applicability: Applicability, style: SuggestionStyle, @@ -1003,7 +979,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { substitutions: vec![Substitution { parts: vec![SubstitutionPart { snippet: suggestion.to_string(), span: sp }], }], - msg: self.subdiagnostic_message_to_diagnostic_message(msg), + msg: msg.into(), style, applicability, }); @@ -1015,7 +991,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn span_suggestion_verbose( &mut self, sp: Span, - msg: impl Into, + msg: impl Into, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self { @@ -1035,7 +1011,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn span_suggestions( &mut self, sp: Span, - msg: impl Into, + msg: impl Into, suggestions: impl IntoIterator, applicability: Applicability, ) -> &mut Self { @@ -1051,7 +1027,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn span_suggestions_with_style( &mut self, sp: Span, - msg: impl Into, + msg: impl Into, suggestions: impl IntoIterator, applicability: Applicability, style: SuggestionStyle, @@ -1068,7 +1044,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { .collect(); self.push_suggestion(CodeSuggestion { substitutions, - msg: self.subdiagnostic_message_to_diagnostic_message(msg), + msg: msg.into(), style, applicability, }); @@ -1080,7 +1056,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// See also [`Diag::multipart_suggestion()`]. pub fn multipart_suggestions( &mut self, - msg: impl Into, + msg: impl Into, suggestions: impl IntoIterator>, applicability: Applicability, ) -> &mut Self { @@ -1112,7 +1088,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self.push_suggestion(CodeSuggestion { substitutions, - msg: self.subdiagnostic_message_to_diagnostic_message(msg), + msg: msg.into(), style: SuggestionStyle::ShowAlways, applicability, }); @@ -1127,7 +1103,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn span_suggestion_short( &mut self, sp: Span, - msg: impl Into, + msg: impl Into, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self { @@ -1150,7 +1126,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn span_suggestion_hidden( &mut self, sp: Span, - msg: impl Into, + msg: impl Into, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self { @@ -1172,7 +1148,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn tool_only_span_suggestion( &mut self, sp: Span, - msg: impl Into, + msg: impl Into, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self { @@ -1200,10 +1176,9 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// one value will clobber the other. Eagerly translating the /// diagnostic uses the variables defined right then, before the /// clobbering occurs. - pub fn eagerly_translate(&self, msg: impl Into) -> SubdiagMessage { + pub fn eagerly_translate(&self, msg: impl Into) -> DiagMessage { let args = self.args.iter(); - let msg = self.subdiagnostic_message_to_diagnostic_message(msg.into()); - self.dcx.eagerly_translate(msg, args) + self.dcx.eagerly_translate(msg.into(), args) } with_fn! { with_span, @@ -1256,31 +1231,18 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } } - /// Helper function that takes a `SubdiagMessage` and returns a `DiagMessage` by - /// combining it with the primary message of the diagnostic (if translatable, otherwise it just - /// passes the user's string along). - pub(crate) fn subdiagnostic_message_to_diagnostic_message( - &self, - attr: impl Into, - ) -> DiagMessage { - self.deref().subdiagnostic_message_to_diagnostic_message(attr) - } - /// Convenience function for internal use, clients should use one of the /// public methods above. /// /// Used by `proc_macro_server` for implementing `server::Diagnostic`. - pub fn sub(&mut self, level: Level, message: impl Into, span: MultiSpan) { + pub fn sub(&mut self, level: Level, message: impl Into, span: MultiSpan) { self.deref_mut().sub(level, message, span); } /// Convenience function for internal use, clients should use one of the /// public methods above. fn sub_with_highlights(&mut self, level: Level, messages: Vec, span: MultiSpan) { - let messages = messages - .into_iter() - .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.content), m.style)) - .collect(); + let messages = messages.into_iter().map(|m| (m.content.into(), m.style)).collect(); let sub = Subdiag { level, messages, span }; self.children.push(sub); } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 3433db1e0704..4ceb5cf06f93 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -24,7 +24,6 @@ use rustc_span::source_map::SourceMap; use rustc_span::{FileName, SourceFile, Span}; use tracing::{debug, warn}; -use crate::registry::Registry; use crate::timings::TimingRecord; use crate::translation::Translator; use crate::{ @@ -54,7 +53,7 @@ pub type DynEmitter = dyn Emitter + DynSend; /// Emitter trait for emitting errors and other structured information. pub trait Emitter { /// Emit a structured diagnostic. - fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry); + fn emit_diagnostic(&mut self, diag: DiagInner); /// Emit a notification that an artifact has been output. /// Currently only supported for the JSON format. @@ -66,7 +65,7 @@ pub trait Emitter { /// Emit a report about future breakage. /// Currently only supported for the JSON format. - fn emit_future_breakage_report(&mut self, _diags: Vec, _registry: &Registry) {} + fn emit_future_breakage_report(&mut self, _diags: Vec) {} /// Emit list of unused externs. /// Currently only supported for the JSON format. @@ -380,9 +379,9 @@ impl Emitter for EmitterWithNote { None } - fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) { + fn emit_diagnostic(&mut self, mut diag: DiagInner) { diag.sub(Level::Note, self.note.clone(), MultiSpan::new()); - self.emitter.emit_diagnostic(diag, registry); + self.emitter.emit_diagnostic(diag); } fn translator(&self) -> &Translator { @@ -399,7 +398,7 @@ impl Emitter for SilentEmitter { None } - fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {} + fn emit_diagnostic(&mut self, _diag: DiagInner) {} fn translator(&self) -> &Translator { &self.translator diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 85801245bea9..310a64745bad 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -31,7 +31,6 @@ use crate::emitter::{ ColorConfig, Destination, Emitter, HumanReadableErrorType, OutputTheme, TimingEvent, should_show_source_code, }; -use crate::registry::Registry; use crate::timings::{TimingRecord, TimingSection}; use crate::translation::{Translator, to_fluent_args}; use crate::{CodeSuggestion, MultiSpan, SpanLabel, Subdiag, Suggestions, TerminalUrl}; @@ -107,8 +106,8 @@ enum EmitTyped<'a> { } impl Emitter for JsonEmitter { - fn emit_diagnostic(&mut self, diag: crate::DiagInner, registry: &Registry) { - let data = Diagnostic::from_errors_diagnostic(diag, self, registry); + fn emit_diagnostic(&mut self, diag: crate::DiagInner) { + let data = Diagnostic::from_errors_diagnostic(diag, self); let result = self.emit(EmitTyped::Diagnostic(data)); if let Err(e) = result { panic!("failed to print diagnostics: {e:?}"); @@ -139,7 +138,7 @@ impl Emitter for JsonEmitter { } } - fn emit_future_breakage_report(&mut self, diags: Vec, registry: &Registry) { + fn emit_future_breakage_report(&mut self, diags: Vec) { let data: Vec> = diags .into_iter() .map(|mut diag| { @@ -153,7 +152,7 @@ impl Emitter for JsonEmitter { } FutureBreakageItem { diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic( - diag, self, registry, + diag, self, )), } }) @@ -307,11 +306,7 @@ struct UnusedExterns<'a> { impl Diagnostic { /// Converts from `rustc_errors::DiagInner` to `Diagnostic`. - fn from_errors_diagnostic( - diag: crate::DiagInner, - je: &JsonEmitter, - registry: &Registry, - ) -> Diagnostic { + fn from_errors_diagnostic(diag: crate::DiagInner, je: &JsonEmitter) -> Diagnostic { let args = to_fluent_args(diag.args.iter()); let sugg_to_diag = |sugg: &CodeSuggestion| { let translated_message = @@ -351,7 +346,7 @@ impl Diagnostic { let code = if let Some(code) = diag.code { Some(DiagnosticCode { code: code.to_string(), - explanation: registry.try_find_description(code).ok(), + explanation: crate::codes::try_find_description(code).ok(), }) } else if let Some(IsLint { name, .. }) = &diag.is_lint { Some(DiagnosticCode { code: name.to_string(), explanation: None }) @@ -388,7 +383,7 @@ impl Diagnostic { .ui_testing(je.ui_testing) .ignored_directories_in_source_blocks(je.ignored_directories_in_source_blocks.clone()) .theme(if je.json_rendered.unicode { OutputTheme::Unicode } else { OutputTheme::Ascii }) - .emit_diagnostic(diag, registry); + .emit_diagnostic(diag); let buf = Arc::try_unwrap(buf.0).unwrap().into_inner().unwrap(); let buf = String::from_utf8(buf).unwrap(); diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 6f99930c433b..ffcf9947a7a8 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -45,7 +45,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { rustc_span::create_default_session_globals_then(|| { let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); sm.new_source_file(filename(&sm, "test.rs"), code.to_owned()); - let translator = Translator::with_fallback_bundle(vec![], false); + let translator = Translator::new(); let output = Arc::new(Mutex::new(Vec::new())); let je = JsonEmitter::new( diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index f89a2af71022..cfd4ae3795e1 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -5,7 +5,7 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::direct_use_of_rustc_type_ir)] -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(default_field_values)] @@ -57,8 +57,8 @@ use rustc_data_structures::sync::{DynSend, Lock}; use rustc_data_structures::{AtomicRef, assert_matches}; pub use rustc_error_messages::{ DiagArg, DiagArgFromDisplay, DiagArgName, DiagArgValue, DiagMessage, FluentBundle, IntoDiagArg, - LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagMessage, - fallback_fluent_bundle, fluent_bundle, into_diag_arg_using_display, + LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, fallback_fluent_bundle, + fluent_bundle, into_diag_arg_using_display, }; use rustc_hashes::Hash128; use rustc_lint_defs::LintExpectationId; @@ -71,7 +71,6 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::debug; use crate::emitter::TimingEvent; -use crate::registry::Registry; use crate::timings::TimingRecord; pub mod annotate_snippet_emitter_writer; @@ -84,9 +83,6 @@ pub mod error; pub mod json; mod lock; pub mod markdown; -pub mod registry; -#[cfg(test)] -mod tests; pub mod timings; pub mod translation; @@ -297,8 +293,6 @@ impl<'a> std::ops::Deref for DiagCtxtHandle<'a> { struct DiagCtxtInner { flags: DiagCtxtFlags, - registry: Registry, - /// The error guarantees from all emitted errors. The length gives the error count. err_guars: Vec, /// The error guarantee from all emitted lint errors. The length gives the @@ -480,11 +474,6 @@ impl DiagCtxt { self } - pub fn with_registry(mut self, registry: Registry) -> Self { - self.inner.get_mut().registry = registry; - self - } - pub fn new(emitter: Box) -> Self { Self { inner: Lock::new(DiagCtxtInner::new(emitter)) } } @@ -499,12 +488,12 @@ impl DiagCtxt { self.inner.borrow_mut().emitter = emitter; } - /// Translate `message` eagerly with `args` to `SubdiagMessage::Eager`. + /// Translate `message` eagerly with `args` to `DiagMessage::Eager`. pub fn eagerly_translate<'a>( &self, message: DiagMessage, args: impl Iterator>, - ) -> SubdiagMessage { + ) -> DiagMessage { let inner = self.inner.borrow(); inner.eagerly_translate(message, args) } @@ -537,7 +526,6 @@ impl DiagCtxt { let mut inner = self.inner.borrow_mut(); let DiagCtxtInner { flags: _, - registry: _, err_guars, lint_err_guars, delayed_bugs, @@ -811,7 +799,7 @@ impl<'a> DiagCtxtHandle<'a> { .emitted_diagnostic_codes .iter() .filter_map(|&code| { - if inner.registry.try_find_description(code).is_ok() { + if crate::codes::try_find_description(code).is_ok() { Some(code.to_string()) } else { None @@ -883,7 +871,7 @@ impl<'a> DiagCtxtHandle<'a> { let inner = &mut *self.inner.borrow_mut(); let diags = std::mem::take(&mut inner.future_breakage_diagnostics); if !diags.is_empty() { - inner.emitter.emit_future_breakage_report(diags, &inner.registry); + inner.emitter.emit_future_breakage_report(diags); } } @@ -1184,7 +1172,6 @@ impl DiagCtxtInner { fn new(emitter: Box) -> Self { Self { flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() }, - registry: Registry::new(&[]), err_guars: Vec::new(), lint_err_guars: Vec::new(), delayed_bugs: Vec::new(), @@ -1360,7 +1347,7 @@ impl DiagCtxtInner { } self.has_printed = true; - self.emitter.emit_diagnostic(diagnostic, &self.registry); + self.emitter.emit_diagnostic(diagnostic); } if is_error { @@ -1436,13 +1423,13 @@ impl DiagCtxtInner { self.has_errors().or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied()) } - /// Translate `message` eagerly with `args` to `SubdiagMessage::Eager`. + /// Translate `message` eagerly with `args` to `DiagMessage::Eager`. fn eagerly_translate<'a>( &self, message: DiagMessage, args: impl Iterator>, - ) -> SubdiagMessage { - SubdiagMessage::Str(Cow::from(self.eagerly_translate_to_string(message, args))) + ) -> DiagMessage { + DiagMessage::Str(Cow::from(self.eagerly_translate_to_string(message, args))) } /// Translate `message` eagerly with `args` to `String`. @@ -1463,10 +1450,9 @@ impl DiagCtxtInner { fn eagerly_translate_for_subdiag( &self, diag: &DiagInner, - msg: impl Into, - ) -> SubdiagMessage { - let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); - self.eagerly_translate(msg, diag.args.iter()) + msg: impl Into, + ) -> DiagMessage { + self.eagerly_translate(msg.into(), diag.args.iter()) } fn flush_delayed(&mut self) { diff --git a/compiler/rustc_errors/src/registry.rs b/compiler/rustc_errors/src/registry.rs deleted file mode 100644 index baca7700d90e..000000000000 --- a/compiler/rustc_errors/src/registry.rs +++ /dev/null @@ -1,23 +0,0 @@ -use rustc_data_structures::fx::FxHashMap; - -use crate::ErrCode; - -#[derive(Debug)] -pub struct InvalidErrorCode; - -#[derive(Clone)] -pub struct Registry { - long_descriptions: FxHashMap, -} - -impl Registry { - pub fn new(long_descriptions: &[(ErrCode, &'static str)]) -> Registry { - Registry { long_descriptions: long_descriptions.iter().copied().collect() } - } - - /// Returns `InvalidErrorCode` if the code requested does not exist in the - /// registry. - pub fn try_find_description(&self, code: ErrCode) -> Result<&'static str, InvalidErrorCode> { - self.long_descriptions.get(&code).copied().ok_or(InvalidErrorCode) - } -} diff --git a/compiler/rustc_errors/src/tests.rs b/compiler/rustc_errors/src/tests.rs deleted file mode 100644 index 34ebac0fde11..000000000000 --- a/compiler/rustc_errors/src/tests.rs +++ /dev/null @@ -1,182 +0,0 @@ -use std::sync::{Arc, LazyLock}; - -use rustc_data_structures::sync::IntoDynSyncSend; -use rustc_error_messages::fluent_bundle::resolver::errors::{ReferenceKind, ResolverError}; -use rustc_error_messages::{DiagMessage, langid}; - -use crate::FluentBundle; -use crate::error::{TranslateError, TranslateErrorKind}; -use crate::fluent_bundle::*; -use crate::translation::Translator; - -fn make_translator(ftl: &'static str) -> Translator { - let resource = FluentResource::try_new(ftl.into()).expect("Failed to parse an FTL string."); - - let langid_en = langid!("en-US"); - - let mut bundle: FluentBundle = - IntoDynSyncSend(crate::fluent_bundle::bundle::FluentBundle::new_concurrent(vec![ - langid_en, - ])); - - bundle.add_resource(resource).expect("Failed to add FTL resources to the bundle."); - - Translator { - fluent_bundle: None, - fallback_fluent_bundle: Arc::new(LazyLock::new(Box::new(|| bundle))), - } -} - -#[test] -fn wellformed_fluent() { - let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value - .label = value moved into `{$name}` here - .occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait - .value_borrowed_label = value borrowed here after move - .suggestion = borrow this binding in the pattern to avoid moving the value"); - - let mut args = FluentArgs::new(); - args.set("name", "Foo"); - args.set("ty", "std::string::String"); - { - let message = DiagMessage::FluentIdentifier( - "mir_build_borrow_of_moved_value".into(), - Some("suggestion".into()), - ); - - assert_eq!( - translator.translate_message(&message, &args).unwrap(), - "borrow this binding in the pattern to avoid moving the value" - ); - } - - { - let message = DiagMessage::FluentIdentifier( - "mir_build_borrow_of_moved_value".into(), - Some("value_borrowed_label".into()), - ); - - assert_eq!( - translator.translate_message(&message, &args).unwrap(), - "value borrowed here after move" - ); - } - - { - let message = DiagMessage::FluentIdentifier( - "mir_build_borrow_of_moved_value".into(), - Some("occurs_because_label".into()), - ); - - assert_eq!( - translator.translate_message(&message, &args).unwrap(), - "move occurs because `\u{2068}Foo\u{2069}` has type `\u{2068}std::string::String\u{2069}` which does not implement the `Copy` trait" - ); - - { - let message = DiagMessage::FluentIdentifier( - "mir_build_borrow_of_moved_value".into(), - Some("label".into()), - ); - - assert_eq!( - translator.translate_message(&message, &args).unwrap(), - "value moved into `\u{2068}Foo\u{2069}` here" - ); - } - } -} - -#[test] -fn misformed_fluent() { - let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value - .label = value moved into `{name}` here - .occurs_because_label = move occurs because `{$oops}` has type `{$ty}` which does not implement the `Copy` trait - .suggestion = borrow this binding in the pattern to avoid moving the value"); - - let mut args = FluentArgs::new(); - args.set("name", "Foo"); - args.set("ty", "std::string::String"); - { - let message = DiagMessage::FluentIdentifier( - "mir_build_borrow_of_moved_value".into(), - Some("value_borrowed_label".into()), - ); - - let err = translator.translate_message(&message, &args).unwrap_err(); - assert!( - matches!( - &err, - TranslateError::Two { - primary: box TranslateError::One { - kind: TranslateErrorKind::PrimaryBundleMissing, - .. - }, - fallback: box TranslateError::One { - kind: TranslateErrorKind::AttributeMissing { attr: "value_borrowed_label" }, - .. - } - } - ), - "{err:#?}" - ); - assert_eq!( - format!("{err}"), - "failed while formatting fluent string `mir_build_borrow_of_moved_value`: \nthe attribute `value_borrowed_label` was missing\nhelp: add `.value_borrowed_label = `\n" - ); - } - - { - let message = DiagMessage::FluentIdentifier( - "mir_build_borrow_of_moved_value".into(), - Some("label".into()), - ); - - let err = translator.translate_message(&message, &args).unwrap_err(); - if let TranslateError::Two { - primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. }, - fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. }, - } = &err - && let [ - FluentError::ResolverError(ResolverError::Reference( - ReferenceKind::Message { id, .. } | ReferenceKind::Variable { id, .. }, - )), - ] = &**errs - && id == "name" - { - } else { - panic!("{err:#?}") - }; - assert_eq!( - format!("{err}"), - "failed while formatting fluent string `mir_build_borrow_of_moved_value`: \nargument `name` exists but was not referenced correctly\nhelp: try using `{$name}` instead\n" - ); - } - - { - let message = DiagMessage::FluentIdentifier( - "mir_build_borrow_of_moved_value".into(), - Some("occurs_because_label".into()), - ); - - let err = translator.translate_message(&message, &args).unwrap_err(); - if let TranslateError::Two { - primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. }, - fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. }, - } = &err - && let [ - FluentError::ResolverError(ResolverError::Reference( - ReferenceKind::Message { id, .. } | ReferenceKind::Variable { id, .. }, - )), - ] = &**errs - && id == "oops" - { - } else { - panic!("{err:#?}") - }; - assert_eq!( - format!("{err}"), - "failed while formatting fluent string `mir_build_borrow_of_moved_value`: \nthe fluent string has an argument `oops` that was not found.\nhelp: the arguments `name` and `ty` are available\n" - ); - } -} diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index aba79c30f3a3..b5de3f87e9f3 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -1,13 +1,12 @@ use std::borrow::Cow; -use std::env; use std::error::Report; use std::sync::Arc; -use rustc_error_messages::langid; pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle}; +use rustc_error_messages::{langid, register_functions}; use tracing::{debug, trace}; -use crate::error::{TranslateError, TranslateErrorKind}; +use crate::error::TranslateError; use crate::fluent_bundle::FluentResource; use crate::{DiagArg, DiagMessage, FluentBundle, Style, fluent_bundle}; @@ -35,24 +34,11 @@ pub struct Translator { /// Localized diagnostics for the locale requested by the user. If no language was requested by /// the user then this will be `None` and `fallback_fluent_bundle` should be used. pub fluent_bundle: Option>, - /// Return `FluentBundle` with localized diagnostics for the default locale of the compiler. - /// Used when the user has not requested a specific language or when a localized diagnostic is - /// unavailable for the requested locale. - pub fallback_fluent_bundle: LazyFallbackBundle, } impl Translator { - pub fn with_fallback_bundle( - resources: Vec<&'static str>, - with_directionality_markers: bool, - ) -> Translator { - Translator { - fluent_bundle: None, - fallback_fluent_bundle: crate::fallback_fluent_bundle( - resources, - with_directionality_markers, - ), - } + pub fn new() -> Translator { + Translator { fluent_bundle: None } } /// Convert `DiagMessage`s to a string, performing translation if necessary. @@ -76,11 +62,8 @@ impl Translator { args: &'a FluentArgs<'_>, ) -> Result, TranslateError<'a>> { trace!(?message, ?args); - let (identifier, attr) = match message { - DiagMessage::Str(msg) => { - return Ok(Cow::Borrowed(msg)); - } - DiagMessage::FluentIdentifier(identifier, attr) => (identifier, attr), + match message { + DiagMessage::Str(msg) => Ok(Cow::Borrowed(msg)), // This translates an inline fluent diagnostic message // It does this by creating a new `FluentBundle` with only one message, // and then translating using this bundle. @@ -91,77 +74,18 @@ impl Translator { let mut bundle = fluent_bundle::FluentBundle::new(vec![langid!("en-US")]); bundle.set_use_isolating(false); bundle.add_resource(resource).unwrap(); + register_functions(&mut bundle); let message = bundle.get_message(GENERATED_MSG_ID).unwrap(); let value = message.value().unwrap(); let mut errs = vec![]; let translated = bundle.format_pattern(value, Some(args), &mut errs).to_string(); debug!(?translated, ?errs); - return if errs.is_empty() { + if errs.is_empty() { Ok(Cow::Owned(translated)) } else { Err(TranslateError::fluent(&Cow::Borrowed(GENERATED_MSG_ID), args, errs)) - }; - } - }; - let translate_with_bundle = - |bundle: &'a FluentBundle| -> Result, TranslateError<'_>> { - let message = bundle - .get_message(identifier) - .ok_or(TranslateError::message(identifier, args))?; - let value = match attr { - Some(attr) => message - .get_attribute(attr) - .ok_or(TranslateError::attribute(identifier, args, attr))? - .value(), - None => message.value().ok_or(TranslateError::value(identifier, args))?, - }; - debug!(?message, ?value); - - let mut errs = vec![]; - let translated = bundle.format_pattern(value, Some(args), &mut errs); - debug!(?translated, ?errs); - if errs.is_empty() { - Ok(translated) - } else { - Err(TranslateError::fluent(identifier, args, errs)) } - }; - - try { - match self.fluent_bundle.as_ref().map(|b| translate_with_bundle(b)) { - // The primary bundle was present and translation succeeded - Some(Ok(t)) => t, - - // If `translate_with_bundle` returns `Err` with the primary bundle, this is likely - // just that the primary bundle doesn't contain the message being translated, so - // proceed to the fallback bundle. - Some(Err( - primary @ TranslateError::One { - kind: TranslateErrorKind::MessageMissing, .. - }, - )) => translate_with_bundle(&self.fallback_fluent_bundle) - .map_err(|fallback| primary.and(fallback))?, - - // Always yeet out for errors on debug (unless - // `RUSTC_TRANSLATION_NO_DEBUG_ASSERT` is set in the environment - this allows - // local runs of the test suites, of builds with debug assertions, to test the - // behaviour in a normal build). - Some(Err(primary)) - if cfg!(debug_assertions) - && env::var("RUSTC_TRANSLATION_NO_DEBUG_ASSERT").is_err() => - { - do yeet primary - } - - // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so - // just hide it and try with the fallback bundle. - Some(Err(primary)) => translate_with_bundle(&self.fallback_fluent_bundle) - .map_err(|fallback| primary.and(fallback))?, - - // The primary bundle is missing, proceed to the fallback bundle - None => translate_with_bundle(&self.fallback_fluent_bundle) - .map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?, } } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 6b3c7fe97932..91f98d475f00 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -962,15 +962,6 @@ impl SyntaxExtension { let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability); - // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem - if let Some(sp) = - find_attr!(attrs, AttributeKind::RustcConstStability { span, .. } => *span) - { - sess.dcx().emit_err(errors::MacroConstStability { - span: sp, - head_span: sess.source_map().guess_head_span(span), - }); - } if let Some(sp) = find_attr!(attrs, AttributeKind::RustcBodyStability{ span, .. } => *span) { sess.dcx().emit_err(errors::MacroBodyStability { diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index e5c06889f3e0..7320ee12abe3 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -727,7 +727,7 @@ impl<'a> ExtCtxt<'a> { span: Span, ident: Ident, ty: Box, - rhs: ast::ConstItemRhs, + rhs_kind: ast::ConstItemRhsKind, ) -> Box { let defaultness = ast::Defaultness::Final; self.item( @@ -740,7 +740,7 @@ impl<'a> ExtCtxt<'a> { // FIXME(generic_const_items): Pass the generics as a parameter. generics: ast::Generics::default(), ty, - rhs: Some(rhs), + rhs_kind, define_opaque: None, } .into(), diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index e64bfc136d16..b6fcc13321ee 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -80,16 +80,6 @@ pub(crate) struct ResolveRelativePath { pub path: String, } -#[derive(Diagnostic)] -#[diag("macros cannot have const stability attributes")] -pub(crate) struct MacroConstStability { - #[primary_span] - #[label("invalid const stability attribute")] - pub span: Span, - #[label("const stability attribute affects this macro")] - pub head_span: Span, -} - #[derive(Diagnostic)] #[diag("macros cannot have body stability attributes")] pub(crate) struct MacroBodyStability { diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 12490880ab0e..947b8a6e3e5e 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -7,7 +7,7 @@ use rustc_ast::tokenstream::{self, DelimSpacing, Spacing, TokenStream}; use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult}; +use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan}; use rustc_parse::lexer::{StripTokens, nfc_normalize}; use rustc_parse::parser::Parser; use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; @@ -591,7 +591,7 @@ impl server::Server for Rustc<'_, '_> { fn ts_expand_expr(&mut self, stream: &Self::TokenStream) -> Result { // Parse the expression from our tokenstream. - let expr: PResult<'_, _> = try { + let expr = try { let mut p = Parser::new(self.psess(), stream.clone(), Some("proc_macro expand expr")); let expr = p.parse_expr()?; if p.token != token::Eof { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 182ef6816337..d74e8442545b 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -888,13 +888,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::Yes, experimental!(patchable_function_entry) ), - // Probably temporary component of min_generic_const_args. - // `#[type_const] const ASSOC: usize;` - gated!( - type_const, Normal, template!(Word), ErrorFollowing, - EncodeCrossCrate::Yes, min_generic_const_args, experimental!(type_const), - ), - // The `#[loop_match]` and `#[const_continue]` attributes are part of the // lang experiment for RFC 3720 tracked in: // diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 4ba50d03d707..619726f0d5d8 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -136,5 +136,6 @@ pub use builtin_attrs::{ }; pub use removed::REMOVED_LANG_FEATURES; pub use unstable::{ - EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, UNSTABLE_LANG_FEATURES, + DEPENDENT_FEATURES, EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, + UNSTABLE_LANG_FEATURES, }; diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index c99af9658cde..add09c3ea58b 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -373,6 +373,8 @@ declare_features! ( (unstable, async_for_loop, "1.77.0", Some(118898)), /// Allows `async` trait bound modifier. (unstable, async_trait_bounds, "1.85.0", Some(62290)), + /// Target features on avr. + (unstable, avr_target_feature, "CURRENT_RUSTC_VERSION", Some(146889)), /// Allows using Intel AVX10 target features and intrinsics (unstable, avx10_target_feature, "1.88.0", Some(138843)), /// Target features on bpf. @@ -392,6 +394,8 @@ declare_features! ( (unstable, cfg_sanitize, "1.41.0", Some(39699)), /// Allows `cfg(sanitizer_cfi_generalize_pointers)` and `cfg(sanitizer_cfi_normalize_integers)`. (unstable, cfg_sanitizer_cfi, "1.77.0", Some(89653)), + /// Provides a native way to easily manage multiple conditional flags without having to rewrite each clause multiple times. + (unstable, cfg_select, "CURRENT_RUSTC_VERSION", Some(115585)), /// Allows `cfg(target(abi = "..."))`. (unstable, cfg_target_compact, "1.63.0", Some(96901)), /// Allows `cfg(target_has_atomic_load_store = "...")`. @@ -557,6 +561,8 @@ declare_features! ( (unstable, macro_metavar_expr_concat, "1.81.0", Some(124225)), /// Allows `#[marker]` on certain traits allowing overlapping implementations. (unstable, marker_trait_attr, "1.30.0", Some(29864)), + /// Enable mgca `type const` syntax before expansion. + (incomplete, mgca_type_const_syntax, "CURRENT_RUSTC_VERSION", Some(132980)), /// Enables the generic const args MVP (only bare paths, not arbitrary computation). (incomplete, min_generic_const_args, "1.84.0", Some(132980)), /// A minimal, sound subset of specialization intended to be used by the @@ -601,6 +607,8 @@ declare_features! ( (unstable, offset_of_enum, "1.75.0", Some(120141)), /// Allows using fields with slice type in offset_of! (unstable, offset_of_slice, "1.81.0", Some(126151)), + /// Allows using generics in more complex const expressions, based on definitional equality. + (unstable, opaque_generic_const_args, "CURRENT_RUSTC_VERSION", Some(151972)), /// Allows using `#[optimize(X)]`. (unstable, optimize_attribute, "1.34.0", Some(54882)), /// Allows specifying nop padding on functions for dynamic patching. @@ -778,3 +786,9 @@ pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[ // boolean logic required to tell which typing rules to use. (sym::ref_pat_eat_one_layer_2024, sym::ref_pat_eat_one_layer_2024_structural), ]; + +/// Some features require one or more other features to be enabled. +pub const DEPENDENT_FEATURES: &[(Symbol, &[Symbol])] = &[ + (sym::opaque_generic_const_args, &[sym::min_generic_const_args]), + (sym::unsized_const_params, &[sym::adt_const_params]), +]; diff --git a/compiler/rustc_fluent_macro/Cargo.toml b/compiler/rustc_fluent_macro/Cargo.toml deleted file mode 100644 index d7ef4280aef0..000000000000 --- a/compiler/rustc_fluent_macro/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "rustc_fluent_macro" -version = "0.0.0" -edition = "2024" - -[lib] -proc-macro = true - -[dependencies] -# tidy-alphabetical-start -annotate-snippets = "0.11" -fluent-bundle = "0.16" -fluent-syntax = "0.12" -proc-macro2 = "1" -quote = "1" -syn = { version = "2", features = ["full"] } -unic-langid = { version = "0.9.0", features = ["macros"] } -# tidy-alphabetical-end diff --git a/compiler/rustc_fluent_macro/src/fluent.rs b/compiler/rustc_fluent_macro/src/fluent.rs deleted file mode 100644 index 41357bb1ac76..000000000000 --- a/compiler/rustc_fluent_macro/src/fluent.rs +++ /dev/null @@ -1,315 +0,0 @@ -use std::collections::{HashMap, HashSet}; -use std::fs::read_to_string; -use std::path::{Path, PathBuf}; - -use annotate_snippets::{Renderer, Snippet}; -use fluent_bundle::{FluentBundle, FluentError, FluentResource}; -use fluent_syntax::ast::{ - Attribute, Entry, Expression, Identifier, InlineExpression, Message, Pattern, PatternElement, -}; -use fluent_syntax::parser::ParserError; -use proc_macro::tracked::path; -use proc_macro::{Diagnostic, Level, Span}; -use proc_macro2::TokenStream; -use quote::quote; -use syn::{Ident, LitStr, parse_macro_input}; -use unic_langid::langid; - -/// Helper function for returning an absolute path for macro-invocation relative file paths. -/// -/// If the input is already absolute, then the input is returned. If the input is not absolute, -/// then it is appended to the directory containing the source file with this macro invocation. -fn invocation_relative_path_to_absolute(span: Span, path: &str) -> PathBuf { - let path = Path::new(path); - if path.is_absolute() { - path.to_path_buf() - } else { - // `/a/b/c/foo/bar.rs` contains the current macro invocation - let mut source_file_path = span.local_file().unwrap(); - // `/a/b/c/foo/` - source_file_path.pop(); - // `/a/b/c/foo/../locales/en-US/example.ftl` - source_file_path.push(path); - source_file_path - } -} - -/// Final tokens. -fn finish(body: TokenStream, resource: TokenStream) -> proc_macro::TokenStream { - quote! { - /// Raw content of Fluent resource for this crate, generated by `fluent_messages` macro, - /// imported by `rustc_driver` to include all crates' resources in one bundle. - pub static DEFAULT_LOCALE_RESOURCE: &'static str = #resource; - - #[allow(non_upper_case_globals)] - #[doc(hidden)] - /// Auto-generated constants for type-checked references to Fluent messages. - pub(crate) mod fluent_generated { - #body - - /// Constants expected to exist by the diagnostic derive macros to use as default Fluent - /// identifiers for different subdiagnostic kinds. - pub mod _subdiag { - /// Default for `#[help]` - pub const help: rustc_errors::SubdiagMessage = - rustc_errors::SubdiagMessage::FluentAttr(std::borrow::Cow::Borrowed("help")); - /// Default for `#[note]` - pub const note: rustc_errors::SubdiagMessage = - rustc_errors::SubdiagMessage::FluentAttr(std::borrow::Cow::Borrowed("note")); - /// Default for `#[warn]` - pub const warn: rustc_errors::SubdiagMessage = - rustc_errors::SubdiagMessage::FluentAttr(std::borrow::Cow::Borrowed("warn")); - /// Default for `#[label]` - pub const label: rustc_errors::SubdiagMessage = - rustc_errors::SubdiagMessage::FluentAttr(std::borrow::Cow::Borrowed("label")); - /// Default for `#[suggestion]` - pub const suggestion: rustc_errors::SubdiagMessage = - rustc_errors::SubdiagMessage::FluentAttr(std::borrow::Cow::Borrowed("suggestion")); - } - } - } - .into() -} - -/// Tokens to be returned when the macro cannot proceed. -fn failed(crate_name: &Ident) -> proc_macro::TokenStream { - finish(quote! { pub mod #crate_name {} }, quote! { "" }) -} - -/// See [rustc_fluent_macro::fluent_messages]. -pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let crate_name = std::env::var("CARGO_CRATE_NAME") - // If `CARGO_CRATE_NAME` is missing, then we're probably running in a test, so use - // `no_crate`. - .unwrap_or_else(|_| "no_crate".to_string()) - .replace("rustc_", ""); - - // Cannot iterate over individual messages in a bundle, so do that using the - // `FluentResource` instead. Construct a bundle anyway to find out if there are conflicting - // messages in the resources. - let mut bundle = FluentBundle::new(vec![langid!("en-US")]); - - // Set of Fluent attribute names already output, to avoid duplicate type errors - any given - // constant created for a given attribute is the same. - let mut previous_attrs = HashSet::new(); - - let resource_str = parse_macro_input!(input as LitStr); - let resource_span = resource_str.span().unwrap(); - let relative_ftl_path = resource_str.value(); - let absolute_ftl_path = invocation_relative_path_to_absolute(resource_span, &relative_ftl_path); - - let crate_name = Ident::new(&crate_name, resource_str.span()); - - path(absolute_ftl_path.to_str().unwrap()); - let resource_contents = match read_to_string(absolute_ftl_path) { - Ok(resource_contents) => resource_contents, - Err(e) => { - Diagnostic::spanned( - resource_span, - Level::Error, - format!("could not open Fluent resource: {e}"), - ) - .emit(); - return failed(&crate_name); - } - }; - let mut bad = false; - for esc in ["\\n", "\\\"", "\\'"] { - for _ in resource_contents.matches(esc) { - bad = true; - Diagnostic::spanned(resource_span, Level::Error, format!("invalid escape `{esc}` in Fluent resource")) - .note("Fluent does not interpret these escape sequences ()") - .emit(); - } - } - if bad { - return failed(&crate_name); - } - - let resource = match FluentResource::try_new(resource_contents) { - Ok(resource) => resource, - Err((this, errs)) => { - Diagnostic::spanned(resource_span, Level::Error, "could not parse Fluent resource") - .help("see additional errors emitted") - .emit(); - for ParserError { pos, slice: _, kind } in errs { - let mut err = kind.to_string(); - // Entirely unnecessary string modification so that the error message starts - // with a lowercase as rustc errors do. - err.replace_range(0..1, &err.chars().next().unwrap().to_lowercase().to_string()); - - let message = annotate_snippets::Level::Error.title(&err).snippet( - Snippet::source(this.source()) - .origin(&relative_ftl_path) - .fold(true) - .annotation(annotate_snippets::Level::Error.span(pos.start..pos.end - 1)), - ); - let renderer = Renderer::plain(); - eprintln!("{}\n", renderer.render(message)); - } - - return failed(&crate_name); - } - }; - - let mut constants = TokenStream::new(); - let mut previous_defns = HashMap::new(); - let mut message_refs = Vec::new(); - for entry in resource.entries() { - if let Entry::Message(msg) = entry { - let Message { id: Identifier { name }, attributes, value, .. } = msg; - let _ = previous_defns.entry(name.to_string()).or_insert(resource_span); - if name.contains('-') { - Diagnostic::spanned( - resource_span, - Level::Error, - format!("name `{name}` contains a '-' character"), - ) - .help("replace any '-'s with '_'s") - .emit(); - } - - if let Some(Pattern { elements }) = value { - for elt in elements { - if let PatternElement::Placeable { - expression: - Expression::Inline(InlineExpression::MessageReference { id, .. }), - } = elt - { - message_refs.push((id.name, *name)); - } - } - } - - // `typeck_foo_bar` => `foo_bar` (in `typeck.ftl`) - // `const_eval_baz` => `baz` (in `const_eval.ftl`) - // `const-eval-hyphen-having` => `hyphen_having` (in `const_eval.ftl`) - // The last case we error about above, but we want to fall back gracefully - // so that only the error is being emitted and not also one about the macro - // failing. - let crate_prefix = format!("{crate_name}_"); - - let snake_name = name.replace('-', "_"); - if !snake_name.starts_with(&crate_prefix) { - Diagnostic::spanned( - resource_span, - Level::Error, - format!("name `{name}` does not start with the crate name"), - ) - .help(format!( - "prepend `{crate_prefix}` to the slug name: `{crate_prefix}{snake_name}`" - )) - .emit(); - }; - let snake_name = Ident::new(&snake_name, resource_str.span()); - - if !previous_attrs.insert(snake_name.clone()) { - continue; - } - - let docstr = - format!("Constant referring to Fluent message `{name}` from `{crate_name}`"); - constants.extend(quote! { - #[doc = #docstr] - pub const #snake_name: rustc_errors::DiagMessage = - rustc_errors::DiagMessage::FluentIdentifier( - std::borrow::Cow::Borrowed(#name), - None - ); - }); - - for Attribute { id: Identifier { name: attr_name }, .. } in attributes { - let snake_name = Ident::new( - &format!("{crate_prefix}{}", attr_name.replace('-', "_")), - resource_str.span(), - ); - if !previous_attrs.insert(snake_name.clone()) { - continue; - } - - if attr_name.contains('-') { - Diagnostic::spanned( - resource_span, - Level::Error, - format!("attribute `{attr_name}` contains a '-' character"), - ) - .help("replace any '-'s with '_'s") - .emit(); - } - - let msg = format!( - "Constant referring to Fluent message `{name}.{attr_name}` from `{crate_name}`" - ); - constants.extend(quote! { - #[doc = #msg] - pub const #snake_name: rustc_errors::SubdiagMessage = - rustc_errors::SubdiagMessage::FluentAttr(std::borrow::Cow::Borrowed(#attr_name)); - }); - } - - // Record variables referenced by these messages so we can produce - // tests in the derive diagnostics to validate them. - let ident = quote::format_ident!("{snake_name}_refs"); - let vrefs = variable_references(msg); - constants.extend(quote! { - #[cfg(test)] - pub const #ident: &[&str] = &[#(#vrefs),*]; - }) - } - } - - for (mref, name) in message_refs.into_iter() { - if !previous_defns.contains_key(mref) { - Diagnostic::spanned( - resource_span, - Level::Error, - format!("referenced message `{mref}` does not exist (in message `{name}`)"), - ) - .help(format!("you may have meant to use a variable reference (`{{${mref}}}`)")) - .emit(); - } - } - - if let Err(errs) = bundle.add_resource(resource) { - for e in errs { - match e { - FluentError::Overriding { kind, id } => { - Diagnostic::spanned( - resource_span, - Level::Error, - format!("overrides existing {kind}: `{id}`"), - ) - .emit(); - } - FluentError::ResolverError(_) | FluentError::ParserError(_) => unreachable!(), - } - } - } - - finish(constants, quote! { include_str!(#relative_ftl_path) }) -} - -fn variable_references<'a>(msg: &Message<&'a str>) -> Vec<&'a str> { - let mut refs = vec![]; - if let Some(Pattern { elements }) = &msg.value { - for elt in elements { - if let PatternElement::Placeable { - expression: Expression::Inline(InlineExpression::VariableReference { id }), - } = elt - { - refs.push(id.name); - } - } - } - for attr in &msg.attributes { - for elt in &attr.value.elements { - if let PatternElement::Placeable { - expression: Expression::Inline(InlineExpression::VariableReference { id }), - } = elt - { - refs.push(id.name); - } - } - } - refs -} diff --git a/compiler/rustc_fluent_macro/src/lib.rs b/compiler/rustc_fluent_macro/src/lib.rs deleted file mode 100644 index 9cd947c4c672..000000000000 --- a/compiler/rustc_fluent_macro/src/lib.rs +++ /dev/null @@ -1,67 +0,0 @@ -// tidy-alphabetical-start -#![allow(rustc::default_hash_types)] -#![feature(proc_macro_diagnostic)] -#![feature(proc_macro_tracked_path)] -// tidy-alphabetical-end - -use proc_macro::TokenStream; - -mod fluent; - -/// Implements the `fluent_messages` macro, which performs compile-time validation of the -/// compiler's Fluent resources (i.e. that the resources parse and don't multiply define the same -/// messages) and generates constants that make using those messages in diagnostics more ergonomic. -/// -/// For example, given the following invocation of the macro.. -/// -/// ```ignore (rust) -/// fluent_messages! { "./typeck.ftl" } -/// ``` -/// ..where `typeck.ftl` has the following contents.. -/// -/// ```fluent -/// typeck_field_multiply_specified_in_initializer = -/// field `{$ident}` specified more than once -/// .label = used more than once -/// .label_previous_use = first use of `{$ident}` -/// ``` -/// ...then the macro parse the Fluent resource, emitting a diagnostic if it fails to do so, and -/// will generate the following code: -/// -/// ```ignore (rust) -/// pub static DEFAULT_LOCALE_RESOURCE: &'static [&'static str] = include_str!("./typeck.ftl"); -/// -/// mod fluent_generated { -/// mod typeck { -/// pub const field_multiply_specified_in_initializer: DiagMessage = -/// DiagMessage::fluent("typeck_field_multiply_specified_in_initializer"); -/// pub const field_multiply_specified_in_initializer_label_previous_use: DiagMessage = -/// DiagMessage::fluent_attr( -/// "typeck_field_multiply_specified_in_initializer", -/// "previous_use_label" -/// ); -/// } -/// } -/// ``` -/// When emitting a diagnostic, the generated constants can be used as follows: -/// -/// ```ignore (rust) -/// let mut err = sess.struct_span_err( -/// span, -/// fluent::typeck::field_multiply_specified_in_initializer -/// ); -/// err.span_default_label(span); -/// err.span_label( -/// previous_use_span, -/// fluent::typeck::field_multiply_specified_in_initializer_label_previous_use -/// ); -/// err.emit(); -/// ``` -/// -/// Note: any crate using this macro must also have a dependency on -/// `rustc_errors`, because the generated code refers to things from that -/// crate. -#[proc_macro] -pub fn fluent_messages(input: TokenStream) -> TokenStream { - fluent::fluent_messages(input) -} diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index a444b5e4badf..e28ecd06b89b 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -5,7 +5,7 @@ pub use ReprAttr::*; use rustc_abi::Align; pub use rustc_ast::attr::data_structures::*; use rustc_ast::token::DocFragmentKind; -use rustc_ast::{AttrStyle, ast}; +use rustc_ast::{AttrStyle, Path, ast}; use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; @@ -49,6 +49,57 @@ pub struct EiiDecl { pub name: Ident, } +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)] +pub enum CguKind { + No, + PreDashLto, + PostDashLto, + Any, +} + +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)] +pub enum CguFields { + PartitionReused { cfg: Symbol, module: Symbol }, + PartitionCodegened { cfg: Symbol, module: Symbol }, + ExpectedCguReuse { cfg: Symbol, module: Symbol, kind: CguKind }, +} + +#[derive(Copy, Clone, PartialEq, Debug, PrintAttribute)] +#[derive(HashStable_Generic, Encodable, Decodable)] +pub enum DivergingFallbackBehavior { + /// Always fallback to `()` (aka "always spontaneous decay") + ToUnit, + /// Always fallback to `!` (which should be equivalent to never falling back + not making + /// never-to-any coercions unless necessary) + ToNever, + /// Don't fallback at all + NoFallback, +} + +#[derive(Copy, Clone, PartialEq, Debug, PrintAttribute, Default)] +#[derive(HashStable_Generic, Encodable, Decodable)] +pub enum DivergingBlockBehavior { + /// This is the current stable behavior: + /// + /// ```rust + /// { + /// return; + /// } // block has type = !, even though we are supposedly dropping it with `;` + /// ``` + #[default] + Never, + + /// Alternative behavior: + /// + /// ```ignore (very-unstable-new-attribute) + /// #![rustc_never_type_options(diverging_block_default = "unit")] + /// { + /// return; + /// } // block has type = (), since we are dropping `!` from `return` with `;` + /// ``` + Unit, +} + #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)] pub enum InlineAttr { None, @@ -167,6 +218,14 @@ pub enum CoverageAttrKind { Off, } +/// Successfully-parsed value of a `#[rustc_abi(..)]` attribute. +#[derive(Copy, Debug, Eq, PartialEq, Encodable, Decodable, Clone)] +#[derive(HashStable_Generic, PrintAttribute)] +pub enum RustcAbiAttrKind { + Debug, + AssertEq, +} + impl Deprecation { /// Whether an item marked with #[deprecated(since = "X")] is currently /// deprecated (i.e., whether X is not greater than the current rustc @@ -994,6 +1053,9 @@ pub enum AttributeKind { /// Represents `#[pointee]` Pointee(Span), + /// Represents `#[prelude_import]` + PreludeImport, + /// Represents `#[proc_macro]` ProcMacro(Span), @@ -1015,6 +1077,9 @@ pub enum AttributeKind { /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span }, + /// Represents `#[rustc_abi(..)]` + RustcAbi { attr_span: Span, kind: RustcAbiAttrKind }, + /// Represents `#[rustc_allocator]` RustcAllocator, @@ -1042,6 +1107,12 @@ pub enum AttributeKind { /// Represents `#[rustc_builtin_macro]`. RustcBuiltinMacro { builtin_name: Option, helper_attrs: ThinVec, span: Span }, + /// Represents `#[rustc_capture_analysis]` + RustcCaptureAnalysis, + + /// Represents `#[rustc_expected_cgu_reuse]`, `#[rustc_partition_codegened]` and `#[rustc_partition_reused]`. + RustcCguTestAttr(ThinVec<(Span, CguFields)>), + /// Represents `#[rustc_clean]` RustcClean(ThinVec), @@ -1067,12 +1138,24 @@ pub enum AttributeKind { /// Represents `#[rustc_const_stable_indirect]`. RustcConstStabilityIndirect, + /// Represents `#[rustc_conversion_suggestion]` + RustcConversionSuggestion, + /// Represents `#[rustc_deallocator]` RustcDeallocator, + /// Represents `#[rustc_def_path]` + RustcDefPath(Span), + + /// Represents `#[rustc_delayed_bug_from_inside_query]` + RustcDelayedBugFromInsideQuery, + /// Represents `#[rustc_deny_explicit_impl]`. RustcDenyExplicitImpl(Span), + /// Represents `#[rustc_deprecated_safe_2024]` + RustcDeprecatedSafe2024 { suggestion: Symbol }, + /// Represents `#[rustc_dummy]`. RustcDummy, @@ -1094,6 +1177,12 @@ pub enum AttributeKind { /// Represents `#[rustc_dyn_incompatible_trait]`. RustcDynIncompatibleTrait(Span), + /// Represents `#[rustc_effective_visibility]`. + RustcEffectiveVisibility, + + /// Represents `#[rustc_evaluate_where_clauses]` + RustcEvaluateWhereClauses, + /// Represents `#[rustc_has_incoherent_inherent_impls]` RustcHasIncoherentInherentImpls, @@ -1103,6 +1192,15 @@ pub enum AttributeKind { /// Represents `#[rustc_if_this_changed]` RustcIfThisChanged(Span, Option), + /// Represents `#[rustc_insignificant_dtor]` + RustcInsignificantDtor, + + /// Represents `#[rustc_intrinsic]` + RustcIntrinsic, + + /// Represents `#[rustc_intrinsic_const_stable_indirect]` + RustcIntrinsicConstStableIndirect, + /// Represents `#[rustc_layout]` RustcLayout(ThinVec), @@ -1142,9 +1240,21 @@ pub enum AttributeKind { /// Represents `#[rustc_never_returns_null_ptr]` RustcNeverReturnsNullPointer, + /// Represents `#[rustc_never_type_options]`. + RustcNeverTypeOptions { + fallback: Option, + diverging_block_default: Option, + }, + /// Represents `#[rustc_no_implicit_autorefs]` RustcNoImplicitAutorefs, + /// Represents `#[rustc_no_implicit_bounds]` + RustcNoImplicitBounds, + + /// Represents `#[rustc_no_mir_inline]` + RustcNoMirInline, + /// Represents `#[rustc_non_const_trait_method]`. RustcNonConstTraitMethod, @@ -1163,6 +1273,9 @@ pub enum AttributeKind { /// Represents `#[rustc_offload_kernel]` RustcOffloadKernel, + /// Represents `#[rustc_outlives]` + RustcOutlives, + /// Represents `#[rustc_paren_sugar]`. RustcParenSugar(Span), @@ -1181,6 +1294,12 @@ pub enum AttributeKind { /// Represents `#[rustc_reallocator]` RustcReallocator, + /// Represents `#[rustc_regions]` + RustcRegions, + + /// Represents `#[rustc_reservation_impl]` + RustcReservationImpl(Span, Symbol), + /// Represents `#[rustc_scalable_vector(N)]` RustcScalableVector { /// The base multiple of lanes that are in a scalable vector, if provided. `element_count` @@ -1204,9 +1323,18 @@ pub enum AttributeKind { /// Represents `#[rustc_std_internal_symbol]`. RustcStdInternalSymbol(Span), + /// Represents `#[rustc_strict_coherence]`. + RustcStrictCoherence(Span), + + /// Represents `#[rustc_symbol_name]` + RustcSymbolName(Span), + /// Represents `#[rustc_then_this_would_need]` RustcThenThisWouldNeed(Span, ThinVec), + /// Represents `#[rustc_trivial_field_reads]` + RustcTrivialFieldReads, + /// Represents `#[rustc_unsafe_specialization_marker]`. RustcUnsafeSpecializationMarker(Span), @@ -1237,19 +1365,20 @@ pub enum AttributeKind { /// Span of the attribute. span: Span, }, + /// Represents `#[target_feature(enable = "...")]` and /// `#[unsafe(force_target_feature(enable = "...")]`. TargetFeature { features: ThinVec<(Symbol, Span)>, attr_span: Span, was_forced: bool }, + /// Represents `#![test_runner(path)]` + TestRunner(Path), + /// Represents `#[thread_local]` ThreadLocal, /// Represents `#[track_caller]` TrackCaller(Span), - /// Represents `#[type_const]`. - TypeConst(Span), - /// Represents `#[type_length_limit]` TypeLengthLimit { attr_span: Span, limit_span: Span, limit: Limit }, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index b6249e1e2ec2..b4cf244bfb8a 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -82,6 +82,7 @@ impl AttributeKind { PatternComplexityLimit { .. } => No, PinV2(..) => Yes, Pointee(..) => No, + PreludeImport => No, ProcMacro(..) => No, ProcMacroAttribute(..) => No, ProcMacroDerive { .. } => No, @@ -89,6 +90,7 @@ impl AttributeKind { RecursionLimit { .. } => No, ReexportTestHarnessMain(..) => No, Repr { .. } => No, + RustcAbi { .. } => No, RustcAllocator => No, RustcAllocatorZeroed => No, RustcAllocatorZeroedVariant { .. } => Yes, @@ -97,14 +99,20 @@ impl AttributeKind { RustcAsPtr(..) => Yes, RustcBodyStability { .. } => No, RustcBuiltinMacro { .. } => Yes, + RustcCaptureAnalysis => No, + RustcCguTestAttr { .. } => No, RustcClean { .. } => No, RustcCoherenceIsCore(..) => No, RustcCoinductive(..) => No, RustcConfusables { .. } => Yes, RustcConstStability { .. } => Yes, RustcConstStabilityIndirect => No, + RustcConversionSuggestion => Yes, RustcDeallocator => No, + RustcDefPath(..) => No, + RustcDelayedBugFromInsideQuery => No, RustcDenyExplicitImpl(..) => No, + RustcDeprecatedSafe2024 { .. } => Yes, RustcDummy => No, RustcDumpDefParents => No, RustcDumpItemBounds => No, @@ -112,9 +120,14 @@ impl AttributeKind { RustcDumpUserArgs => No, RustcDumpVtable(..) => No, RustcDynIncompatibleTrait(..) => No, + RustcEffectiveVisibility => Yes, + RustcEvaluateWhereClauses => Yes, RustcHasIncoherentInherentImpls => Yes, RustcHiddenTypeOfOpaques => No, RustcIfThisChanged(..) => No, + RustcInsignificantDtor => Yes, + RustcIntrinsic => Yes, + RustcIntrinsicConstStableIndirect => No, RustcLayout(..) => No, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, @@ -128,26 +141,35 @@ impl AttributeKind { RustcMir(..) => Yes, RustcMustImplementOneOf { .. } => No, RustcNeverReturnsNullPointer => Yes, + RustcNeverTypeOptions { .. } => No, RustcNoImplicitAutorefs => Yes, + RustcNoImplicitBounds => No, + RustcNoMirInline => Yes, RustcNonConstTraitMethod => No, // should be reported via other queries like `constness` RustcNounwind => No, RustcObjcClass { .. } => No, RustcObjcSelector { .. } => No, RustcObjectLifetimeDefault => No, RustcOffloadKernel => Yes, + RustcOutlives => No, RustcParenSugar(..) => No, RustcPassByValue(..) => Yes, RustcPassIndirectlyInNonRusticAbis(..) => No, RustcPreserveUbChecks => No, RustcPubTransparent(..) => Yes, RustcReallocator => No, + RustcRegions => No, + RustcReservationImpl(..) => Yes, RustcScalableVector { .. } => Yes, RustcShouldNotBeCalledOnConstItems(..) => Yes, RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate RustcSkipDuringMethodDispatch { .. } => No, RustcSpecializationTrait(..) => No, RustcStdInternalSymbol(..) => No, + RustcStrictCoherence(..) => Yes, + RustcSymbolName(..) => Yes, RustcThenThisWouldNeed(..) => No, + RustcTrivialFieldReads => Yes, RustcUnsafeSpecializationMarker(..) => No, RustcVariance => No, RustcVarianceOfOpaques => No, @@ -155,9 +177,9 @@ impl AttributeKind { ShouldPanic { .. } => No, Stability { .. } => Yes, TargetFeature { .. } => No, + TestRunner(..) => Yes, ThreadLocal => No, TrackCaller(..) => Yes, - TypeConst(..) => Yes, TypeLengthLimit { .. } => No, UnstableFeatureBound(..) => No, Used { .. } => No, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index 20efd72e20f7..8fce52901015 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -3,6 +3,7 @@ use std::ops::Deref; use std::path::PathBuf; use rustc_abi::Align; +use rustc_ast::ast::{Path, join_path_idents}; use rustc_ast::attr::data_structures::CfgEntry; use rustc_ast::attr::version::RustcVersion; use rustc_ast::token::{CommentKind, DocFragmentKind}; @@ -106,6 +107,16 @@ impl PrintAttribute for PathBuf { p.word(self.display().to_string()); } } +impl PrintAttribute for Path { + fn should_render(&self) -> bool { + true + } + + fn print_attribute(&self, p: &mut Printer) { + p.word(join_path_idents(self.segments.iter().map(|seg| seg.ident))); + } +} + macro_rules! print_skip { ($($t: ty),* $(,)?) => {$( impl PrintAttribute for $t { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index b117fa832729..68fc7653b636 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -521,7 +521,10 @@ pub enum ConstArgKind<'hir, Unambig = ()> { /// This variant is not always used to represent inference consts, sometimes /// [`GenericArg::Infer`] is used instead. Infer(Unambig), - Literal(LitKind), + Literal { + lit: LitKind, + negated: bool, + }, } #[derive(Clone, Copy, Debug, HashStable_Generic)] @@ -1958,8 +1961,6 @@ pub struct PatExpr<'hir> { pub enum PatExprKind<'hir> { Lit { lit: Lit, - // FIXME: move this into `Lit` and handle negated literal expressions - // once instead of matching on unop neg expressions everywhere. negated: bool, }, /// A path pattern for a unit struct/variant or a (maybe-associated) constant. @@ -3198,7 +3199,7 @@ impl<'hir> TraitItem<'hir> { expect_methods_self_kind! { expect_const, (&'hir Ty<'hir>, Option>), - TraitItemKind::Const(ty, rhs), (ty, *rhs); + TraitItemKind::Const(ty, rhs, _), (ty, *rhs); expect_fn, (&FnSig<'hir>, &TraitFn<'hir>), TraitItemKind::Fn(ty, trfn), (ty, trfn); @@ -3218,11 +3219,32 @@ pub enum TraitFn<'hir> { Provided(BodyId), } +#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable_Generic)] +pub enum IsTypeConst { + No, + Yes, +} + +impl From for IsTypeConst { + fn from(value: bool) -> Self { + if value { Self::Yes } else { Self::No } + } +} + +impl From for bool { + fn from(value: IsTypeConst) -> Self { + matches!(value, IsTypeConst::Yes) + } +} + /// Represents a trait method or associated constant or type #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum TraitItemKind<'hir> { + // FIXME(mgca) eventually want to move the option that is around `ConstItemRhs<'hir>` + // into `ConstItemRhs`, much like `ast::ConstItemRhsKind`, but for now mark whether + // this node is a TypeConst with a flag. /// An associated constant with an optional value (otherwise `impl`s must contain a value). - Const(&'hir Ty<'hir>, Option>), + Const(&'hir Ty<'hir>, Option>, IsTypeConst), /// An associated function with an optional body. Fn(FnSig<'hir>, TraitFn<'hir>), /// An associated type with (possibly empty) bounds and optional concrete @@ -4685,7 +4707,7 @@ impl<'hir> OwnerNode<'hir> { | OwnerNode::TraitItem(TraitItem { kind: TraitItemKind::Fn(_, TraitFn::Provided(body)) - | TraitItemKind::Const(_, Some(ConstItemRhs::Body(body))), + | TraitItemKind::Const(_, Some(ConstItemRhs::Body(body)), _), .. }) | OwnerNode::ImplItem(ImplItem { @@ -4912,7 +4934,7 @@ impl<'hir> Node<'hir> { _ => None, }, Node::TraitItem(it) => match it.kind { - TraitItemKind::Const(ty, _) => Some(ty), + TraitItemKind::Const(ty, _, _) => Some(ty), TraitItemKind::Type(_, ty) => ty, _ => None, }, @@ -4955,7 +4977,7 @@ impl<'hir> Node<'hir> { | Node::TraitItem(TraitItem { owner_id, kind: - TraitItemKind::Const(.., Some(ConstItemRhs::Body(body))) + TraitItemKind::Const(_, Some(ConstItemRhs::Body(body)), _) | TraitItemKind::Fn(_, TraitFn::Provided(body)), .. }) diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index d1b75325af26..21bcf53b5619 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1110,7 +1110,7 @@ pub fn walk_const_arg<'v, V: Visitor<'v>>( ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, *hir_id, qpath.span()), ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon), ConstArgKind::Error(_) => V::Result::output(), // errors and spans are not important - ConstArgKind::Literal(..) => V::Result::output(), // FIXME(mcga) + ConstArgKind::Literal { .. } => V::Result::output(), // FIXME(mcga) } } @@ -1271,7 +1271,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_defaultness(&defaultness)); try_visit!(visitor.visit_id(hir_id)); match *kind { - TraitItemKind::Const(ref ty, default) => { + TraitItemKind::Const(ref ty, default, _) => { try_visit!(visitor.visit_ty_unambig(ty)); visit_opt!(visitor, visit_const_item_rhs, default); } diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 16e8bac3d8a4..a3f4415ec343 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -11,9 +11,7 @@ use crate::lints::DelayedLints; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro /// instead of implementing everything in `rustc_middle`. -pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext { - fn hash_attr_id(&mut self, id: &HashIgnoredAttrId, hasher: &mut StableHasher); -} +pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {} impl ToStableHashKey for BodyId { type KeyType = (DefPathHash, ItemLocalId); @@ -104,7 +102,7 @@ impl HashStable for Crate<'_> { } impl HashStable for HashIgnoredAttrId { - fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - hcx.hash_attr_id(self, hasher) + fn hash_stable(&self, _hcx: &mut HirCtx, _hasher: &mut StableHasher) { + /* we don't hash HashIgnoredAttrId, we ignore them */ } } diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 8d114862b3fe..0ce93561ddd4 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -16,7 +16,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl deleted file mode 100644 index 4dd9f50d4a5d..000000000000 --- a/compiler/rustc_hir_analysis/messages.ftl +++ /dev/null @@ -1,606 +0,0 @@ -hir_analysis_abi_custom_clothed_function = - items with the "custom" ABI can only be declared externally or defined via naked functions - .suggestion = convert this to an `#[unsafe(naked)]` function - -hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}` - .label = ambiguous associated {$assoc_kind} `{$assoc_ident}` - -hir_analysis_ambiguous_lifetime_bound = - ambiguous lifetime bound, explicit lifetime bound required - -hir_analysis_assoc_item_constraints_not_allowed_here = - associated item constraints are not allowed here - .label = associated item constraint not allowed here - -hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private - .label = private {$kind} - .defined_here_label = the {$kind} is defined here - -hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_ident}` not found for `{$qself}` - -hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named -> - [true] an - *[false] a similarly named - } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}` -hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_ident}` not found -hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind} -hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg = - consider fully qualifying{$identically_named -> - [true] {""} - *[false] {" "}and renaming - } the associated {$assoc_kind} -hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}` -hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = ...and changing the associated {$assoc_kind} name -hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name - -hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got} - .label = unexpected {$got} - .expected_because_label = expected a {$expected} because of this associated {$expected} - .note = the associated {$assoc_kind} is defined here - .bound_on_assoc_const_label = bounds are not allowed on associated constants - -hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg = consider adding braces here - -hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the {$what} of a trait with uninferred generic parameters - .suggestion = use a fully qualified path with inferred lifetimes - -hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion = use a fully qualified path with explicit lifetimes - -hir_analysis_async_drop_without_sync_drop = `AsyncDrop` impl without `Drop` impl - .help = type implementing `AsyncDrop` trait must also implement `Drop` trait to be used in sync context and unwinds - -hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit while auto-dereferencing `{$ty}` - .label = deref recursion limit reached - .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`) - -hir_analysis_bad_precise_capture = expected {$kind} parameter in `use<...>` precise captures list, found {$found} - -hir_analysis_bad_return_type_notation_position = return type notation not allowed in this position yet - -hir_analysis_cannot_capture_late_bound_const = - cannot capture late-bound const parameter in {$what} - .label = parameter defined here - -hir_analysis_cannot_capture_late_bound_lifetime = - cannot capture late-bound lifetime in {$what} - .label = lifetime defined here - -hir_analysis_cannot_capture_late_bound_ty = - cannot capture late-bound type parameter in {$what} - .label = parameter defined here - -hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present - .label = `for<...>` is here - -hir_analysis_cmse_generic = - generics are not allowed in `extern {$abi}` signatures - -hir_analysis_cmse_impl_trait = - `impl Trait` is not allowed in `extern {$abi}` signatures - -hir_analysis_cmse_inputs_stack_spill = - arguments for `{$abi}` function too large to pass via registers - .label = does not fit in the available registers - .note = functions with the `{$abi}` ABI must pass all their arguments via the 4 32-bit argument registers - -hir_analysis_cmse_output_stack_spill = - return value of `{$abi}` function too large to pass via registers - .label = this type doesn't fit in the available registers - .note1 = functions with the `{$abi}` ABI must pass their result via the available return registers - .note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size - -hir_analysis_coerce_multi = implementing `{$trait_name}` does not allow multiple fields to be coerced - .note = the trait `{$trait_name}` may only be implemented when a single field is being coerced - .label = these fields must be coerced for `{$trait_name}` to be valid - -hir_analysis_coerce_pointee_no_field = `CoercePointee` can only be derived on `struct`s with at least one field - -hir_analysis_coerce_pointee_no_user_validity_assertion = asserting applicability of `derive(CoercePointee)` on a target data is forbidden - -hir_analysis_coerce_pointee_not_concrete_ty = `derive(CoercePointee)` is only applicable to `struct` - -hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applicable to `struct`, instead of `{$kind}` - -hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout - -hir_analysis_coerce_same_pat_kind = only pattern types with the same pattern can be coerced between each other - -hir_analysis_coerce_unsized_field_validity = for `{$ty}` to have a valid implementation of `{$trait_name}`, it must be possible to coerce the field of type `{$field_ty}` - .label = `{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized - -hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures - -hir_analysis_coerce_zero = implementing `{$trait_name}` requires a field to be coerced - -hir_analysis_coercion_between_struct_same_note = expected coercion between the same definition; expected `{$source_path}`, found `{$target_path}` - -hir_analysis_coercion_between_struct_single_note = expected a single field to be coerced, none found - -hir_analysis_const_bound_for_non_const_trait = `{$modifier}` can only be applied to `const` traits - .label = can't be applied to `{$trait_name}` - .note = `{$trait_name}` can't be used with `{$modifier}` because it isn't `const` - .suggestion = {$suggestion_pre}mark `{$trait_name}` as `const` to allow it to have `const` implementations - -hir_analysis_const_impl_for_non_const_trait = const `impl` for trait `{$trait_name}` which is not `const` - .label = this trait is not `const` - .suggestion = {$suggestion_pre}mark `{$trait_name}` as `const` to allow it to have `const` implementations - .note = marking a trait with `const` ensures all default method bodies are `const` - .adding = adding a non-const method body in the future would be a breaking change - -hir_analysis_const_param_ty_impl_on_non_adt = - the trait `ConstParamTy` may not be implemented for this type - .label = type is not a structure or enumeration - -hir_analysis_const_param_ty_impl_on_unsized = - the trait `ConstParamTy` may not be implemented for this type - .label = type is not `Sized` - -hir_analysis_copy_impl_on_non_adt = - the trait `Copy` cannot be implemented for this type - .label = type is not a structure or enumeration - -hir_analysis_copy_impl_on_type_with_dtor = - the trait `Copy` cannot be implemented for this type; the type has a destructor - .label = `Copy` not allowed on types with destructors - .note = destructor declared here - -hir_analysis_cross_crate_traits = cross-crate traits with a default impl, like `{$traits}`, can only be implemented for a struct/enum type, not `{$self_ty}` - .label = can't implement cross-crate trait with a default impl for non-struct/enum type - -hir_analysis_cross_crate_traits_defined = cross-crate traits with a default impl, like `{$traits}`, can only be implemented for a struct/enum type defined in the current crate - .label = can't implement cross-crate trait for type in another crate - -hir_analysis_dispatch_from_dyn_repr = structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]` - -hir_analysis_dispatch_from_dyn_zst = the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else - .note = extra field `{$name}` of type `{$ty}` is not allowed - -hir_analysis_drop_impl_negative = negative `Drop` impls are not supported - -hir_analysis_drop_impl_on_wrong_item = - the `{$trait_}` trait may only be implemented for local structs, enums, and unions - .label = must be a struct, enum, or union in the current crate - -hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported - -hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice - .label = parameter captured again here - -hir_analysis_dyn_trait_assoc_item_binding_mentions_self = - {$kind} binding in trait object type mentions `Self` - .label = contains a mention of `Self` - .binding_label = this binding mentions `Self` - -hir_analysis_eii_with_generics = - `{$impl_name}` cannot have generic parameters other than lifetimes - .label = required by this attribute - .help = `#[{$eii_name}]` marks the implementation of an "externally implementable item" - -hir_analysis_empty_specialization = specialization impl does not specialize any associated items - .note = impl is a specialization of this impl - -hir_analysis_enum_discriminant_overflowed = enum discriminant overflowed - .label = overflowed on value after {$discr} - .note = explicitly set `{$item_name} = {$wrapped_discr}` if that is desired outcome - -hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding = - the type of the associated constant `{$assoc_const}` cannot capture late-bound generic parameters - .label = its type cannot capture the late-bound {$var_def_kind} `{$var_name}` - .var_defined_here_label = the late-bound {$var_def_kind} `{$var_name}` is defined here - -hir_analysis_field_already_declared = - field `{$field_name}` is already declared - .label = field already declared - .previous_decl_label = `{$field_name}` first declared here - -hir_analysis_field_already_declared_both_nested = - field `{$field_name}` is already declared - .label = field `{$field_name}` declared in this unnamed field - .nested_field_decl_note = field `{$field_name}` declared here - .previous_decl_label = `{$field_name}` first declared here in this unnamed field - .previous_nested_field_decl_note = field `{$field_name}` first declared here - -hir_analysis_field_already_declared_current_nested = - field `{$field_name}` is already declared - .label = field `{$field_name}` declared in this unnamed field - .nested_field_decl_note = field `{$field_name}` declared here - .previous_decl_label = `{$field_name}` first declared here - -hir_analysis_field_already_declared_nested_help = - fields from the type of this unnamed field are considered fields of the outer type - -hir_analysis_field_already_declared_previous_nested = - field `{$field_name}` is already declared - .label = field already declared - .previous_decl_label = `{$field_name}` first declared here in this unnamed field - .previous_nested_field_decl_note = field `{$field_name}` first declared here - -hir_analysis_generic_args_on_overridden_impl = could not resolve generic parameters on overridden impl - -hir_analysis_impl_not_marked_default = `{$ident}` specializes an item from a parent `impl`, but that item is not marked `default` - .label = cannot specialize default item `{$ident}` - .ok_label = parent `impl` is here - .note = to specialize, `{$ident}` in the parent `impl` must be marked `default` - -hir_analysis_impl_not_marked_default_err = `{$ident}` specializes an item from a parent `impl`, but that item is not marked `default` - .note = parent implementation is in crate `{$cname}` - -hir_analysis_impl_unpin_for_pin_projected_type = explicit impls for the `Unpin` trait are not permitted for structurally pinned types - .label = impl of `Unpin` not allowed - .help = `{$adt_name}` is structurally pinned because it is marked as `#[pin_v2]` - -hir_analysis_inherent_dyn = cannot define inherent `impl` for a dyn auto trait - .label = impl requires at least one non-auto trait - .note = define and implement a new trait or type instead - -hir_analysis_inherent_nominal = no nominal type found for inherent implementation - .label = impl requires a nominal type - .note = either implement a trait on it or create a newtype to wrap it instead -hir_analysis_inherent_primitive_ty = cannot define inherent `impl` for primitive types - .help = consider using an extension trait instead - -hir_analysis_inherent_primitive_ty_note = you could also try moving the reference to uses of `{$subty}` (such as `self`) within the implementation - -hir_analysis_inherent_ty_outside = cannot define inherent `impl` for a type outside of the crate where the type is defined - .help = consider moving this inherent impl into the crate defining the type if possible - .span_help = alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items - -hir_analysis_inherent_ty_outside_new = cannot define inherent `impl` for a type outside of the crate where the type is defined - .label = impl for type defined outside of crate - .note = define and implement a trait or new type instead - -hir_analysis_inherent_ty_outside_primitive = cannot define inherent `impl` for primitive types outside of `core` - .help = consider moving this inherent impl into `core` if possible - .span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items - -hir_analysis_inherent_ty_outside_relevant = cannot define inherent `impl` for a type outside of the crate where the type is defined - .help = consider moving this inherent impl into the crate defining the type if possible - .span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items - -hir_analysis_invalid_generic_receiver_ty = invalid generic `self` parameter type: `{$receiver_ty}` - .note = type of `self` must not be a method generic parameter type - -hir_analysis_invalid_generic_receiver_ty_help = - use a concrete type such as `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) - -hir_analysis_invalid_receiver_ty = invalid `self` parameter type: `{$receiver_ty}` - .note = type of `self` must be `Self` or some type implementing `Receiver` - -hir_analysis_invalid_receiver_ty_help = - consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` - -hir_analysis_invalid_receiver_ty_help_no_arbitrary_self_types = - consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) - -hir_analysis_invalid_receiver_ty_help_nonnull_note = - `NonNull` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `NonNull` in a newtype wrapper for which you implement `Receiver` - -hir_analysis_invalid_receiver_ty_help_weak_note = - `Weak` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `Weak` in a newtype wrapper for which you implement `Receiver` - -hir_analysis_invalid_receiver_ty_no_arbitrary_self_types = invalid `self` parameter type: `{$receiver_ty}` - .note = type of `self` must be `Self` or a type that dereferences to it - -hir_analysis_invalid_union_field = - field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union - .note = union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` - -hir_analysis_invalid_union_field_sugg = - wrap the field type in `ManuallyDrop<...>` - -hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl - .label = const parameter declared here - -hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetimes from an fn or impl - .label = lifetime declared here - -hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl - .label = type parameter declared here - -hir_analysis_lifetime_implicitly_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list - .param_label = all lifetime parameters originating from a trait are captured implicitly - -hir_analysis_lifetime_must_be_first = lifetime parameter `{$name}` must be listed before non-lifetime parameters - .label = move the lifetime before this parameter - -hir_analysis_lifetime_not_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list - .label = lifetime captured due to being mentioned in the bounds of the `impl Trait` - .param_label = this lifetime parameter is captured - -hir_analysis_lifetimes_or_bounds_mismatch_on_eii = - lifetime parameters or bounds of `{$ident}` do not match the declaration - .label = lifetimes do not match - .generics_label = lifetimes in impl do not match this signature - .where_label = this `where` clause might not match the one in the declaration - .bounds_label = this bound might be missing in the implementation - -hir_analysis_lifetimes_or_bounds_mismatch_on_trait = - lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration - .label = lifetimes do not match {$item_kind} in trait - .generics_label = lifetimes in impl do not match this {$item_kind} in trait - .where_label = this `where` clause might not match the one in the trait - .bounds_label = this bound might be missing in the impl - -hir_analysis_linkage_type = - invalid type for variable with `#[linkage]` attribute - -hir_analysis_main_function_async = `main` function is not allowed to be `async` - .label = `main` function is not allowed to be `async` - -hir_analysis_main_function_generic_parameters = `main` function is not allowed to have generic parameters - .label = `main` cannot have generic parameters - -hir_analysis_main_function_return_type_generic = `main` function return type is not allowed to have generic parameters - -hir_analysis_manual_implementation = - manual implementations of `{$trait_name}` are experimental - .label = manual implementations of `{$trait_name}` are experimental - .help = add `#![feature(unboxed_closures)]` to the crate attributes to enable - -hir_analysis_method_should_return_future = method should be `async` or return a future, but it is synchronous - .note = this method is `async` so it expects a future to be returned - -hir_analysis_missing_generic_params = - the {$descr} {$parameterCount -> - [one] parameter - *[other] parameters - } {$parameters} must be explicitly specified - .label = {$descr} {$parameterCount -> - [one] parameter - *[other] parameters - } {$parameters} must be specified for this - .suggestion = explicitly specify the {$descr} {$parameterCount -> - [one] parameter - *[other] parameters - } - .no_suggestion_label = missing {$parameterCount -> - [one] reference - *[other] references - } to {$parameters} - .note = because the parameter {$parameterCount -> - [one] default references - *[other] defaults reference - } `Self`, the {$parameterCount -> - [one] parameter - *[other] parameters - } must be specified on the trait object type - -hir_analysis_missing_one_of_trait_item = not all trait items implemented, missing one of: `{$missing_items_msg}` - .label = missing one of `{$missing_items_msg}` in implementation - .note = required because of this annotation - -hir_analysis_missing_trait_item = not all trait items implemented, missing: `{$missing_items_msg}` - .label = missing `{$missing_items_msg}` in implementation - -hir_analysis_missing_trait_item_label = `{$item}` from trait - -hir_analysis_missing_trait_item_suggestion = implement the missing item: `{$snippet}` - -hir_analysis_missing_trait_item_unstable = not all trait items implemented, missing: `{$missing_item_name}` - .note = default implementation of `{$missing_item_name}` is unstable - .some_note = use of unstable library feature `{$feature}`: {$reason} - .none_note = use of unstable library feature `{$feature}` - -hir_analysis_no_variant_named = no variant named `{$ident}` found for enum `{$ty}` - -hir_analysis_not_supported_delegation = {$descr} - .label = callee defined here - -hir_analysis_only_current_traits_adt = `{$name}` is not defined in the current crate - -hir_analysis_only_current_traits_arbitrary = only traits defined in the current crate can be implemented for arbitrary types - -hir_analysis_only_current_traits_foreign = this is not defined in the current crate because this is a foreign trait - -hir_analysis_only_current_traits_name = this is not defined in the current crate because {$name} are always foreign - -hir_analysis_only_current_traits_note = define and implement a trait or new type instead - -hir_analysis_only_current_traits_note_more_info = for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules - -hir_analysis_only_current_traits_note_uncovered = impl doesn't have any local type before any uncovered type parameters - -hir_analysis_only_current_traits_opaque = type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate - -hir_analysis_only_current_traits_outside = only traits defined in the current crate can be implemented for types defined outside of the crate - -hir_analysis_only_current_traits_pointer = `{$pointer}` is not defined in the current crate because raw pointers are always foreign - -hir_analysis_only_current_traits_pointer_sugg = consider introducing a new wrapper type - -hir_analysis_only_current_traits_primitive = only traits defined in the current crate can be implemented for primitive types - -hir_analysis_only_current_traits_ty = `{$ty}` is not defined in the current crate - -hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot capture {$bad_place} - .label = `impl Trait` implicitly captures all lifetimes in scope - .note = lifetime declared here - -hir_analysis_param_in_ty_of_assoc_const_binding = - the type of the associated constant `{$assoc_const}` must not depend on {$param_category -> - [self] `Self` - [synthetic] `impl Trait` - *[normal] generic parameters - } - .label = its type must not depend on {$param_category -> - [self] `Self` - [synthetic] `impl Trait` - *[normal] the {$param_def_kind} `{$param_name}` - } - .param_defined_here_label = {$param_category -> - [synthetic] the `impl Trait` is specified here - *[normal] the {$param_def_kind} `{$param_name}` is defined here - } - -hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope in `use<...>` - .label = {$kind} parameter is implicitly captured by this `impl Trait` - .note = currently, all {$kind} parameters are required to be mentioned in the precise captures list - -hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation - .help = add `#![feature(unboxed_closures)]` to the crate attributes to use it - -hir_analysis_parenthesized_fn_trait_expansion = - parenthesized trait syntax expands to `{$expanded_type}` - -hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} - .label = not allowed in type signatures - -hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias - .label = `Self` is not a generic argument, but an alias to the type of the {$what} - -hir_analysis_recursive_generic_parameter = {$param_def_kind} `{$param_name}` is only used recursively - .label = {$param_def_kind} must be used non-recursively in the definition - .note = all type parameters must be used in a non-recursive way in order to constrain their variance - -hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` - .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` - -hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}` - -hir_analysis_return_type_notation_equality_bound = - return type notation is not allowed to use type equality - -hir_analysis_return_type_notation_illegal_param_const = - return type notation is not allowed for functions that have const parameters - .label = const parameter declared here -hir_analysis_return_type_notation_illegal_param_type = - return type notation is not allowed for functions that have type parameters - .label = type parameter declared here - -hir_analysis_return_type_notation_on_non_rpitit = - return type notation used on function that is not `async` and does not return `impl Trait` - .note = function returns `{$ty}`, which is not compatible with associated type return bounds - .label = this function must be `async` or return `impl Trait` - -hir_analysis_rpitit_refined = impl trait in impl method signature does not match trait method signature - .suggestion = replace the return type so that it matches the trait - .label = return type from trait method defined here - .unmatched_bound_label = this bound is stronger than that defined on the trait - .note = add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate - .feedback_note = we are soliciting feedback, see issue #121718 for more information - -hir_analysis_rpitit_refined_lifetimes = impl trait in impl method captures fewer lifetimes than in trait - .suggestion = modify the `use<..>` bound to capture the same lifetimes that the trait does - .note = add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate - .feedback_note = we are soliciting feedback, see issue #121718 for more information - -hir_analysis_self_in_impl_self = - `Self` is not valid in the self type of an impl block - .note = replace `Self` with a different type - -hir_analysis_self_ty_not_captured = `impl Trait` must mention the `Self` type of the trait in `use<...>` - .label = `Self` type parameter is implicitly captured by this `impl Trait` - .note = currently, all type parameters are required to be mentioned in the precise captures list - -hir_analysis_simd_ffi_highly_experimental = use of SIMD type{$snip} in FFI is highly experimental and may result in invalid code - .help = add `#![feature(simd_ffi)]` to the crate attributes to enable - -hir_analysis_specialization_trait = implementing `rustc_specialization_trait` traits is unstable - .help = add `#![feature(min_specialization)]` to the crate attributes to enable - -hir_analysis_static_specialize = cannot specialize on `'static` lifetime - -hir_analysis_supertrait_item_multiple_shadowee = items from several supertraits are shadowed: {$traits} - -hir_analysis_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by a subtrait item - -hir_analysis_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait - -hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[target_feature]` - -hir_analysis_too_large_static = extern static is too large for the target architecture - -hir_analysis_track_caller_on_main = `main` function is not allowed to be `#[track_caller]` - .suggestion = remove this annotation - -hir_analysis_trait_cannot_impl_for_ty = the trait `{$trait_name}` cannot be implemented for this type - .label = this field does not implement `{$trait_name}` - -hir_analysis_trait_object_declared_with_no_traits = - at least one trait is required for an object type - .alias_span = this alias does not contain a trait - -hir_analysis_traits_with_default_impl = traits with a default impl, like `{$traits}`, cannot be implemented for {$problematic_kind} `{$self_ty}` - .note = a trait object implements `{$traits}` if and only if `{$traits}` is one of the trait object's trait bounds - -hir_analysis_transparent_enum_variant = transparent enum needs exactly one variant, but has {$number} - .label = needs exactly one variant, but has {$number} - .many_label = too many variants in `{$path}` - .multi_label = variant here - -hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one field with non-trivial size or alignment, but has {$field_count} - .label = needs at most one field with non-trivial size or alignment, but has {$field_count} - .labels = this field has non-zero size or requires alignment - -hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$desc} needs at most one field with non-trivial size or alignment, but has {$field_count} - .label = needs at most one field with non-trivial size or alignment, but has {$field_count} - .labels = this field has non-zero size or requires alignment - -hir_analysis_ty_of_assoc_const_binding_note = `{$assoc_const}` has type `{$ty}` - -hir_analysis_ty_param_first_local = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`) - .label = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`) - .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type - .case_note = in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last - -hir_analysis_ty_param_some = type parameter `{$param}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param}>`) - .label = type parameter `{$param}` must be used as the type parameter for some local type - .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local - .only_note = only traits defined in the current crate can be implemented for a type parameter - -hir_analysis_type_of = {$ty} - -hir_analysis_unconstrained_generic_parameter = the {$param_def_kind} `{$param_name}` is not constrained by the impl trait, self type, or predicates - .label = unconstrained {$param_def_kind} - .const_param_note = expressions using a const parameter must map each value to a distinct output value - .const_param_note2 = proving the result of expressions other than the parameter are unique is not supported - -hir_analysis_unconstrained_opaque_type = unconstrained opaque type - .note = `{$name}` must be used in combination with a concrete type within the same {$what} - -hir_analysis_unrecognized_intrinsic_function = - unrecognized intrinsic function: `{$name}` - .label = unrecognized intrinsic - .help = if you're adding an intrinsic, be sure to update `check_intrinsic_type` - -hir_analysis_unused_associated_type_bounds = - unnecessary associated type bound for dyn-incompatible associated type - .note = this associated type has a `where Self: Sized` bound, and while the associated type can be specified, it cannot be used because trait objects are never `Sized` - .suggestion = remove this bound - -hir_analysis_unused_generic_parameter = - {$param_def_kind} `{$param_name}` is never used - .label = unused {$param_def_kind} - .const_param_help = if you intended `{$param_name}` to be a const parameter, use `const {$param_name}: /* Type */` instead - .usage_spans = `{$param_name}` is named here, but is likely unused in the containing type - -hir_analysis_unused_generic_parameter_adt_help = - consider removing `{$param_name}`, referring to it in a field, or using a marker such as `{$phantom_data}` -hir_analysis_unused_generic_parameter_adt_no_phantom_data_help = - consider removing `{$param_name}` or referring to it in a field -hir_analysis_unused_generic_parameter_ty_alias_help = - consider removing `{$param_name}` or referring to it in the body of the type alias - -hir_analysis_useless_impl_item = this item cannot be used as its where bounds are not satisfied for the `Self` type - -hir_analysis_value_of_associated_struct_already_specified = - the value of the associated type `{$item_name}` in trait `{$def_path}` is already specified - .label = re-bound here - .previous_bound_label = `{$item_name}` bound here first - -hir_analysis_variadic_function_compatible_convention = C-variadic functions with the {$convention} calling convention are not supported - .label = C-variadic function must have a compatible calling convention - -hir_analysis_variances_of = {$variances} - -hir_analysis_where_clause_on_main = `main` function is not allowed to have a `where` clause - .label = `main` cannot have a `where` clause - -hir_analysis_within_macro = due to this macro variable - -hir_analysis_wrong_number_of_generic_arguments_to_intrinsic = - intrinsic has wrong number of {$descr} parameters: found {$found}, expected {$expected} - .label = expected {$expected} {$descr} {$expected -> - [one] parameter - *[other] parameters - } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index c00122bce559..1bee7a72229a 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -923,7 +923,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ); check_where_clauses(wfcx, def_id); - if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TypeConst(_)) { + if tcx.is_type_const(def_id) { wfcheck::check_type_const(wfcx, def_id, ty, true)?; } Ok(()) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 9e07d5260d20..c29ebe67a158 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -6,10 +6,9 @@ use hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::VisitorExt; -use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, find_attr, intravisit}; +use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisit}; use rustc_infer::infer::{self, BoundRegionConversionTime, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -1845,7 +1844,7 @@ fn compare_synthetic_generics<'tcx>( // The case where the impl method uses `impl Trait` but the trait method uses // explicit generics err.span_label(impl_span, "expected generic parameter, found `impl Trait`"); - let _: Option<_> = try { + try { // try taking the name from the trait impl // FIXME: this is obviously suboptimal since the name can already be used // as another generic argument @@ -1882,7 +1881,7 @@ fn compare_synthetic_generics<'tcx>( // The case where the trait method uses `impl Trait`, but the impl method uses // explicit generics. err.span_label(impl_span, "expected `impl Trait`, found generic parameter"); - let _: Option<_> = try { + try { let impl_m = impl_m.def_id.as_local()?; let impl_m = tcx.hir_expect_impl_item(impl_m); let (sig, _) = impl_m.expect_fn(); @@ -2051,12 +2050,8 @@ fn compare_type_const<'tcx>( impl_const_item: ty::AssocItem, trait_const_item: ty::AssocItem, ) -> Result<(), ErrorGuaranteed> { - let impl_is_type_const = - find_attr!(tcx.get_all_attrs(impl_const_item.def_id), AttributeKind::TypeConst(_)); - let trait_type_const_span = find_attr!( - tcx.get_all_attrs(trait_const_item.def_id), - AttributeKind::TypeConst(sp) => *sp - ); + let impl_is_type_const = tcx.is_type_const(impl_const_item.def_id); + let trait_type_const_span = tcx.type_const_span(trait_const_item.def_id); if let Some(trait_type_const_span) = trait_type_const_span && !impl_is_type_const @@ -2065,14 +2060,14 @@ fn compare_type_const<'tcx>( .dcx() .struct_span_err( tcx.def_span(impl_const_item.def_id), - "implementation of `#[type_const]` const must be marked with `#[type_const]`", + "implementation of a `type const` must also be marked as `type const`", ) .with_span_note( MultiSpan::from_spans(vec![ tcx.def_span(trait_const_item.def_id), trait_type_const_span, ]), - "trait declaration of const is marked with `#[type_const]`", + "trait declaration of const is marked as `type const`", ) .emit()); } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 8b50eceb26e4..32af13b88a1a 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -5,7 +5,9 @@ use hir::intravisit::{self, Visitor}; use rustc_abi::{ExternAbi, ScalableElt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; -use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; +use rustc_errors::{ + Applicability, ErrorGuaranteed, inline_fluent, pluralize, struct_span_code_err, +}; use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl, EiiImplResolution}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -42,8 +44,8 @@ use {rustc_ast as ast, rustc_hir as hir}; use super::compare_eii::compare_eii_function_types; use crate::autoderef::Autoderef; use crate::constrained_generic_params::{Parameter, identify_constrained_generic_params}; +use crate::errors; use crate::errors::InvalidReceiverTyHint; -use crate::{errors, fluent_generated as fluent}; pub(super) struct WfCheckingCtxt<'a, 'tcx> { pub(super) ocx: ObligationCtxt<'a, 'tcx, FulfillmentError<'tcx>>, @@ -953,7 +955,7 @@ pub(crate) fn check_associated_item( wfcx.register_wf_obligation(span, loc, ty.into()); let has_value = item.defaultness(tcx).has_value(); - if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TypeConst(_)) { + if tcx.is_type_const(def_id) { check_type_const(wfcx, def_id, ty, has_value)?; } @@ -1567,11 +1569,40 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id: let predicates = predicates.instantiate_identity(tcx); + let assoc_const_obligations: Vec<_> = predicates + .predicates + .iter() + .copied() + .zip(predicates.spans.iter().copied()) + .filter_map(|(clause, sp)| { + let proj = clause.as_projection_clause()?; + let pred_binder = proj + .map_bound(|pred| { + pred.term.as_const().map(|ct| { + let assoc_const_ty = tcx + .type_of(pred.projection_term.def_id) + .instantiate(tcx, pred.projection_term.args); + ty::ClauseKind::ConstArgHasType(ct, assoc_const_ty) + }) + }) + .transpose(); + pred_binder.map(|pred_binder| { + let cause = traits::ObligationCause::new( + sp, + wfcx.body_def_id, + ObligationCauseCode::WhereClause(def_id.to_def_id(), sp), + ); + Obligation::new(tcx, cause, wfcx.param_env, pred_binder) + }) + }) + .collect(); + assert_eq!(predicates.predicates.len(), predicates.spans.len()); let wf_obligations = predicates.into_iter().flat_map(|(p, sp)| { traits::wf::clause_obligations(infcx, wfcx.param_env, wfcx.body_def_id, p, sp) }); - let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect(); + let obligations: Vec<_> = + wf_obligations.chain(default_obligations).chain(assoc_const_obligations).collect(); wfcx.register_obligations(obligations); } @@ -1740,7 +1771,7 @@ fn check_method_receiver<'tcx>( the `arbitrary_self_types` feature", ), ) - .with_help(fluent::hir_analysis_invalid_receiver_ty_help) + .with_help(inline_fluent!("consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc`")) .emit() } None | Some(ArbitrarySelfTypesLevel::Basic) @@ -1764,7 +1795,7 @@ fn check_method_receiver<'tcx>( the `arbitrary_self_types_pointers` feature", ), ) - .with_help(fluent::hir_analysis_invalid_receiver_ty_help) + .with_help(inline_fluent!("consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc`")) .emit() } _ => @@ -2442,8 +2473,8 @@ fn lint_redundant_lifetimes<'tcx>( } #[derive(LintDiagnostic)] -#[diag(hir_analysis_redundant_lifetime_args)] -#[note] +#[diag("unnecessary lifetime parameter `{$victim}`")] +#[note("you can use the `{$candidate}` lifetime directly, in place of `{$victim}`")] struct RedundantLifetimeArgsLint<'tcx> { /// The lifetime we have found to be redundant. victim: ty::Region<'tcx>, diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index edaf33e493c0..588747f46d17 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -110,7 +110,22 @@ impl<'tcx> InherentCollect<'tcx> { Ok(()) } else { let impl_span = self.tcx.def_span(impl_def_id); - Err(self.tcx.dcx().emit_err(errors::InherentTyOutsideNew { span: impl_span })) + let mut err = errors::InherentTyOutsideNew { span: impl_span, note: None }; + + if let hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)) = + self.tcx.hir_node_by_def_id(impl_def_id).expect_item().expect_impl().self_ty.kind + && let rustc_hir::def::Res::Def(DefKind::TyAlias, def_id) = path.res + { + let ty_name = self.tcx.def_path_str(def_id); + let alias_ty_name = self.tcx.type_of(def_id).skip_binder().to_string(); + err.note = Some(errors::InherentTyOutsideNewAliasNote { + span: self.tcx.def_span(def_id), + ty_name, + alias_ty_name, + }); + } + + Err(self.tcx.dcx().emit_err(err)) } } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index f50aff187f25..03bbc9c0a7c9 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -16,22 +16,21 @@ use std::cell::Cell; use std::iter; -use std::ops::Bound; +use std::ops::{Bound, ControlFlow}; use rustc_abi::{ExternAbi, Size}; use rustc_ast::Recovered; use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, E0228, ErrorGuaranteed, StashKey, struct_span_code_err, -}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, E0228, ErrorGuaranteed, StashKey}; use rustc_hir::attrs::AttributeKind; -use rustc_hir::def::DefKind; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt}; +use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt}; use rustc_hir::{self as hir, GenericParamKind, HirId, Node, PreciseCapturingArgKind, find_attr}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause}; +use rustc_middle::hir::nested_filter; use rustc_middle::query::Providers; use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{ @@ -94,6 +93,7 @@ pub(crate) fn provide(providers: &mut Providers) { const_param_default, anon_const_kind, const_of_item, + is_rhs_type_const, ..*providers }; } @@ -316,16 +316,24 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { } fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx> { - if let RegionInferReason::ObjectLifetimeDefault = reason { - let e = struct_span_code_err!( - self.dcx(), - span, - E0228, - "the lifetime bound for this object type cannot be deduced \ - from context; please supply an explicit bound" - ) - .emit(); - ty::Region::new_error(self.tcx(), e) + if let RegionInferReason::ObjectLifetimeDefault(sugg_sp) = reason { + // FIXME: Account for trailing plus `dyn Trait+`, the need of parens in + // `*const dyn Trait` and `Fn() -> *const dyn Trait`. + let guar = self + .dcx() + .struct_span_err( + span, + "cannot deduce the lifetime bound for this trait object type from context", + ) + .with_code(E0228) + .with_span_suggestion_verbose( + sugg_sp, + "please supply an explicit bound", + " + /* 'a */", + Applicability::HasPlaceholders, + ) + .emit(); + ty::Region::new_error(self.tcx(), guar) } else { // This indicates an illegal lifetime in a non-assoc-trait position ty::Region::new_error_with_message(self.tcx(), span, "unelided lifetime in signature") @@ -1274,7 +1282,8 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplTraitHeader .of_trait .unwrap_or_else(|| panic!("expected impl trait, found inherent impl on {def_id:?}")); let selfty = tcx.type_of(def_id).instantiate_identity(); - let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl); + let is_rustc_reservation = + find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcReservationImpl(..)); check_impl_constness(tcx, impl_.constness, &of_trait.trait_ref); @@ -1511,6 +1520,20 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin let parent_hir_node = tcx.hir_node(tcx.parent_hir_id(const_arg_id)); if tcx.features().generic_const_exprs() { ty::AnonConstKind::GCE + } else if tcx.features().opaque_generic_const_args() { + // Only anon consts that are the RHS of a const item can be OGCA. + // Note: We can't just check tcx.parent because it needs to be EXACTLY + // the RHS, not just part of the RHS. + if !is_anon_const_rhs_of_const_item(tcx, def) { + return ty::AnonConstKind::MCG; + } + + let body = tcx.hir_body_owned_by(def); + let mut visitor = OGCAParamVisitor(tcx); + match visitor.visit_body(body) { + ControlFlow::Break(UsesParam) => ty::AnonConstKind::OGCA, + ControlFlow::Continue(()) => ty::AnonConstKind::MCG, + } } else if tcx.features().min_generic_const_args() { ty::AnonConstKind::MCG } else if let hir::Node::Expr(hir::Expr { @@ -1528,6 +1551,50 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin } } +fn is_anon_const_rhs_of_const_item<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool { + let hir_id = tcx.local_def_id_to_hir_id(def_id); + let Some((_, grandparent_node)) = tcx.hir_parent_iter(hir_id).nth(1) else { return false }; + let (Node::Item(hir::Item { kind: hir::ItemKind::Const(_, _, _, ct_rhs), .. }) + | Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(_, ct_rhs), .. }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Const(_, Some(ct_rhs), _), + .. + })) = grandparent_node + else { + return false; + }; + let hir::ConstItemRhs::TypeConst(hir::ConstArg { + kind: hir::ConstArgKind::Anon(rhs_anon), .. + }) = ct_rhs + else { + return false; + }; + def_id == rhs_anon.def_id +} + +struct OGCAParamVisitor<'tcx>(TyCtxt<'tcx>); + +struct UsesParam; + +impl<'tcx> Visitor<'tcx> for OGCAParamVisitor<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + type Result = ControlFlow; + + fn maybe_tcx(&mut self) -> TyCtxt<'tcx> { + self.0 + } + + fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: HirId) -> ControlFlow { + if let Res::Def(DefKind::TyParam | DefKind::ConstParam | DefKind::LifetimeParam, _) = + path.res + { + return ControlFlow::Break(UsesParam); + } + + intravisit::walk_path(self, path) + } +} + #[instrument(level = "debug", skip(tcx), ret)] fn const_of_item<'tcx>( tcx: TyCtxt<'tcx>, @@ -1536,7 +1603,7 @@ fn const_of_item<'tcx>( let ct_rhs = match tcx.hir_node_by_def_id(def_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(.., ct), .. }) => *ct, hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Const(.., ct), .. + kind: hir::TraitItemKind::Const(_, ct, _), .. }) => ct.expect("no default value for trait assoc const"), hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(.., ct), .. }) => *ct, _ => { @@ -1566,3 +1633,22 @@ fn const_of_item<'tcx>( ty::EarlyBinder::bind(ct) } } + +/// Check if a Const or AssocConst is a type const (mgca) +fn is_rhs_type_const<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> bool { + match tcx.hir_node_by_def_id(def) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Const(_, _, _, hir::ConstItemRhs::TypeConst(_)), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Const(_, hir::ConstItemRhs::TypeConst(_)), + .. + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Const(_, _, hir::IsTypeConst::Yes), + .. + }) => return true, + _ => return false, + } +} diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 511d513216eb..0723418d8ddf 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -92,6 +92,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { match tcx.anon_const_kind(def_id) { // Stable: anon consts are not able to use any generic parameters... ty::AnonConstKind::MCG => None, + // OGCA anon consts inherit their parent's generics. + ty::AnonConstKind::OGCA => Some(parent_did), // we provide generics to repeat expr counts as a backwards compatibility hack. #76200 ty::AnonConstKind::RepeatExprCount => Some(parent_did), diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 26f79d374075..3e687700f11c 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -859,7 +859,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } }) } - Const(_, _) => self.visit_early(trait_item.hir_id(), trait_item.generics, |this| { + Const(_, _, _) => self.visit_early(trait_item.hir_id(), trait_item.generics, |this| { intravisit::walk_trait_item(this, trait_item) }), } @@ -1695,7 +1695,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { | DefKind::Union | DefKind::Enum | DefKind::TyAlias - | DefKind::Trait, + | DefKind::Trait + | DefKind::TraitAlias, def_id, ) if depth == 0 => Some(def_id), _ => None, @@ -1865,7 +1866,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { if constraint.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation { let bound_vars = if let Some(type_def_id) = type_def_id - && self.tcx.def_kind(type_def_id) == DefKind::Trait + && let DefKind::Trait | DefKind::TraitAlias = self.tcx.def_kind(type_def_id) && let Some((mut bound_vars, assoc_fn)) = BoundVarContext::supertrait_hrtb_vars( self.tcx, type_def_id, diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 910176a0689c..23df419d06a7 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -63,7 +63,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ let args = ty::GenericArgs::identity_for_item(tcx, def_id); Ty::new_fn_def(tcx, def_id.to_def_id(), args) } - TraitItemKind::Const(ty, rhs) => rhs + TraitItemKind::Const(ty, rhs, _) => rhs .and_then(|rhs| { ty.is_suggestable_infer_ty().then(|| { infer_placeholder_type( @@ -420,9 +420,9 @@ fn infer_placeholder_type<'tcx>( kind: &'static str, ) -> Ty<'tcx> { let tcx = cx.tcx(); - // If the type is omitted on a #[type_const] we can't run + // If the type is omitted on a `type const` we can't run // type check on since that requires the const have a body - // which type_consts don't. + // which `type const`s don't. let ty = if tcx.is_type_const(def_id.to_def_id()) { if let Some(trait_item_def_id) = tcx.trait_item_of(def_id.to_def_id()) { tcx.type_of(trait_item_def_id).instantiate_identity() @@ -430,7 +430,7 @@ fn infer_placeholder_type<'tcx>( Ty::new_error_with_message( tcx, ty_span, - "constant with #[type_const] requires an explicit type", + "constant with `type const` requires an explicit type", ) } } else { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index c996fc97cd67..fb79789df76e 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -4,24 +4,22 @@ use rustc_abi::ExternAbi; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, - MultiSpan, listify, + MultiSpan, inline_fluent, listify, }; use rustc_hir::limit::Limit; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; use rustc_span::{Ident, Span, Symbol}; - -use crate::fluent_generated as fluent; pub(crate) mod wrong_number_of_generic_args; mod precise_captures; pub(crate) use precise_captures::*; #[derive(Diagnostic)] -#[diag(hir_analysis_ambiguous_assoc_item)] +#[diag("ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}`")] pub(crate) struct AmbiguousAssocItem<'a> { #[primary_span] - #[label] + #[label("ambiguous associated {$assoc_kind} `{$assoc_ident}`")] pub span: Span, pub assoc_kind: &'static str, pub assoc_ident: Ident, @@ -29,29 +27,26 @@ pub(crate) struct AmbiguousAssocItem<'a> { } #[derive(Diagnostic)] -#[diag(hir_analysis_assoc_kind_mismatch)] +#[diag("expected {$expected}, found {$got}")] pub(crate) struct AssocKindMismatch { #[primary_span] - #[label] + #[label("unexpected {$got}")] pub span: Span, pub expected: &'static str, pub got: &'static str, - #[label(hir_analysis_expected_because_label)] + #[label("expected a {$expected} because of this associated {$expected}")] pub expected_because_label: Option, pub assoc_kind: &'static str, - #[note] + #[note("the associated {$assoc_kind} is defined here")] pub def_span: Span, - #[label(hir_analysis_bound_on_assoc_const_label)] + #[label("bounds are not allowed on associated constants")] pub bound_on_assoc_const_label: Option, #[subdiagnostic] pub wrap_in_braces_sugg: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion( - hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg, - applicability = "maybe-incorrect" -)] +#[multipart_suggestion("consider adding braces here", applicability = "maybe-incorrect")] pub(crate) struct AssocKindMismatchWrapInBracesSugg { #[suggestion_part(code = "{{ ")] pub lo: Span, @@ -60,19 +55,19 @@ pub(crate) struct AssocKindMismatchWrapInBracesSugg { } #[derive(Diagnostic)] -#[diag(hir_analysis_assoc_item_is_private, code = E0624)] +#[diag("{$kind} `{$name}` is private", code = E0624)] pub(crate) struct AssocItemIsPrivate { #[primary_span] - #[label] + #[label("private {$kind}")] pub span: Span, pub kind: &'static str, pub name: Ident, - #[label(hir_analysis_defined_here_label)] + #[label("the {$kind} is defined here")] pub defined_here_label: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_assoc_item_not_found, code = E0220)] +#[diag("associated {$assoc_kind} `{$assoc_ident}` not found for `{$qself}`", code = E0220)] pub(crate) struct AssocItemNotFound<'a> { #[primary_span] pub span: Span, @@ -83,18 +78,23 @@ pub(crate) struct AssocItemNotFound<'a> { pub label: Option>, #[subdiagnostic] pub sugg: Option>, - #[label(hir_analysis_within_macro)] + #[label("due to this macro variable")] pub within_macro_span: Option, } #[derive(Subdiagnostic)] pub(crate) enum AssocItemNotFoundLabel<'a> { - #[label(hir_analysis_assoc_item_not_found_label)] + #[label("associated {$assoc_kind} `{$assoc_ident}` not found")] NotFound { #[primary_span] span: Span, }, - #[label(hir_analysis_assoc_item_not_found_found_in_other_trait_label)] + #[label( + "there is {$identically_named -> + [true] an + *[false] a similarly named + } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}`" + )] FoundInOtherTrait { #[primary_span] span: Span, @@ -109,7 +109,7 @@ pub(crate) enum AssocItemNotFoundLabel<'a> { pub(crate) enum AssocItemNotFoundSugg<'a> { #[suggestion( - hir_analysis_assoc_item_not_found_similar_sugg, + "there is an associated {$assoc_kind} with a similar name", code = "{suggested_name}", applicability = "maybe-incorrect" )] @@ -120,7 +120,7 @@ pub(crate) enum AssocItemNotFoundSugg<'a> { suggested_name: Symbol, }, #[suggestion( - hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg, + "change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}`", code = "{suggested_name}", style = "verbose", applicability = "maybe-incorrect" @@ -133,7 +133,10 @@ pub(crate) enum AssocItemNotFoundSugg<'a> { suggested_name: Symbol, }, #[multipart_suggestion( - hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg, + "consider fully qualifying{$identically_named -> + [true] {\"\"} + *[false] {\" \"}and renaming + } the associated {$assoc_kind}", style = "verbose" )] SimilarInOtherTraitQPath { @@ -150,7 +153,7 @@ pub(crate) enum AssocItemNotFoundSugg<'a> { applicability: Applicability, }, #[suggestion( - hir_analysis_assoc_item_not_found_other_sugg, + "`{$qself}` has the following associated {$assoc_kind}", code = "{suggested_name}", applicability = "maybe-incorrect" )] @@ -164,10 +167,15 @@ pub(crate) enum AssocItemNotFoundSugg<'a> { } #[derive(Diagnostic)] -#[diag(hir_analysis_wrong_number_of_generic_arguments_to_intrinsic, code = E0094)] +#[diag("intrinsic has wrong number of {$descr} parameters: found {$found}, expected {$expected}", code = E0094)] pub(crate) struct WrongNumberOfGenericArgumentsToIntrinsic<'a> { #[primary_span] - #[label] + #[label( + "expected {$expected} {$descr} {$expected -> + [one] parameter + *[other] parameters + }" + )] pub span: Span, pub found: usize, pub expected: usize, @@ -175,90 +183,90 @@ pub(crate) struct WrongNumberOfGenericArgumentsToIntrinsic<'a> { } #[derive(Diagnostic)] -#[diag(hir_analysis_unrecognized_intrinsic_function, code = E0093)] -#[help] +#[diag("unrecognized intrinsic function: `{$name}`", code = E0093)] +#[help("if you're adding an intrinsic, be sure to update `check_intrinsic_type`")] pub(crate) struct UnrecognizedIntrinsicFunction { #[primary_span] - #[label] + #[label("unrecognized intrinsic")] pub span: Span, pub name: Symbol, } #[derive(Diagnostic)] -#[diag(hir_analysis_lifetimes_or_bounds_mismatch_on_trait, code = E0195)] +#[diag("lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration", code = E0195)] pub(crate) struct LifetimesOrBoundsMismatchOnTrait { #[primary_span] - #[label] + #[label("lifetimes do not match {$item_kind} in trait")] pub span: Span, - #[label(hir_analysis_generics_label)] + #[label("lifetimes in impl do not match this {$item_kind} in trait")] pub generics_span: Span, - #[label(hir_analysis_where_label)] + #[label("this `where` clause might not match the one in the trait")] pub where_span: Option, - #[label(hir_analysis_bounds_label)] + #[label("this bound might be missing in the impl")] pub bounds_span: Vec, pub item_kind: &'static str, pub ident: Ident, } #[derive(Diagnostic)] -#[diag(hir_analysis_drop_impl_on_wrong_item, code = E0120)] +#[diag("the `{$trait_}` trait may only be implemented for local structs, enums, and unions", code = E0120)] pub(crate) struct DropImplOnWrongItem { #[primary_span] - #[label] + #[label("must be a struct, enum, or union in the current crate")] pub span: Span, pub trait_: Symbol, } #[derive(Diagnostic)] pub(crate) enum FieldAlreadyDeclared { - #[diag(hir_analysis_field_already_declared, code = E0124)] + #[diag("field `{$field_name}` is already declared", code = E0124)] NotNested { field_name: Ident, #[primary_span] - #[label] + #[label("field already declared")] span: Span, - #[label(hir_analysis_previous_decl_label)] + #[label("`{$field_name}` first declared here")] prev_span: Span, }, - #[diag(hir_analysis_field_already_declared_current_nested)] + #[diag("field `{$field_name}` is already declared")] CurrentNested { field_name: Ident, #[primary_span] - #[label] + #[label("field `{$field_name}` declared in this unnamed field")] span: Span, - #[note(hir_analysis_nested_field_decl_note)] + #[note("field `{$field_name}` declared here")] nested_field_span: Span, #[subdiagnostic] help: FieldAlreadyDeclaredNestedHelp, - #[label(hir_analysis_previous_decl_label)] + #[label("`{$field_name}` first declared here")] prev_span: Span, }, - #[diag(hir_analysis_field_already_declared_previous_nested)] + #[diag("field `{$field_name}` is already declared")] PreviousNested { field_name: Ident, #[primary_span] - #[label] + #[label("field already declared")] span: Span, - #[label(hir_analysis_previous_decl_label)] + #[label("`{$field_name}` first declared here in this unnamed field")] prev_span: Span, - #[note(hir_analysis_previous_nested_field_decl_note)] + #[note("field `{$field_name}` first declared here")] prev_nested_field_span: Span, #[subdiagnostic] prev_help: FieldAlreadyDeclaredNestedHelp, }, - #[diag(hir_analysis_field_already_declared_both_nested)] + #[diag("field `{$field_name}` is already declared")] BothNested { field_name: Ident, #[primary_span] - #[label] + #[label("field `{$field_name}` declared in this unnamed field")] span: Span, - #[note(hir_analysis_nested_field_decl_note)] + #[note("field `{$field_name}` declared here")] nested_field_span: Span, #[subdiagnostic] help: FieldAlreadyDeclaredNestedHelp, - #[label(hir_analysis_previous_decl_label)] + #[label("`{$field_name}` first declared here in this unnamed field")] prev_span: Span, - #[note(hir_analysis_previous_nested_field_decl_note)] + #[note("field `{$field_name}` first declared here")] prev_nested_field_span: Span, #[subdiagnostic] prev_help: FieldAlreadyDeclaredNestedHelp, @@ -266,67 +274,67 @@ pub(crate) enum FieldAlreadyDeclared { } #[derive(Subdiagnostic)] -#[help(hir_analysis_field_already_declared_nested_help)] +#[help("fields from the type of this unnamed field are considered fields of the outer type")] pub(crate) struct FieldAlreadyDeclaredNestedHelp { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_copy_impl_on_type_with_dtor, code = E0184)] +#[diag("the trait `Copy` cannot be implemented for this type; the type has a destructor", code = E0184)] pub(crate) struct CopyImplOnTypeWithDtor { #[primary_span] - #[label] + #[label("`Copy` not allowed on types with destructors")] pub span: Span, - #[note] + #[note("destructor declared here")] pub impl_: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_copy_impl_on_non_adt, code = E0206)] +#[diag("the trait `Copy` cannot be implemented for this type", code = E0206)] pub(crate) struct CopyImplOnNonAdt { #[primary_span] - #[label] + #[label("type is not a structure or enumeration")] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_const_param_ty_impl_on_unsized)] +#[diag("the trait `ConstParamTy` may not be implemented for this type")] pub(crate) struct ConstParamTyImplOnUnsized { #[primary_span] - #[label] + #[label("type is not `Sized`")] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_const_param_ty_impl_on_non_adt)] +#[diag("the trait `ConstParamTy` may not be implemented for this type")] pub(crate) struct ConstParamTyImplOnNonAdt { #[primary_span] - #[label] + #[label("type is not a structure or enumeration")] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_trait_object_declared_with_no_traits, code = E0224)] +#[diag("at least one trait is required for an object type", code = E0224)] pub(crate) struct TraitObjectDeclaredWithNoTraits { #[primary_span] pub span: Span, - #[label(hir_analysis_alias_span)] + #[label("this alias does not contain a trait")] pub trait_alias_span: Option, } #[derive(Diagnostic)] -#[diag(hir_analysis_ambiguous_lifetime_bound, code = E0227)] +#[diag("ambiguous lifetime bound, explicit lifetime bound required", code = E0227)] pub(crate) struct AmbiguousLifetimeBound { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_assoc_item_constraints_not_allowed_here, code = E0229)] +#[diag("associated item constraints are not allowed here", code = E0229)] pub(crate) struct AssocItemConstraintsNotAllowedHere { #[primary_span] - #[label] + #[label("associated item constraint not allowed here")] pub span: Span, #[subdiagnostic] @@ -334,45 +342,64 @@ pub(crate) struct AssocItemConstraintsNotAllowedHere { } #[derive(Diagnostic)] -#[diag(hir_analysis_param_in_ty_of_assoc_const_binding)] +#[diag( + "the type of the associated constant `{$assoc_const}` must not depend on {$param_category -> + [self] `Self` + [synthetic] `impl Trait` + *[normal] generic parameters + }" +)] pub(crate) struct ParamInTyOfAssocConstBinding<'tcx> { #[primary_span] - #[label] + #[label( + "its type must not depend on {$param_category -> + [self] `Self` + [synthetic] `impl Trait` + *[normal] the {$param_def_kind} `{$param_name}` + }" + )] pub span: Span, pub assoc_const: Ident, pub param_name: Symbol, pub param_def_kind: &'static str, pub param_category: &'static str, - #[label(hir_analysis_param_defined_here_label)] + #[label( + "{$param_category -> + [synthetic] the `impl Trait` is specified here + *[normal] the {$param_def_kind} `{$param_name}` is defined here + }" + )] pub param_defined_here_label: Option, #[subdiagnostic] pub ty_note: Option>, } #[derive(Subdiagnostic, Clone, Copy)] -#[note(hir_analysis_ty_of_assoc_const_binding_note)] +#[note("`{$assoc_const}` has type `{$ty}`")] pub(crate) struct TyOfAssocConstBindingNote<'tcx> { pub assoc_const: Ident, pub ty: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding)] +#[diag( + "the type of the associated constant `{$assoc_const}` cannot capture late-bound generic parameters" +)] pub(crate) struct EscapingBoundVarInTyOfAssocConstBinding<'tcx> { #[primary_span] - #[label] + #[label("its type cannot capture the late-bound {$var_def_kind} `{$var_name}`")] pub span: Span, pub assoc_const: Ident, pub var_name: Symbol, pub var_def_kind: &'static str, - #[label(hir_analysis_var_defined_here_label)] + #[label("the late-bound {$var_def_kind} `{$var_name}` is defined here")] pub var_defined_here_label: Span, #[subdiagnostic] pub ty_note: Option>, } #[derive(Subdiagnostic)] -#[help(hir_analysis_parenthesized_fn_trait_expansion)] +#[help("parenthesized trait syntax expands to `{$expanded_type}`")] pub(crate) struct ParenthesizedFnTraitExpansion { #[primary_span] pub span: Span, @@ -381,20 +408,20 @@ pub(crate) struct ParenthesizedFnTraitExpansion { } #[derive(Diagnostic)] -#[diag(hir_analysis_value_of_associated_struct_already_specified, code = E0719)] +#[diag("the value of the associated type `{$item_name}` in trait `{$def_path}` is already specified", code = E0719)] pub(crate) struct ValueOfAssociatedStructAlreadySpecified { #[primary_span] - #[label] + #[label("re-bound here")] pub span: Span, - #[label(hir_analysis_previous_bound_label)] + #[label("`{$item_name}` bound here first")] pub prev_span: Span, pub item_name: Ident, pub def_path: String, } #[derive(Diagnostic)] -#[diag(hir_analysis_unconstrained_opaque_type)] -#[note] +#[diag("unconstrained opaque type")] +#[note("`{$name}` must be used in combination with a concrete type within the same {$what}")] pub(crate) struct UnconstrainedOpaqueType { #[primary_span] pub span: Span, @@ -414,10 +441,27 @@ pub(crate) struct MissingGenericParams { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingGenericParams { #[track_caller] fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - let mut err = Diag::new(dcx, level, fluent::hir_analysis_missing_generic_params); + let mut err = Diag::new( + dcx, + level, + inline_fluent!( + "the {$descr} {$parameterCount -> + [one] parameter + *[other] parameters + } {$parameters} must be explicitly specified" + ), + ); err.span(self.span); err.code(E0393); - err.span_label(self.def_span, fluent::hir_analysis_label); + err.span_label( + self.def_span, + inline_fluent!( + "{$descr} {$parameterCount -> + [one] parameter + *[other] parameters + } {$parameters} must be specified for this" + ), + ); enum Descr { Generic, @@ -467,7 +511,12 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingGenericParams { // least we can clue them to the correct syntax `Trait`. err.span_suggestion_verbose( self.span.shrink_to_hi(), - fluent::hir_analysis_suggestion, + inline_fluent!( + "explicitly specify the {$descr} {$parameterCount -> + [one] parameter + *[other] parameters + }" + ), format!( "<{}>", self.missing_generic_params @@ -482,84 +531,112 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingGenericParams { } } if !suggested { - err.span_label(self.span, fluent::hir_analysis_no_suggestion_label); + err.span_label( + self.span, + inline_fluent!( + "missing {$parameterCount -> + [one] reference + *[other] references + } to {$parameters}" + ), + ); } - err.note(fluent::hir_analysis_note); + err.note(inline_fluent!( + "because the parameter {$parameterCount -> + [one] default references + *[other] defaults reference + } `Self`, the {$parameterCount -> + [one] parameter + *[other] parameters + } must be specified on the trait object type" + )); err } } #[derive(Diagnostic)] -#[diag(hir_analysis_manual_implementation, code = E0183)] -#[help] +#[diag("manual implementations of `{$trait_name}` are experimental", code = E0183)] +#[help("add `#![feature(unboxed_closures)]` to the crate attributes to enable")] pub(crate) struct ManualImplementation { #[primary_span] - #[label] + #[label("manual implementations of `{$trait_name}` are experimental")] pub span: Span, pub trait_name: String, } #[derive(Diagnostic)] -#[diag(hir_analysis_generic_args_on_overridden_impl)] +#[diag("could not resolve generic parameters on overridden impl")] pub(crate) struct GenericArgsOnOverriddenImpl { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_const_impl_for_non_const_trait)] +#[diag("const `impl` for trait `{$trait_name}` which is not `const`")] pub(crate) struct ConstImplForNonConstTrait { #[primary_span] - #[label] + #[label("this trait is not `const`")] pub trait_ref_span: Span, pub trait_name: String, - #[suggestion(applicability = "machine-applicable", code = "const ", style = "verbose")] + #[suggestion( + "{$suggestion_pre}mark `{$trait_name}` as `const` to allow it to have `const` implementations", + applicability = "machine-applicable", + code = "const ", + style = "verbose" + )] pub suggestion: Option, pub suggestion_pre: &'static str, - #[note] + #[note("marking a trait with `const` ensures all default method bodies are `const`")] pub marking: (), - #[note(hir_analysis_adding)] + #[note("adding a non-const method body in the future would be a breaking change")] pub adding: (), } #[derive(Diagnostic)] -#[diag(hir_analysis_const_bound_for_non_const_trait)] +#[diag("`{$modifier}` can only be applied to `const` traits")] pub(crate) struct ConstBoundForNonConstTrait { #[primary_span] - #[label] + #[label("can't be applied to `{$trait_name}`")] pub span: Span, pub modifier: &'static str, - #[note] + #[note("`{$trait_name}` can't be used with `{$modifier}` because it isn't `const`")] pub def_span: Option, - #[suggestion(applicability = "machine-applicable", code = "const ", style = "verbose")] + #[suggestion( + "{$suggestion_pre}mark `{$trait_name}` as `const` to allow it to have `const` implementations", + applicability = "machine-applicable", + code = "const ", + style = "verbose" + )] pub suggestion: Option, pub suggestion_pre: &'static str, pub trait_name: String, } #[derive(Diagnostic)] -#[diag(hir_analysis_self_in_impl_self)] +#[diag("`Self` is not valid in the self type of an impl block")] pub(crate) struct SelfInImplSelf { #[primary_span] pub span: MultiSpan, - #[note] + #[note("replace `Self` with a different type")] pub note: (), } #[derive(Diagnostic)] -#[diag(hir_analysis_linkage_type, code = E0791)] +#[diag("invalid type for variable with `#[linkage]` attribute", code = E0791)] pub(crate) struct LinkageType { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[help] -#[diag(hir_analysis_auto_deref_reached_recursion_limit, code = E0055)] +#[help( + "consider increasing the recursion limit by adding a `#![recursion_limit = \"{$suggested_limit}\"]` attribute to your crate (`{$crate_name}`)" +)] +#[diag("reached the recursion limit while auto-dereferencing `{$ty}`", code = E0055)] pub(crate) struct AutoDerefReachedRecursionLimit<'a> { #[primary_span] - #[label] + #[label("deref recursion limit reached")] pub span: Span, pub ty: Ty<'a>, pub suggested_limit: Limit, @@ -567,96 +644,96 @@ pub(crate) struct AutoDerefReachedRecursionLimit<'a> { } #[derive(Diagnostic)] -#[diag(hir_analysis_where_clause_on_main, code = E0646)] +#[diag("`main` function is not allowed to have a `where` clause", code = E0646)] pub(crate) struct WhereClauseOnMain { #[primary_span] pub span: Span, - #[label] + #[label("`main` cannot have a `where` clause")] pub generics_span: Option, } #[derive(Diagnostic)] -#[diag(hir_analysis_track_caller_on_main)] +#[diag("`main` function is not allowed to be `#[track_caller]`")] pub(crate) struct TrackCallerOnMain { #[primary_span] - #[suggestion(applicability = "maybe-incorrect", code = "")] + #[suggestion("remove this annotation", applicability = "maybe-incorrect", code = "")] pub span: Span, - #[label(hir_analysis_track_caller_on_main)] + #[label("`main` function is not allowed to be `#[track_caller]`")] pub annotated: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_target_feature_on_main)] +#[diag("`main` function is not allowed to have `#[target_feature]`")] pub(crate) struct TargetFeatureOnMain { #[primary_span] - #[label(hir_analysis_target_feature_on_main)] + #[label("`main` function is not allowed to have `#[target_feature]`")] pub main: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_main_function_return_type_generic, code = E0131)] +#[diag("`main` function return type is not allowed to have generic parameters", code = E0131)] pub(crate) struct MainFunctionReturnTypeGeneric { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_main_function_async, code = E0752)] +#[diag("`main` function is not allowed to be `async`", code = E0752)] pub(crate) struct MainFunctionAsync { #[primary_span] pub span: Span, - #[label] + #[label("`main` function is not allowed to be `async`")] pub asyncness: Option, } #[derive(Diagnostic)] -#[diag(hir_analysis_main_function_generic_parameters, code = E0131)] +#[diag("`main` function is not allowed to have generic parameters", code = E0131)] pub(crate) struct MainFunctionGenericParameters { #[primary_span] pub span: Span, - #[label] + #[label("`main` cannot have generic parameters")] pub label_span: Option, } #[derive(Diagnostic)] -#[diag(hir_analysis_variadic_function_compatible_convention, code = E0045)] +#[diag("C-variadic functions with the {$convention} calling convention are not supported", code = E0045)] pub(crate) struct VariadicFunctionCompatibleConvention<'a> { #[primary_span] - #[label] + #[label("C-variadic function must have a compatible calling convention")] pub span: Span, pub convention: &'a str, } #[derive(Diagnostic)] pub(crate) enum CannotCaptureLateBound { - #[diag(hir_analysis_cannot_capture_late_bound_ty)] + #[diag("cannot capture late-bound type parameter in {$what}")] Type { #[primary_span] use_span: Span, - #[label] + #[label("parameter defined here")] def_span: Span, what: &'static str, }, - #[diag(hir_analysis_cannot_capture_late_bound_const)] + #[diag("cannot capture late-bound const parameter in {$what}")] Const { #[primary_span] use_span: Span, - #[label] + #[label("parameter defined here")] def_span: Span, what: &'static str, }, - #[diag(hir_analysis_cannot_capture_late_bound_lifetime)] + #[diag("cannot capture late-bound lifetime in {$what}")] Lifetime { #[primary_span] use_span: Span, - #[label] + #[label("lifetime defined here")] def_span: Span, what: &'static str, }, } #[derive(Diagnostic)] -#[diag(hir_analysis_variances_of)] +#[diag("{$variances}")] pub(crate) struct VariancesOf { #[primary_span] pub span: Span, @@ -664,7 +741,7 @@ pub(crate) struct VariancesOf { } #[derive(Diagnostic)] -#[diag(hir_analysis_type_of)] +#[diag("{$ty}")] pub(crate) struct TypeOf<'tcx> { #[primary_span] pub span: Span, @@ -672,30 +749,37 @@ pub(crate) struct TypeOf<'tcx> { } #[derive(Diagnostic)] -#[diag(hir_analysis_invalid_union_field, code = E0740)] +#[diag("field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union", code = E0740)] pub(crate) struct InvalidUnionField { #[primary_span] pub field_span: Span, #[subdiagnostic] pub sugg: InvalidUnionFieldSuggestion, - #[note] + #[note( + "union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`" + )] pub note: (), } #[derive(Diagnostic)] -#[diag(hir_analysis_return_type_notation_on_non_rpitit)] +#[diag( + "return type notation used on function that is not `async` and does not return `impl Trait`" +)] pub(crate) struct ReturnTypeNotationOnNonRpitit<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, - #[label] + #[label("this function must be `async` or return `impl Trait`")] pub fn_span: Option, - #[note] + #[note("function returns `{$ty}`, which is not compatible with associated type return bounds")] pub note: (), } #[derive(Subdiagnostic)] -#[multipart_suggestion(hir_analysis_invalid_union_field_sugg, applicability = "machine-applicable")] +#[multipart_suggestion( + "wrap the field type in `ManuallyDrop<...>`", + applicability = "machine-applicable" +)] pub(crate) struct InvalidUnionFieldSuggestion { #[suggestion_part(code = "std::mem::ManuallyDrop<")] pub lo: Span, @@ -704,27 +788,32 @@ pub(crate) struct InvalidUnionFieldSuggestion { } #[derive(Diagnostic)] -#[diag(hir_analysis_return_type_notation_equality_bound)] +#[diag("return type notation is not allowed to use type equality")] pub(crate) struct ReturnTypeNotationEqualityBound { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_placeholder_not_allowed_item_signatures, code = E0121)] +#[diag("the placeholder `_` is not allowed within types on item signatures for {$kind}", code = E0121)] pub(crate) struct PlaceholderNotAllowedItemSignatures { #[primary_span] - #[label] + #[label("not allowed in type signatures")] pub spans: Vec, pub kind: String, } #[derive(Diagnostic)] -#[diag(hir_analysis_associated_type_trait_uninferred_generic_params, code = E0212)] +#[diag("cannot use the {$what} of a trait with uninferred generic parameters", code = E0212)] pub(crate) struct AssociatedItemTraitUninferredGenericParams { #[primary_span] pub span: Span, - #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "{bound}")] + #[suggestion( + "use a fully qualified path with inferred lifetimes", + style = "verbose", + applicability = "maybe-incorrect", + code = "{bound}" + )] pub inferred_sugg: Option, pub bound: String, #[subdiagnostic] @@ -734,7 +823,7 @@ pub(crate) struct AssociatedItemTraitUninferredGenericParams { #[derive(Subdiagnostic)] #[multipart_suggestion( - hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion, + "use a fully qualified path with explicit lifetimes", applicability = "maybe-incorrect" )] pub(crate) struct AssociatedItemTraitUninferredGenericParamsMultipartSuggestion { @@ -747,11 +836,11 @@ pub(crate) struct AssociatedItemTraitUninferredGenericParamsMultipartSuggestion } #[derive(Diagnostic)] -#[diag(hir_analysis_enum_discriminant_overflowed, code = E0370)] -#[note] +#[diag("enum discriminant overflowed", code = E0370)] +#[note("explicitly set `{$item_name} = {$wrapped_discr}` if that is desired outcome")] pub(crate) struct EnumDiscriminantOverflowed { #[primary_span] - #[label] + #[label("overflowed on value after {$discr}")] pub span: Span, pub discr: String, pub item_name: Ident, @@ -759,16 +848,18 @@ pub(crate) struct EnumDiscriminantOverflowed { } #[derive(Diagnostic)] -#[diag(hir_analysis_paren_sugar_attribute)] -#[help] +#[diag( + "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation" +)] +#[help("add `#![feature(unboxed_closures)]` to the crate attributes to use it")] pub(crate) struct ParenSugarAttribute { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_simd_ffi_highly_experimental)] -#[help] +#[diag("use of SIMD type{$snip} in FFI is highly experimental and may result in invalid code")] +#[help("add `#![feature(simd_ffi)]` to the crate attributes to enable")] pub(crate) struct SIMDFFIHighlyExperimental { #[primary_span] pub span: Span, @@ -777,18 +868,18 @@ pub(crate) struct SIMDFFIHighlyExperimental { #[derive(Diagnostic)] pub(crate) enum ImplNotMarkedDefault { - #[diag(hir_analysis_impl_not_marked_default, code = E0520)] - #[note] + #[diag("`{$ident}` specializes an item from a parent `impl`, but that item is not marked `default`", code = E0520)] + #[note("to specialize, `{$ident}` in the parent `impl` must be marked `default`")] Ok { #[primary_span] - #[label] + #[label("cannot specialize default item `{$ident}`")] span: Span, - #[label(hir_analysis_ok_label)] + #[label("parent `impl` is here")] ok_label: Span, ident: Ident, }, - #[diag(hir_analysis_impl_not_marked_default_err, code = E0520)] - #[note] + #[diag("`{$ident}` specializes an item from a parent `impl`, but that item is not marked `default`", code = E0520)] + #[note("parent implementation is in crate `{$cname}`")] Err { #[primary_span] span: Span, @@ -798,14 +889,14 @@ pub(crate) enum ImplNotMarkedDefault { } #[derive(LintDiagnostic)] -#[diag(hir_analysis_useless_impl_item)] +#[diag("this item cannot be used as its where bounds are not satisfied for the `Self` type")] pub(crate) struct UselessImplItem; #[derive(Diagnostic)] -#[diag(hir_analysis_missing_trait_item, code = E0046)] +#[diag("not all trait items implemented, missing: `{$missing_items_msg}`", code = E0046)] pub(crate) struct MissingTraitItem { #[primary_span] - #[label] + #[label("missing `{$missing_items_msg}` in implementation")] pub span: Span, #[subdiagnostic] pub missing_trait_item_label: Vec, @@ -817,7 +908,7 @@ pub(crate) struct MissingTraitItem { } #[derive(Subdiagnostic)] -#[label(hir_analysis_missing_trait_item_label)] +#[label("`{$item}` from trait")] pub(crate) struct MissingTraitItemLabel { #[primary_span] pub span: Span, @@ -826,7 +917,7 @@ pub(crate) struct MissingTraitItemLabel { #[derive(Subdiagnostic)] #[suggestion( - hir_analysis_missing_trait_item_suggestion, + "implement the missing item: `{$snippet}`", style = "tool-only", applicability = "has-placeholders", code = "{code}" @@ -840,7 +931,7 @@ pub(crate) struct MissingTraitItemSuggestion { #[derive(Subdiagnostic)] #[suggestion( - hir_analysis_missing_trait_item_suggestion, + "implement the missing item: `{$snippet}`", style = "hidden", applicability = "has-placeholders", code = "{code}" @@ -853,25 +944,25 @@ pub(crate) struct MissingTraitItemSuggestionNone { } #[derive(Diagnostic)] -#[diag(hir_analysis_missing_one_of_trait_item, code = E0046)] +#[diag("not all trait items implemented, missing one of: `{$missing_items_msg}`", code = E0046)] pub(crate) struct MissingOneOfTraitItem { #[primary_span] - #[label] + #[label("missing one of `{$missing_items_msg}` in implementation")] pub span: Span, - #[note] + #[note("required because of this annotation")] pub note: Option, pub missing_items_msg: String, } #[derive(Diagnostic)] -#[diag(hir_analysis_missing_trait_item_unstable, code = E0046)] -#[note] +#[diag("not all trait items implemented, missing: `{$missing_item_name}`", code = E0046)] +#[note("default implementation of `{$missing_item_name}` is unstable")] pub(crate) struct MissingTraitItemUnstable { #[primary_span] pub span: Span, - #[note(hir_analysis_some_note)] + #[note("use of unstable library feature `{$feature}`: {$reason}")] pub some_note: bool, - #[note(hir_analysis_none_note)] + #[note("use of unstable library feature `{$feature}`")] pub none_note: bool, pub missing_item_name: Ident, pub feature: Symbol, @@ -879,78 +970,78 @@ pub(crate) struct MissingTraitItemUnstable { } #[derive(Diagnostic)] -#[diag(hir_analysis_transparent_enum_variant, code = E0731)] +#[diag("transparent enum needs exactly one variant, but has {$number}", code = E0731)] pub(crate) struct TransparentEnumVariant { #[primary_span] - #[label] + #[label("needs exactly one variant, but has {$number}")] pub span: Span, - #[label(hir_analysis_multi_label)] + #[label("variant here")] pub spans: Vec, - #[label(hir_analysis_many_label)] + #[label("too many variants in `{$path}`")] pub many: Option, pub number: usize, pub path: String, } #[derive(Diagnostic)] -#[diag(hir_analysis_transparent_non_zero_sized_enum, code = E0690)] +#[diag("the variant of a transparent {$desc} needs at most one field with non-trivial size or alignment, but has {$field_count}", code = E0690)] pub(crate) struct TransparentNonZeroSizedEnum<'a> { #[primary_span] - #[label] + #[label("needs at most one field with non-trivial size or alignment, but has {$field_count}")] pub span: Span, - #[label(hir_analysis_labels)] + #[label("this field has non-zero size or requires alignment")] pub spans: Vec, pub field_count: usize, pub desc: &'a str, } #[derive(Diagnostic)] -#[diag(hir_analysis_transparent_non_zero_sized, code = E0690)] +#[diag("transparent {$desc} needs at most one field with non-trivial size or alignment, but has {$field_count}", code = E0690)] pub(crate) struct TransparentNonZeroSized<'a> { #[primary_span] - #[label] + #[label("needs at most one field with non-trivial size or alignment, but has {$field_count}")] pub span: Span, - #[label(hir_analysis_labels)] + #[label("this field has non-zero size or requires alignment")] pub spans: Vec, pub field_count: usize, pub desc: &'a str, } #[derive(Diagnostic)] -#[diag(hir_analysis_too_large_static)] +#[diag("extern static is too large for the target architecture")] pub(crate) struct TooLargeStatic { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_specialization_trait)] -#[help] +#[diag("implementing `rustc_specialization_trait` traits is unstable")] +#[help("add `#![feature(min_specialization)]` to the crate attributes to enable")] pub(crate) struct SpecializationTrait { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_closure_implicit_hrtb)] +#[diag("implicit types in closure signatures are forbidden when `for<...>` is present")] pub(crate) struct ClosureImplicitHrtb { #[primary_span] pub spans: Vec, - #[label] + #[label("`for<...>` is here")] pub for_sp: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_empty_specialization)] +#[diag("specialization impl does not specialize any associated items")] pub(crate) struct EmptySpecialization { #[primary_span] pub span: Span, - #[note] + #[note("impl is a specialization of this impl")] pub base_impl_span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_static_specialize)] +#[diag("cannot specialize on `'static` lifetime")] pub(crate) struct StaticSpecialize { #[primary_span] pub span: Span, @@ -958,12 +1049,12 @@ pub(crate) struct StaticSpecialize { #[derive(Diagnostic)] pub(crate) enum DropImplPolarity { - #[diag(hir_analysis_drop_impl_negative)] + #[diag("negative `Drop` impls are not supported")] Negative { #[primary_span] span: Span, }, - #[diag(hir_analysis_drop_impl_reservation)] + #[diag("reservation `Drop` impls are not supported")] Reservation { #[primary_span] span: Span, @@ -972,65 +1063,75 @@ pub(crate) enum DropImplPolarity { #[derive(Diagnostic)] pub(crate) enum ReturnTypeNotationIllegalParam { - #[diag(hir_analysis_return_type_notation_illegal_param_type)] + #[diag("return type notation is not allowed for functions that have type parameters")] Type { #[primary_span] span: Span, - #[label] + #[label("type parameter declared here")] param_span: Span, }, - #[diag(hir_analysis_return_type_notation_illegal_param_const)] + #[diag("return type notation is not allowed for functions that have const parameters")] Const { #[primary_span] span: Span, - #[label] + #[label("const parameter declared here")] param_span: Span, }, } #[derive(Diagnostic)] pub(crate) enum LateBoundInApit { - #[diag(hir_analysis_late_bound_type_in_apit)] + #[diag("`impl Trait` can only mention type parameters from an fn or impl")] Type { #[primary_span] span: Span, - #[label] + #[label("type parameter declared here")] param_span: Span, }, - #[diag(hir_analysis_late_bound_const_in_apit)] + #[diag("`impl Trait` can only mention const parameters from an fn or impl")] Const { #[primary_span] span: Span, - #[label] + #[label("const parameter declared here")] param_span: Span, }, - #[diag(hir_analysis_late_bound_lifetime_in_apit)] + #[diag("`impl Trait` can only mention lifetimes from an fn or impl")] Lifetime { #[primary_span] span: Span, - #[label] + #[label("lifetime declared here")] param_span: Span, }, } #[derive(LintDiagnostic)] -#[diag(hir_analysis_unused_associated_type_bounds)] -#[note] +#[diag("unnecessary associated type bound for dyn-incompatible associated type")] +#[note( + "this associated type has a `where Self: Sized` bound, and while the associated type can be specified, it cannot be used because trait objects are never `Sized`" +)] pub(crate) struct UnusedAssociatedTypeBounds { - #[suggestion(code = "")] + #[suggestion("remove this bound", code = "")] pub span: Span, } #[derive(LintDiagnostic)] -#[diag(hir_analysis_rpitit_refined)] -#[note] -#[note(hir_analysis_feedback_note)] +#[diag("impl trait in impl method signature does not match trait method signature")] +#[note( + "add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate" +)] +#[note( + "we are soliciting feedback, see issue #121718 for more information" +)] pub(crate) struct ReturnPositionImplTraitInTraitRefined<'tcx> { - #[suggestion(applicability = "maybe-incorrect", code = "{pre}{return_ty}{post}")] + #[suggestion( + "replace the return type so that it matches the trait", + applicability = "maybe-incorrect", + code = "{pre}{return_ty}{post}" + )] pub impl_return_span: Span, - #[label] + #[label("return type from trait method defined here")] pub trait_return_span: Option, - #[label(hir_analysis_unmatched_bound_label)] + #[label("this bound is stronger than that defined on the trait")] pub unmatched_bound: Option, pub pre: &'static str, @@ -1039,33 +1140,43 @@ pub(crate) struct ReturnPositionImplTraitInTraitRefined<'tcx> { } #[derive(LintDiagnostic)] -#[diag(hir_analysis_rpitit_refined_lifetimes)] -#[note] -#[note(hir_analysis_feedback_note)] +#[diag("impl trait in impl method captures fewer lifetimes than in trait")] +#[note( + "add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate" +)] +#[note( + "we are soliciting feedback, see issue #121718 for more information" +)] pub(crate) struct ReturnPositionImplTraitInTraitRefinedLifetimes { - #[suggestion(applicability = "maybe-incorrect", code = "{suggestion}")] + #[suggestion( + "modify the `use<..>` bound to capture the same lifetimes that the trait does", + applicability = "maybe-incorrect", + code = "{suggestion}" + )] pub suggestion_span: Span, pub suggestion: String, } #[derive(Diagnostic)] -#[diag(hir_analysis_inherent_ty_outside, code = E0390)] -#[help] +#[diag("cannot define inherent `impl` for a type outside of the crate where the type is defined", code = E0390)] +#[help("consider moving this inherent impl into the crate defining the type if possible")] pub(crate) struct InherentTyOutside { #[primary_span] - #[help(hir_analysis_span_help)] + #[help( + "alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_dispatch_from_dyn_repr, code = E0378)] +#[diag("structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]`", code = E0378)] pub(crate) struct DispatchFromDynRepr { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_pointee_not_struct, code = E0802)] +#[diag("`derive(CoercePointee)` is only applicable to `struct`, instead of `{$kind}`", code = E0802)] pub(crate) struct CoercePointeeNotStruct { #[primary_span] pub span: Span, @@ -1073,65 +1184,81 @@ pub(crate) struct CoercePointeeNotStruct { } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_pointee_not_concrete_ty, code = E0802)] +#[diag("`derive(CoercePointee)` is only applicable to `struct`", code = E0802)] pub(crate) struct CoercePointeeNotConcreteType { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_pointee_no_user_validity_assertion, code = E0802)] +#[diag("asserting applicability of `derive(CoercePointee)` on a target data is forbidden", code = E0802)] pub(crate) struct CoercePointeeNoUserValidityAssertion { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_pointee_not_transparent, code = E0802)] +#[diag("`derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout", code = E0802)] pub(crate) struct CoercePointeeNotTransparent { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_pointee_no_field, code = E0802)] +#[diag("`CoercePointee` can only be derived on `struct`s with at least one field", code = E0802)] pub(crate) struct CoercePointeeNoField { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_inherent_ty_outside_relevant, code = E0390)] -#[help] +#[diag("cannot define inherent `impl` for a type outside of the crate where the type is defined", code = E0390)] +#[help("consider moving this inherent impl into the crate defining the type if possible")] pub(crate) struct InherentTyOutsideRelevant { #[primary_span] pub span: Span, - #[help(hir_analysis_span_help)] + #[help("alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items")] pub help_span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_inherent_ty_outside_new, code = E0116)] -#[note] +#[diag("cannot define inherent `impl` for a type outside of the crate where the type is defined", code = E0116)] +#[help( + "consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it" +)] +#[note( + "for more details about the orphan rules, see " +)] pub(crate) struct InherentTyOutsideNew { #[primary_span] - #[label] + #[label("impl for type defined outside of crate")] pub span: Span, + #[subdiagnostic] + pub note: Option, +} + +#[derive(Subdiagnostic)] +#[note("`{$ty_name}` does not define a new type, only an alias of `{$alias_ty_name}` defined here")] +pub(crate) struct InherentTyOutsideNewAliasNote { + #[primary_span] + pub span: Span, + pub ty_name: String, + pub alias_ty_name: String, } #[derive(Diagnostic)] -#[diag(hir_analysis_inherent_ty_outside_primitive, code = E0390)] -#[help] +#[diag("cannot define inherent `impl` for primitive types outside of `core`", code = E0390)] +#[help("consider moving this inherent impl into `core` if possible")] pub(crate) struct InherentTyOutsidePrimitive { #[primary_span] pub span: Span, - #[help(hir_analysis_span_help)] + #[help("alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items")] pub help_span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_inherent_primitive_ty, code = E0390)] -#[help] +#[diag("cannot define inherent `impl` for primitive types", code = E0390)] +#[help("consider using an extension trait instead")] pub(crate) struct InherentPrimitiveTy<'a> { #[primary_span] pub span: Span, @@ -1140,32 +1267,34 @@ pub(crate) struct InherentPrimitiveTy<'a> { } #[derive(Subdiagnostic)] -#[note(hir_analysis_inherent_primitive_ty_note)] +#[note( + "you could also try moving the reference to uses of `{$subty}` (such as `self`) within the implementation" +)] pub(crate) struct InherentPrimitiveTyNote<'a> { pub subty: Ty<'a>, } #[derive(Diagnostic)] -#[diag(hir_analysis_inherent_dyn, code = E0785)] -#[note] +#[diag("cannot define inherent `impl` for a dyn auto trait", code = E0785)] +#[note("define and implement a new trait or type instead")] pub(crate) struct InherentDyn { #[primary_span] - #[label] + #[label("impl requires at least one non-auto trait")] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_inherent_nominal, code = E0118)] -#[note] +#[diag("no nominal type found for inherent implementation", code = E0118)] +#[note("either implement a trait on it or create a newtype to wrap it instead")] pub(crate) struct InherentNominal { #[primary_span] - #[label] + #[label("impl requires a nominal type")] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_dispatch_from_dyn_zst, code = E0378)] -#[note] +#[diag("the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else", code = E0378)] +#[note("extra field `{$name}` of type `{$ty}` is not allowed")] pub(crate) struct DispatchFromDynZST<'a> { #[primary_span] pub span: Span, @@ -1174,28 +1303,30 @@ pub(crate) struct DispatchFromDynZST<'a> { } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_zero, code = E0374)] +#[diag("implementing `{$trait_name}` requires a field to be coerced", code = E0374)] pub(crate) struct CoerceNoField { #[primary_span] pub span: Span, pub trait_name: &'static str, - #[note(hir_analysis_coercion_between_struct_single_note)] + #[note("expected a single field to be coerced, none found")] pub note: bool, } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_multi, code = E0375)] +#[diag("implementing `{$trait_name}` does not allow multiple fields to be coerced", code = E0375)] pub(crate) struct CoerceMulti { pub trait_name: &'static str, #[primary_span] pub span: Span, pub number: usize, - #[note] + #[note( + "the trait `{$trait_name}` may only be implemented when a single field is being coerced" + )] pub fields: MultiSpan, } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_unsized_may, code = E0377)] +#[diag("the trait `{$trait_name}` may only be implemented for a coercion between structures", code = E0377)] pub(crate) struct CoerceUnsizedNonStruct { #[primary_span] pub span: Span, @@ -1203,7 +1334,7 @@ pub(crate) struct CoerceUnsizedNonStruct { } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_same_pat_kind)] +#[diag("only pattern types with the same pattern can be coerced between each other")] pub(crate) struct CoerceSamePatKind { #[primary_span] pub span: Span, @@ -1213,43 +1344,49 @@ pub(crate) struct CoerceSamePatKind { } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_unsized_may, code = E0377)] +#[diag("the trait `{$trait_name}` may only be implemented for a coercion between structures", code = E0377)] pub(crate) struct CoerceSameStruct { #[primary_span] pub span: Span, pub trait_name: &'static str, - #[note(hir_analysis_coercion_between_struct_same_note)] + #[note( + "expected coercion between the same definition; expected `{$source_path}`, found `{$target_path}`" + )] pub note: bool, pub source_path: String, pub target_path: String, } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_unsized_field_validity)] +#[diag( + "for `{$ty}` to have a valid implementation of `{$trait_name}`, it must be possible to coerce the field of type `{$field_ty}`" +)] pub(crate) struct CoerceFieldValidity<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub trait_name: &'static str, - #[label] + #[label( + "`{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized" + )] pub field_span: Span, pub field_ty: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(hir_analysis_trait_cannot_impl_for_ty, code = E0204)] +#[diag("the trait `{$trait_name}` cannot be implemented for this type", code = E0204)] pub(crate) struct TraitCannotImplForTy { #[primary_span] pub span: Span, pub trait_name: String, - #[label] + #[label("this field does not implement `{$trait_name}`")] pub label_spans: Vec, #[subdiagnostic] pub notes: Vec, } #[derive(Subdiagnostic)] -#[note(hir_analysis_requires_note)] +#[note("the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}`")] pub(crate) struct ImplForTyRequires { #[primary_span] pub span: MultiSpan, @@ -1259,8 +1396,10 @@ pub(crate) struct ImplForTyRequires { } #[derive(Diagnostic)] -#[diag(hir_analysis_traits_with_default_impl, code = E0321)] -#[note] +#[diag("traits with a default impl, like `{$traits}`, cannot be implemented for {$problematic_kind} `{$self_ty}`", code = E0321)] +#[note( + "a trait object implements `{$traits}` if and only if `{$traits}` is one of the trait object's trait bounds" +)] pub(crate) struct TraitsWithDefaultImpl<'a> { #[primary_span] pub span: Span, @@ -1270,26 +1409,26 @@ pub(crate) struct TraitsWithDefaultImpl<'a> { } #[derive(Diagnostic)] -#[diag(hir_analysis_cross_crate_traits, code = E0321)] +#[diag("cross-crate traits with a default impl, like `{$traits}`, can only be implemented for a struct/enum type, not `{$self_ty}`", code = E0321)] pub(crate) struct CrossCrateTraits<'a> { #[primary_span] - #[label] + #[label("can't implement cross-crate trait with a default impl for non-struct/enum type")] pub span: Span, pub traits: String, pub self_ty: Ty<'a>, } #[derive(Diagnostic)] -#[diag(hir_analysis_cross_crate_traits_defined, code = E0321)] +#[diag("cross-crate traits with a default impl, like `{$traits}`, can only be implemented for a struct/enum type defined in the current crate", code = E0321)] pub(crate) struct CrossCrateTraitsDefined { #[primary_span] - #[label] + #[label("can't implement cross-crate trait for type in another crate")] pub span: Span, pub traits: String, } #[derive(Diagnostic)] -#[diag(hir_analysis_no_variant_named, code = E0599)] +#[diag("no variant named `{$ident}` found for enum `{$ty}`", code = E0599)] pub struct NoVariantNamed<'tcx> { #[primary_span] pub span: Span, @@ -1300,99 +1439,123 @@ pub struct NoVariantNamed<'tcx> { // FIXME(fmease): Deduplicate: #[derive(Diagnostic)] -#[diag(hir_analysis_ty_param_first_local, code = E0210)] -#[note] +#[diag("type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`)", code = E0210)] +#[note( + "implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type" +)] pub(crate) struct TyParamFirstLocal<'tcx> { #[primary_span] - #[label] + #[label( + "type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`)" + )] pub span: Span, - #[note(hir_analysis_case_note)] + #[note( + "in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last" + )] pub note: (), pub param: Ident, pub local_type: Ty<'tcx>, } #[derive(LintDiagnostic)] -#[diag(hir_analysis_ty_param_first_local, code = E0210)] -#[note] +#[diag("type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`)", code = E0210)] +#[note( + "implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type" +)] pub(crate) struct TyParamFirstLocalLint<'tcx> { - #[label] + #[label( + "type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`)" + )] pub span: Span, - #[note(hir_analysis_case_note)] + #[note( + "in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last" + )] pub note: (), pub param: Ident, pub local_type: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(hir_analysis_ty_param_some, code = E0210)] -#[note] +#[diag("type parameter `{$param}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param}>`)", code = E0210)] +#[note( + "implementing a foreign trait is only possible if at least one of the types for which it is implemented is local" +)] pub(crate) struct TyParamSome { #[primary_span] - #[label] + #[label("type parameter `{$param}` must be used as the type parameter for some local type")] pub span: Span, - #[note(hir_analysis_only_note)] + #[note("only traits defined in the current crate can be implemented for a type parameter")] pub note: (), pub param: Ident, } #[derive(LintDiagnostic)] -#[diag(hir_analysis_ty_param_some, code = E0210)] -#[note] +#[diag("type parameter `{$param}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param}>`)", code = E0210)] +#[note( + "implementing a foreign trait is only possible if at least one of the types for which it is implemented is local" +)] pub(crate) struct TyParamSomeLint { - #[label] + #[label("type parameter `{$param}` must be used as the type parameter for some local type")] pub span: Span, - #[note(hir_analysis_only_note)] + #[note("only traits defined in the current crate can be implemented for a type parameter")] pub note: (), pub param: Ident, } #[derive(Diagnostic)] pub(crate) enum OnlyCurrentTraits { - #[diag(hir_analysis_only_current_traits_outside, code = E0117)] + #[diag("only traits defined in the current crate can be implemented for types defined outside of the crate", code = E0117)] Outside { #[primary_span] span: Span, - #[note(hir_analysis_only_current_traits_note_uncovered)] - #[note(hir_analysis_only_current_traits_note_more_info)] - #[note(hir_analysis_only_current_traits_note)] + #[note("impl doesn't have any local type before any uncovered type parameters")] + #[note( + "for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules" + )] + #[note("define and implement a trait or new type instead")] note: (), }, - #[diag(hir_analysis_only_current_traits_primitive, code = E0117)] + #[diag("only traits defined in the current crate can be implemented for primitive types", code = E0117)] Primitive { #[primary_span] span: Span, - #[note(hir_analysis_only_current_traits_note_uncovered)] - #[note(hir_analysis_only_current_traits_note_more_info)] - #[note(hir_analysis_only_current_traits_note)] + #[note("impl doesn't have any local type before any uncovered type parameters")] + #[note( + "for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules" + )] + #[note("define and implement a trait or new type instead")] note: (), }, - #[diag(hir_analysis_only_current_traits_arbitrary, code = E0117)] + #[diag("only traits defined in the current crate can be implemented for arbitrary types", code = E0117)] Arbitrary { #[primary_span] span: Span, - #[note(hir_analysis_only_current_traits_note_uncovered)] - #[note(hir_analysis_only_current_traits_note_more_info)] - #[note(hir_analysis_only_current_traits_note)] + #[note("impl doesn't have any local type before any uncovered type parameters")] + #[note( + "for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules" + )] + #[note("define and implement a trait or new type instead")] note: (), }, } #[derive(Subdiagnostic)] -#[label(hir_analysis_only_current_traits_opaque)] +#[label( + "type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate" +)] pub(crate) struct OnlyCurrentTraitsOpaque { #[primary_span] pub span: Span, } #[derive(Subdiagnostic)] -#[label(hir_analysis_only_current_traits_foreign)] +#[label("this is not defined in the current crate because this is a foreign trait")] pub(crate) struct OnlyCurrentTraitsForeign { #[primary_span] pub span: Span, } #[derive(Subdiagnostic)] -#[label(hir_analysis_only_current_traits_name)] +#[label("this is not defined in the current crate because {$name} are always foreign")] pub(crate) struct OnlyCurrentTraitsName<'a> { #[primary_span] pub span: Span, @@ -1400,7 +1563,7 @@ pub(crate) struct OnlyCurrentTraitsName<'a> { } #[derive(Subdiagnostic)] -#[label(hir_analysis_only_current_traits_pointer)] +#[label("`{$pointer}` is not defined in the current crate because raw pointers are always foreign")] pub(crate) struct OnlyCurrentTraitsPointer<'a> { #[primary_span] pub span: Span, @@ -1408,7 +1571,7 @@ pub(crate) struct OnlyCurrentTraitsPointer<'a> { } #[derive(Subdiagnostic)] -#[label(hir_analysis_only_current_traits_ty)] +#[label("`{$ty}` is not defined in the current crate")] pub(crate) struct OnlyCurrentTraitsTy<'a> { #[primary_span] pub span: Span, @@ -1416,7 +1579,7 @@ pub(crate) struct OnlyCurrentTraitsTy<'a> { } #[derive(Subdiagnostic)] -#[label(hir_analysis_only_current_traits_adt)] +#[label("`{$name}` is not defined in the current crate")] pub(crate) struct OnlyCurrentTraitsAdt { #[primary_span] pub span: Span, @@ -1425,7 +1588,7 @@ pub(crate) struct OnlyCurrentTraitsAdt { #[derive(Subdiagnostic)] #[multipart_suggestion( - hir_analysis_only_current_traits_pointer_sugg, + "consider introducing a new wrapper type", applicability = "maybe-incorrect" )] pub(crate) struct OnlyCurrentTraitsPointerSugg<'a> { @@ -1438,104 +1601,120 @@ pub(crate) struct OnlyCurrentTraitsPointerSugg<'a> { } #[derive(Diagnostic)] -#[diag(hir_analysis_not_supported_delegation)] +#[diag("{$descr}")] pub(crate) struct UnsupportedDelegation<'a> { #[primary_span] pub span: Span, pub descr: &'a str, - #[label] + #[label("callee defined here")] pub callee_span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_method_should_return_future)] +#[diag("method should be `async` or return a future, but it is synchronous")] pub(crate) struct MethodShouldReturnFuture { #[primary_span] pub span: Span, pub method_name: Ident, - #[note] + #[note("this method is `async` so it expects a future to be returned")] pub trait_item_span: Option, } #[derive(Diagnostic)] -#[diag(hir_analysis_unused_generic_parameter)] +#[diag("{$param_def_kind} `{$param_name}` is never used")] pub(crate) struct UnusedGenericParameter { #[primary_span] - #[label] + #[label("unused {$param_def_kind}")] pub span: Span, pub param_name: Ident, pub param_def_kind: &'static str, - #[label(hir_analysis_usage_spans)] + #[label("`{$param_name}` is named here, but is likely unused in the containing type")] pub usage_spans: Vec, #[subdiagnostic] pub help: UnusedGenericParameterHelp, - #[help(hir_analysis_const_param_help)] + #[help( + "if you intended `{$param_name}` to be a const parameter, use `const {$param_name}: /* Type */` instead" + )] pub const_param_help: bool, } #[derive(Diagnostic)] -#[diag(hir_analysis_recursive_generic_parameter)] +#[diag("{$param_def_kind} `{$param_name}` is only used recursively")] pub(crate) struct RecursiveGenericParameter { #[primary_span] pub spans: Vec, - #[label] + #[label("{$param_def_kind} must be used non-recursively in the definition")] pub param_span: Span, pub param_name: Ident, pub param_def_kind: &'static str, #[subdiagnostic] pub help: UnusedGenericParameterHelp, - #[note] + #[note( + "all type parameters must be used in a non-recursive way in order to constrain their variance" + )] pub note: (), } #[derive(Subdiagnostic)] pub(crate) enum UnusedGenericParameterHelp { - #[help(hir_analysis_unused_generic_parameter_adt_help)] + #[help( + "consider removing `{$param_name}`, referring to it in a field, or using a marker such as `{$phantom_data}`" + )] Adt { param_name: Ident, phantom_data: String }, - #[help(hir_analysis_unused_generic_parameter_adt_no_phantom_data_help)] + #[help("consider removing `{$param_name}` or referring to it in a field")] AdtNoPhantomData { param_name: Ident }, - #[help(hir_analysis_unused_generic_parameter_ty_alias_help)] + #[help("consider removing `{$param_name}` or referring to it in the body of the type alias")] TyAlias { param_name: Ident }, } #[derive(Diagnostic)] -#[diag(hir_analysis_unconstrained_generic_parameter)] +#[diag( + "the {$param_def_kind} `{$param_name}` is not constrained by the impl trait, self type, or predicates" +)] pub(crate) struct UnconstrainedGenericParameter { #[primary_span] - #[label] + #[label("unconstrained {$param_def_kind}")] pub span: Span, pub param_name: Ident, pub param_def_kind: &'static str, - #[note(hir_analysis_const_param_note)] + #[note("expressions using a const parameter must map each value to a distinct output value")] pub const_param_note: bool, - #[note(hir_analysis_const_param_note2)] + #[note( + "proving the result of expressions other than the parameter are unique is not supported" + )] pub const_param_note2: bool, } #[derive(Diagnostic)] -#[diag(hir_analysis_opaque_captures_higher_ranked_lifetime, code = E0657)] +#[diag("`impl Trait` cannot capture {$bad_place}", code = E0657)] pub(crate) struct OpaqueCapturesHigherRankedLifetime { #[primary_span] pub span: MultiSpan, - #[label] + #[label("`impl Trait` implicitly captures all lifetimes in scope")] pub label: Option, - #[note] + #[note("lifetime declared here")] pub decl_span: MultiSpan, pub bad_place: &'static str, } #[derive(Subdiagnostic)] pub(crate) enum InvalidReceiverTyHint { - #[note(hir_analysis_invalid_receiver_ty_help_weak_note)] + #[note( + "`Weak` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `Weak` in a newtype wrapper for which you implement `Receiver`" + )] Weak, - #[note(hir_analysis_invalid_receiver_ty_help_nonnull_note)] + #[note( + "`NonNull` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `NonNull` in a newtype wrapper for which you implement `Receiver`" + )] NonNull, } #[derive(Diagnostic)] -#[diag(hir_analysis_invalid_receiver_ty_no_arbitrary_self_types, code = E0307)] -#[note] -#[help(hir_analysis_invalid_receiver_ty_help_no_arbitrary_self_types)] +#[diag("invalid `self` parameter type: `{$receiver_ty}`", code = E0307)] +#[note("type of `self` must be `Self` or a type that dereferences to it")] +#[help( + "consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`)" +)] pub(crate) struct InvalidReceiverTyNoArbitrarySelfTypes<'tcx> { #[primary_span] pub span: Span, @@ -1543,9 +1722,11 @@ pub(crate) struct InvalidReceiverTyNoArbitrarySelfTypes<'tcx> { } #[derive(Diagnostic)] -#[diag(hir_analysis_invalid_receiver_ty, code = E0307)] -#[note] -#[help(hir_analysis_invalid_receiver_ty_help)] +#[diag("invalid `self` parameter type: `{$receiver_ty}`", code = E0307)] +#[note("type of `self` must be `Self` or some type implementing `Receiver`")] +#[help( + "consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc`" +)] pub(crate) struct InvalidReceiverTy<'tcx> { #[primary_span] pub span: Span, @@ -1555,9 +1736,11 @@ pub(crate) struct InvalidReceiverTy<'tcx> { } #[derive(Diagnostic)] -#[diag(hir_analysis_invalid_generic_receiver_ty, code = E0801)] -#[note] -#[help(hir_analysis_invalid_generic_receiver_ty_help)] +#[diag("invalid generic `self` parameter type: `{$receiver_ty}`", code = E0801)] +#[note("type of `self` must not be a method generic parameter type")] +#[help( + "use a concrete type such as `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`)" +)] pub(crate) struct InvalidGenericReceiverTy<'tcx> { #[primary_span] pub span: Span, @@ -1565,28 +1748,32 @@ pub(crate) struct InvalidGenericReceiverTy<'tcx> { } #[derive(Diagnostic)] -#[diag(hir_analysis_cmse_inputs_stack_spill, code = E0798)] -#[note] +#[diag("arguments for `{$abi}` function too large to pass via registers", code = E0798)] +#[note( + "functions with the `{$abi}` ABI must pass all their arguments via the 4 32-bit argument registers" +)] pub(crate) struct CmseInputsStackSpill { #[primary_span] - #[label] + #[label("does not fit in the available registers")] pub spans: Vec, pub abi: ExternAbi, } #[derive(Diagnostic)] -#[diag(hir_analysis_cmse_output_stack_spill, code = E0798)] -#[note(hir_analysis_note1)] -#[note(hir_analysis_note2)] +#[diag("return value of `{$abi}` function too large to pass via registers", code = E0798)] +#[note("functions with the `{$abi}` ABI must pass their result via the available return registers")] +#[note( + "the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size" +)] pub(crate) struct CmseOutputStackSpill { #[primary_span] - #[label] + #[label("this type doesn't fit in the available registers")] pub span: Span, pub abi: ExternAbi, } #[derive(Diagnostic)] -#[diag(hir_analysis_cmse_generic, code = E0798)] +#[diag("generics are not allowed in `extern {$abi}` signatures", code = E0798)] pub(crate) struct CmseGeneric { #[primary_span] pub span: Span, @@ -1594,7 +1781,7 @@ pub(crate) struct CmseGeneric { } #[derive(Diagnostic)] -#[diag(hir_analysis_cmse_impl_trait, code = E0798)] +#[diag("`impl Trait` is not allowed in `extern {$abi}` signatures", code = E0798)] pub(crate) struct CmseImplTrait { #[primary_span] pub span: Span, @@ -1602,14 +1789,14 @@ pub(crate) struct CmseImplTrait { } #[derive(Diagnostic)] -#[diag(hir_analysis_bad_return_type_notation_position)] +#[diag("return type notation not allowed in this position yet")] pub(crate) struct BadReturnTypeNotation { #[primary_span] pub span: Span, } #[derive(LintDiagnostic)] -#[diag(hir_analysis_supertrait_item_shadowing)] +#[diag("trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait")] pub(crate) struct SupertraitItemShadowing { pub item: Symbol, pub subtrait: Symbol, @@ -1619,13 +1806,13 @@ pub(crate) struct SupertraitItemShadowing { #[derive(Subdiagnostic)] pub(crate) enum SupertraitItemShadowee { - #[note(hir_analysis_supertrait_item_shadowee)] + #[note("item from `{$supertrait}` is shadowed by a subtrait item")] Labeled { #[primary_span] span: Span, supertrait: Symbol, }, - #[note(hir_analysis_supertrait_item_multiple_shadowee)] + #[note("items from several supertraits are shadowed: {$traits}")] Several { #[primary_span] spans: MultiSpan, @@ -1634,23 +1821,25 @@ pub(crate) enum SupertraitItemShadowee { } #[derive(Diagnostic)] -#[diag(hir_analysis_dyn_trait_assoc_item_binding_mentions_self)] +#[diag("{$kind} binding in trait object type mentions `Self`")] pub(crate) struct DynTraitAssocItemBindingMentionsSelf { #[primary_span] - #[label] + #[label("contains a mention of `Self`")] pub span: Span, pub kind: &'static str, - #[label(hir_analysis_binding_label)] + #[label("this binding mentions `Self`")] pub binding: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_abi_custom_clothed_function)] +#[diag( + "items with the \"custom\" ABI can only be declared externally or defined via naked functions" +)] pub(crate) struct AbiCustomClothedFunction { #[primary_span] pub span: Span, #[suggestion( - hir_analysis_suggestion, + "convert this to an `#[unsafe(naked)]` function", applicability = "maybe-incorrect", code = "#[unsafe(naked)]\n", style = "short" @@ -1659,47 +1848,49 @@ pub(crate) struct AbiCustomClothedFunction { } #[derive(Diagnostic)] -#[diag(hir_analysis_async_drop_without_sync_drop)] -#[help] +#[diag("`AsyncDrop` impl without `Drop` impl")] +#[help( + "type implementing `AsyncDrop` trait must also implement `Drop` trait to be used in sync context and unwinds" +)] pub(crate) struct AsyncDropWithoutSyncDrop { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_lifetimes_or_bounds_mismatch_on_eii)] +#[diag("lifetime parameters or bounds of `{$ident}` do not match the declaration")] pub(crate) struct LifetimesOrBoundsMismatchOnEii { #[primary_span] - #[label] + #[label("lifetimes do not match")] pub span: Span, - #[label(hir_analysis_generics_label)] + #[label("lifetimes in impl do not match this signature")] pub generics_span: Span, - #[label(hir_analysis_where_label)] + #[label("this `where` clause might not match the one in the trait")] pub where_span: Option, - #[label(hir_analysis_bounds_label)] + #[label("this bound might be missing in the impl")] pub bounds_span: Vec, pub ident: Symbol, } #[derive(Diagnostic)] -#[diag(hir_analysis_eii_with_generics)] -#[help] +#[diag("`{$impl_name}` cannot have generic parameters other than lifetimes")] +#[help("`#[{$eii_name}]` marks the implementation of an \"externally implementable item\"")] pub(crate) struct EiiWithGenerics { #[primary_span] pub span: Span, - #[label] + #[label("required by this attribute")] pub attr: Span, pub eii_name: Symbol, pub impl_name: Symbol, } #[derive(Diagnostic)] -#[diag(hir_analysis_impl_unpin_for_pin_projected_type)] +#[diag("explicit impls for the `Unpin` trait are not permitted for structurally pinned types")] pub(crate) struct ImplUnpinForPinProjectedType { #[primary_span] - #[label] + #[label("impl of `Unpin` not allowed")] pub span: Span, - #[help] + #[help("`{$adt_name}` is structurally pinned because it is marked as `#[pin_v2]`")] pub adt_span: Span, pub adt_name: Symbol, } diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs index 8a83866b7fa4..f9e62b8ebcf8 100644 --- a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs +++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs @@ -3,48 +3,54 @@ use rustc_macros::Diagnostic; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] -#[diag(hir_analysis_param_not_captured)] -#[note] +#[diag("`impl Trait` must mention all {$kind} parameters in scope in `use<...>`")] +#[note( + "currently, all {$kind} parameters are required to be mentioned in the precise captures list" +)] pub(crate) struct ParamNotCaptured { #[primary_span] pub opaque_span: Span, - #[label] + #[label("{$kind} parameter is implicitly captured by this `impl Trait`")] pub param_span: Span, pub kind: &'static str, } #[derive(Diagnostic)] -#[diag(hir_analysis_self_ty_not_captured)] -#[note] +#[diag("`impl Trait` must mention the `Self` type of the trait in `use<...>`")] +#[note("currently, all type parameters are required to be mentioned in the precise captures list")] pub(crate) struct SelfTyNotCaptured { #[primary_span] pub opaque_span: Span, - #[label] + #[label("`Self` type parameter is implicitly captured by this `impl Trait`")] pub trait_span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_lifetime_not_captured)] +#[diag( + "`impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list" +)] pub(crate) struct LifetimeNotCaptured { #[primary_span] pub use_span: Span, - #[label(hir_analysis_param_label)] + #[label("this lifetime parameter is captured")] pub param_span: Span, - #[label] + #[label("lifetime captured due to being mentioned in the bounds of the `impl Trait`")] pub opaque_span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_lifetime_implicitly_captured)] +#[diag( + "`impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list" +)] pub(crate) struct LifetimeImplicitlyCaptured { #[primary_span] pub opaque_span: Span, - #[label(hir_analysis_param_label)] + #[label("all lifetime parameters originating from a trait are captured implicitly")] pub param_span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_bad_precise_capture)] +#[diag("expected {$kind} parameter in `use<...>` precise captures list, found {$found}")] pub(crate) struct BadPreciseCapture { #[primary_span] pub span: Span, @@ -53,31 +59,31 @@ pub(crate) struct BadPreciseCapture { } #[derive(Diagnostic)] -#[diag(hir_analysis_precise_capture_self_alias, code = E0799)] +#[diag("`Self` can't be captured in `use<...>` precise captures list, since it is an alias", code = E0799)] pub(crate) struct PreciseCaptureSelfAlias { #[primary_span] pub span: Span, - #[label] + #[label("`Self` is not a generic argument, but an alias to the type of the {$what}")] pub self_span: Span, pub what: &'static str, } #[derive(Diagnostic)] -#[diag(hir_analysis_duplicate_precise_capture)] +#[diag("cannot capture parameter `{$name}` twice")] pub(crate) struct DuplicatePreciseCapture { #[primary_span] pub first_span: Span, pub name: Symbol, - #[label] + #[label("parameter captured again here")] pub second_span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_lifetime_must_be_first)] +#[diag("lifetime parameter `{$name}` must be listed before non-lifetime parameters")] pub(crate) struct LifetimesMustBeFirst { #[primary_span] pub lifetime_span: Span, pub name: Symbol, - #[label] + #[label("move the lifetime before this parameter")] pub other_span: Span, } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 3515ce4ea939..670312ff1ba1 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::{ self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, Upcast, }; -use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; +use rustc_span::{ErrorGuaranteed, Ident, Span, kw}; use rustc_trait_selection::traits; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -171,7 +171,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); // Skip adding any default bounds if `#![rustc_no_implicit_bounds]` - if tcx.has_attr(CRATE_DEF_ID, sym::rustc_no_implicit_bounds) { + if find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcNoImplicitBounds) { return; } @@ -285,7 +285,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { context: ImpliedBoundsContext<'tcx>, ) -> bool { let collected = collect_bounds(hir_bounds, context, trait_def_id); - !self.tcx().has_attr(CRATE_DEF_ID, sym::rustc_no_implicit_bounds) && !collected.any() + !find_attr!(self.tcx().get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcNoImplicitBounds) + && !collected.any() } fn reject_duplicate_relaxed_bounds(&self, relaxed_bounds: SmallVec<[&PolyTraitRef<'_>; 1]>) { @@ -603,22 +604,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }); if let ty::AssocTag::Const = assoc_tag - && !find_attr!( - self.tcx().get_all_attrs(assoc_item.def_id), - AttributeKind::TypeConst(_) - ) + && !self.tcx().is_type_const(assoc_item.def_id) { if tcx.features().min_generic_const_args() { let mut err = self.dcx().struct_span_err( constraint.span, - "use of trait associated const without `#[type_const]`", + "use of trait associated const not defined as `type const`", ); - err.note("the declaration in the trait must be marked with `#[type_const]`"); + err.note("the declaration in the trait must begin with `type const` not just `const` alone"); return Err(err.emit()); } else { let err = self.dcx().span_delayed_bug( constraint.span, - "use of trait associated const without `#[type_const]`", + "use of trait associated const defined as `type const`", ); return Err(err); } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 81bdfc1705a1..58c296d92c24 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -1,8 +1,8 @@ -use rustc_abi::{BackendRepr, ExternAbi, Float, Integer, Primitive, Scalar}; +use rustc_abi::ExternAbi; use rustc_errors::{DiagCtxtHandle, E0781, struct_span_code_err}; use rustc_hir::{self as hir, HirId}; use rustc_middle::bug; -use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; +use rustc_middle::ty::layout::{LayoutCx, LayoutError, TyAndLayout}; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::Span; @@ -150,8 +150,9 @@ fn is_valid_cmse_output<'tcx>( let typing_env = ty::TypingEnv::fully_monomorphized(); let layout = tcx.layout_of(typing_env.as_query_input(return_type))?; + let layout_cx = LayoutCx::new(tcx, typing_env); - if !is_valid_cmse_output_layout(layout) { + if !is_valid_cmse_output_layout(layout_cx, layout) { dcx.emit_err(errors::CmseOutputStackSpill { span: fn_decl.output.span(), abi }); } @@ -159,25 +160,20 @@ fn is_valid_cmse_output<'tcx>( } /// Returns whether the output will fit into the available registers -fn is_valid_cmse_output_layout<'tcx>(layout: TyAndLayout<'tcx>) -> bool { +fn is_valid_cmse_output_layout<'tcx>(cx: LayoutCx<'tcx>, layout: TyAndLayout<'tcx>) -> bool { let size = layout.layout.size().bytes(); if size <= 4 { return true; - } else if size > 8 { + } else if size != 8 { return false; } - // Accept scalar 64-bit types. - let BackendRepr::Scalar(scalar) = layout.layout.backend_repr else { - return false; - }; - - let Scalar::Initialized { value, .. } = scalar else { - return false; - }; - - matches!(value, Primitive::Int(Integer::I64, _) | Primitive::Float(Float::F64)) + // Accept (transparently wrapped) scalar 64-bit primitives. + matches!( + layout.peel_transparent_wrappers(&cx).ty.kind(), + ty::Int(ty::IntTy::I64) | ty::Uint(ty::UintTy::U64) | ty::Float(ty::FloatTy::F64) + ) } fn should_emit_layout_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index 57defac577d8..a0b9b60cde5c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -460,7 +460,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Parent lifetime must have failed to resolve. Don't emit a redundant error. RegionInferReason::ExplicitObjectLifetime } else { - RegionInferReason::ObjectLifetimeDefault + RegionInferReason::ObjectLifetimeDefault(span.shrink_to_hi()) } } else { RegionInferReason::ExplicitObjectLifetime diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index d114691b25e8..36dae4c2798e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -3,8 +3,8 @@ use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordMap; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, ErrorGuaranteed, MultiSpan, SuggestionStyle, listify, pluralize, - struct_span_code_err, + Applicability, Diag, ErrorGuaranteed, MultiSpan, SuggestionStyle, inline_fluent, listify, + pluralize, struct_span_code_err, }; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -31,7 +31,6 @@ use crate::errors::{ self, AssocItemConstraintsNotAllowedHere, ManualImplementation, ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits, }; -use crate::fluent_generated as fluent; use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { @@ -305,7 +304,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // was also not an exact match, so we also suggest changing it. err.span_suggestion_verbose( assoc_ident.span, - fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, + inline_fluent!("...and changing the associated {$assoc_kind} name"), suggested_name, Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index cbd0bec38d57..ff0a5a8df0fa 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -393,8 +393,7 @@ pub fn check_generic_arg_count_for_call( IsMethodCall::Yes => GenericArgPosition::MethodCall, IsMethodCall::No => GenericArgPosition::Value, }; - let has_self = generics.parent.is_none() && generics.has_self; - check_generic_arg_count(cx, def_id, seg, generics, gen_pos, has_self) + check_generic_arg_count(cx, def_id, seg, generics, gen_pos, generics.has_own_self()) } /// Checks that the correct number of generic arguments have been provided. diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 9f84f652698b..1578f098dd95 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -28,10 +28,9 @@ use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err, }; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId, find_attr}; +use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::DynCompatibilityViolation; use rustc_macros::{TypeFoldable, TypeVisitable}; @@ -105,7 +104,7 @@ pub enum RegionInferReason<'a> { /// Lifetime on a trait object that is spelled explicitly, e.g. `+ 'a` or `+ '_`. ExplicitObjectLifetime, /// A trait object's lifetime when it is elided, e.g. `dyn Any`. - ObjectLifetimeDefault, + ObjectLifetimeDefault(Span), /// Generic lifetime parameter Param(&'a ty::GenericParamDef), RegionPredicate, @@ -405,6 +404,11 @@ impl<'tcx> ForbidMCGParamUsesFolder<'tcx> { diag.span_note(impl_.self_ty.span, "not a concrete type"); } } + if self.tcx.features().min_generic_const_args() + && !self.tcx.features().opaque_generic_const_args() + { + diag.help("add `#![feature(opaque_generic_const_args)]` to allow generic expressions as the RHS of const items"); + } diag.emit() } } @@ -1423,14 +1427,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { LowerTypeRelativePathMode::Const, )? { TypeRelativePath::AssocItem(def_id, args) => { - if !find_attr!(self.tcx().get_all_attrs(def_id), AttributeKind::TypeConst(_)) { - let mut err = self.dcx().struct_span_err( - span, - "use of trait associated const without `#[type_const]`", - ); - err.note("the declaration in the trait must be marked with `#[type_const]`"); - return Err(err.emit()); - } + self.require_type_const_attribute(def_id, span)?; let ct = Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(def_id, args)); let ct = self.check_param_uses_if_mcg(ct, span, false); Ok(ct) @@ -1886,30 +1883,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { item_def_id: DefId, trait_segment: Option<&hir::PathSegment<'tcx>>, item_segment: &hir::PathSegment<'tcx>, - ) -> Const<'tcx> { - match self.lower_resolved_assoc_item_path( + ) -> Result, ErrorGuaranteed> { + let (item_def_id, item_args) = self.lower_resolved_assoc_item_path( span, opt_self_ty, item_def_id, trait_segment, item_segment, ty::AssocTag::Const, - ) { - Ok((item_def_id, item_args)) => { - if !find_attr!(self.tcx().get_all_attrs(item_def_id), AttributeKind::TypeConst(_)) { - let mut err = self.dcx().struct_span_err( - span, - "use of `const` in the type system without `#[type_const]`", - ); - err.note("the declaration must be marked with `#[type_const]`"); - return Const::new_error(self.tcx(), err.emit()); - } - - let uv = ty::UnevaluatedConst::new(item_def_id, item_args); - Const::new_unevaluated(self.tcx(), uv) - } - Err(guar) => Const::new_error(self.tcx(), guar), - } + )?; + self.require_type_const_attribute(item_def_id, span)?; + let uv = ty::UnevaluatedConst::new(item_def_id, item_args); + Ok(Const::new_unevaluated(self.tcx(), uv)) } /// Lower a [resolved][hir::QPath::Resolved] (type-level) associated item path. @@ -2397,8 +2382,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::ConstArgKind::Anon(anon) => self.lower_const_arg_anon(anon), hir::ConstArgKind::Infer(()) => self.ct_infer(None, const_arg.span), hir::ConstArgKind::Error(e) => ty::Const::new_error(tcx, e), - hir::ConstArgKind::Literal(kind) => { - self.lower_const_arg_literal(&kind, ty, const_arg.span) + hir::ConstArgKind::Literal { lit, negated } => { + self.lower_const_arg_literal(&lit, negated, ty, const_arg.span) } } } @@ -2669,6 +2654,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lower_const_param(def_id, hir_id) } Res::Def(DefKind::Const, did) => { + if let Err(guar) = self.require_type_const_attribute(did, span) { + return Const::new_error(self.tcx(), guar); + } + assert_eq!(opt_self_ty, None); let [leading_segments @ .., segment] = path.segments else { bug!() }; let _ = self @@ -2719,6 +2708,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_segment, path.segments.last().unwrap(), ) + .unwrap_or_else(|guar| Const::new_error(tcx, guar)) } Res::Def(DefKind::Static { .. }, _) => { span_bug!(span, "use of bare `static` ConstArgKind::Path's not yet supported") @@ -2805,9 +2795,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } #[instrument(skip(self), level = "debug")] - fn lower_const_arg_literal(&self, kind: &LitKind, ty: Ty<'tcx>, span: Span) -> Const<'tcx> { + fn lower_const_arg_literal( + &self, + kind: &LitKind, + neg: bool, + ty: Ty<'tcx>, + span: Span, + ) -> Const<'tcx> { let tcx = self.tcx(); - let input = LitToConstInput { lit: *kind, ty, neg: false }; + let input = LitToConstInput { lit: *kind, ty, neg }; tcx.at(span).lit_to_const(input) } @@ -2844,6 +2840,34 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .map(|l| tcx.at(expr.span).lit_to_const(l)) } + fn require_type_const_attribute( + &self, + def_id: DefId, + span: Span, + ) -> Result<(), ErrorGuaranteed> { + let tcx = self.tcx(); + if tcx.is_type_const(def_id) { + Ok(()) + } else { + let mut err = self.dcx().struct_span_err( + span, + "use of `const` in the type system not defined as `type const`", + ); + if def_id.is_local() { + let name = tcx.def_path_str(def_id); + err.span_suggestion( + tcx.def_span(def_id).shrink_to_lo(), + format!("add `type` before `const` for `{name}`"), + format!("type "), + Applicability::MaybeIncorrect, + ); + } else { + err.note("only consts marked defined as `type const` may be used in types"); + } + Err(err.emit()) + } + } + fn lower_delegation_ty(&self, idx: hir::InferDelegationKind) -> Ty<'tcx> { let delegation_sig = self.tcx().inherit_sig_for_delegation_item(self.item_def_id()); match idx { diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index d414f4dbcc24..08da8d19344d 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -139,7 +139,7 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>( }, hir::Node::TraitItem(item) => match item.kind { hir::TraitItemKind::Type(_, ty) => ty.into_iter().collect(), - hir::TraitItemKind::Const(ty, _) => vec![ty], + hir::TraitItemKind::Const(ty, _, _) => vec![ty], ref item => bug!("Unexpected TraitItem {:?}", item), }, hir::Node::Item(item) => match item.kind { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index f7a143435959..70209993a510 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -56,7 +56,7 @@ This API is completely unstable and subject to change. */ // tidy-alphabetical-start -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(default_field_values)] #![feature(gen_blocks)] #![feature(if_let_guard)] @@ -85,12 +85,9 @@ mod variance; pub use errors::NoVariantNamed; use rustc_abi::{CVariadicStatus, ExternAbi}; -use rustc_hir::attrs::AttributeKind; +use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::lints::DelayedLint; -use rustc_hir::{ - find_attr, {self as hir}, -}; use rustc_middle::mir::interpret::GlobalId; use rustc_middle::query::Providers; use rustc_middle::ty::{Const, Ty, TyCtxt}; @@ -102,8 +99,6 @@ use rustc_trait_selection::traits; pub use crate::collect::suggest_impl_trait; use crate::hir_ty_lowering::HirTyLowerer; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - fn check_c_variadic_abi(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: ExternAbi, span: Span) { if !decl.c_variadic { // Not even a variadic function. @@ -238,7 +233,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } DefKind::Const if !tcx.generics_of(item_def_id).own_requires_monomorphization() - && !find_attr!(tcx.get_all_attrs(item_def_id), AttributeKind::TypeConst(_)) => + && !tcx.is_type_const(item_def_id) => { // FIXME(generic_const_items): Passing empty instead of identity args is fishy but // seems to be fine for now. Revisit this! diff --git a/compiler/rustc_hir_analysis/src/outlives/dump.rs b/compiler/rustc_hir_analysis/src/outlives/dump.rs index 4233896c3720..cf770a2561db 100644 --- a/compiler/rustc_hir_analysis/src/outlives/dump.rs +++ b/compiler/rustc_hir_analysis/src/outlives/dump.rs @@ -1,10 +1,12 @@ +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_middle::bug; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::sym; pub(crate) fn inferred_outlives(tcx: TyCtxt<'_>) { for id in tcx.hir_free_items() { - if !tcx.has_attr(id.owner_id, sym::rustc_outlives) { + if !find_attr!(tcx.get_all_attrs(id.owner_id), AttributeKind::RustcOutlives) { continue; } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index e0e9fe775853..fc6cc4e67251 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -937,7 +937,7 @@ impl<'a> State<'a> { self.maybe_print_comment(ti.span.lo()); self.print_attrs(self.attrs(ti.hir_id())); match ti.kind { - hir::TraitItemKind::Const(ty, default) => { + hir::TraitItemKind::Const(ty, default, _) => { self.print_associated_const(ti.ident, ti.generics, ty, default); } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(arg_idents)) => { @@ -1158,9 +1158,12 @@ impl<'a> State<'a> { ConstArgKind::Anon(anon) => self.print_anon_const(anon), ConstArgKind::Error(_) => self.word("/*ERROR*/"), ConstArgKind::Infer(..) => self.word("_"), - ConstArgKind::Literal(node) => { + ConstArgKind::Literal { lit, negated } => { + if *negated { + self.word("-"); + } let span = const_arg.span; - self.print_literal(&Spanned { span, node: *node }) + self.print_literal(&Spanned { span, node: *lit }) } } } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index d04bd6f2295a..e20d5cdf2889 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -3,9 +3,10 @@ use std::iter; use rustc_abi::{CanonAbi, ExternAbi}; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey, inline_fluent}; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, HirId, LangItem}; +use rustc_hir::{self as hir, HirId, LangItem, find_attr}; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; @@ -526,7 +527,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `#[rustc_evaluate_where_clauses]` trigger special output // to let us test the trait evaluation system. if self.has_rustc_attrs - && self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) + && find_attr!( + self.tcx.get_all_attrs(def_id), + AttributeKind::RustcEvaluateWhereClauses + ) { let predicates = self.tcx.predicates_of(def_id); let predicates = predicates.instantiate(self.tcx, args); diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 3f13a102684e..b5094d736dd5 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -572,6 +572,17 @@ impl<'a, 'tcx> CastCheck<'tcx> { let metadata = known_metadata.unwrap_or("type-specific metadata"); let known_wide = known_metadata.is_some(); let span = self.cast_span; + let param_note = (!known_wide) + .then(|| match cast_ty.kind() { + ty::RawPtr(pointee, _) => match pointee.kind() { + ty::Param(param) => { + Some(errors::IntToWideParamNote { param: param.name }) + } + _ => None, + }, + _ => None, + }) + .flatten(); fcx.dcx().emit_err(errors::IntToWide { span, metadata, @@ -579,6 +590,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { cast_ty, expr_if_nightly, known_wide, + param_note, }); } CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => { diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 36a07b361d9d..88d2e80f1521 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -644,7 +644,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { .infcx .visit_proof_tree( Goal::new(self.tcx, self.param_env, pred), - &mut CoerceVisitor { fcx: self.fcx, span: self.cause.span }, + &mut CoerceVisitor { fcx: self.fcx, span: self.cause.span, errored: false }, ) .is_break() { @@ -1961,6 +1961,10 @@ impl<'tcx> CoerceMany<'tcx> { struct CoerceVisitor<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, + /// Whether the coercion is impossible. If so we sometimes still try to + /// coerce in these cases to emit better errors. This changes the behavior + /// when hitting the recursion limit. + errored: bool, } impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> { @@ -1987,6 +1991,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> { // If we prove the `Unsize` or `CoerceUnsized` goal, continue recursing. Ok(Certainty::Yes) => ControlFlow::Continue(()), Err(NoSolution) => { + self.errored = true; // 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()[..] @@ -2019,4 +2024,15 @@ impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> { } } } + + fn on_recursion_limit(&mut self) -> Self::Result { + if self.errored { + // This prevents accidentally committing unfulfilled unsized coercions while trying to + // find the error source for diagnostics. + // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/266. + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } } diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 84663ff884b4..68cbfa728058 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,7 +1,8 @@ use rustc_errors::{Applicability, Diag, MultiSpan, listify}; -use rustc_hir as hir; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::Res; use rustc_hir::intravisit::Visitor; +use rustc_hir::{self as hir, find_attr}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::bug; use rustc_middle::ty::adjustment::AllowTwoPhase; @@ -1081,19 +1082,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir_id, |m| { self.has_only_self_parameter(m) - && self - .tcx - // This special internal attribute is used to permit - // "identity-like" conversion methods to be suggested here. - // - // FIXME (#46459 and #46460): ideally - // `std::convert::Into::into` and `std::borrow:ToOwned` would - // also be `#[rustc_conversion_suggestion]`, if not for - // method-probing false-positives and -negatives (respectively). - // - // FIXME? Other potential candidate methods: `as_ref` and - // `as_mut`? - .has_attr(m.def_id, sym::rustc_conversion_suggestion) + // This special internal attribute is used to permit + // "identity-like" conversion methods to be suggested here. + // + // FIXME (#46459 and #46460): ideally + // `std::convert::Into::into` and `std::borrow:ToOwned` would + // also be `#[rustc_conversion_suggestion]`, if not for + // method-probing false-positives and -negatives (respectively). + // + // FIXME? Other potential candidate methods: `as_ref` and + // `as_mut`? + && find_attr!(self.tcx.get_all_attrs(m.def_id), AttributeKind::RustcConversionSuggestion) }, ); diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 0f330c3021c0..ad0467ddec96 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -590,6 +590,14 @@ pub(crate) struct IntToWide<'tcx> { )] pub expr_if_nightly: Option, pub known_wide: bool, + #[subdiagnostic] + pub param_note: Option, +} + +#[derive(Subdiagnostic)] +#[note("the type parameter `{$param}` is not known to be `Sized`, so this pointer may be wide")] +pub(crate) struct IntToWideParamNote { + pub param: Symbol, } #[derive(Subdiagnostic)] @@ -1145,7 +1153,7 @@ pub(crate) struct CastThinPointerToWidePointer<'tcx> { pub expr_ty: Ty<'tcx>, pub cast_ty: Ty<'tcx>, #[note( - "Thin pointers are \"simple\" pointers: they are purely a reference to a + "thin pointers are \"simple\" pointers: they are purely a reference to a memory address. Wide pointers are pointers referencing \"Dynamically Sized Types\" (also diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 727666de3c47..2e421c610e7a 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -7,6 +7,7 @@ use rustc_data_structures::graph::{self}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir as hir; use rustc_hir::HirId; +use rustc_hir::attrs::DivergingFallbackBehavior; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{InferKind, Visitor}; @@ -19,17 +20,6 @@ use tracing::debug; use crate::{FnCtxt, errors}; -#[derive(Copy, Clone)] -pub(crate) enum DivergingFallbackBehavior { - /// Always fallback to `()` (aka "always spontaneous decay") - ToUnit, - /// Always fallback to `!` (which should be equivalent to never falling back + not making - /// never-to-any coercions unless necessary) - ToNever, - /// Don't fallback at all - NoFallback, -} - impl<'tcx> FnCtxt<'_, 'tcx> { /// Performs type inference fallback, setting [`FnCtxt::diverging_fallback_has_occurred`] /// if the never type fallback has occurred. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 67007523a067..cfb798b31ea0 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -45,6 +45,38 @@ use crate::method::{self, MethodCallee}; use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Transform generic args for inherent associated type constants (IACs). + /// + /// IACs have a different generic parameter structure than regular associated constants: + /// - Regular assoc const: parent (impl) generic params + own generic params + /// - IAC (type_const): Self type + own generic params + pub(crate) fn transform_args_for_inherent_type_const( + &self, + def_id: DefId, + args: GenericArgsRef<'tcx>, + ) -> GenericArgsRef<'tcx> { + let tcx = self.tcx; + if !tcx.is_type_const(def_id) { + return args; + } + let Some(assoc_item) = tcx.opt_associated_item(def_id) else { + return args; + }; + if !matches!(assoc_item.container, ty::AssocContainer::InherentImpl) { + return args; + } + + let impl_def_id = assoc_item.container_id(tcx); + let generics = tcx.generics_of(def_id); + let impl_args = &args[..generics.parent_count]; + let self_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args); + // Build new args: [Self, own_args...] + let own_args = &args[generics.parent_count..]; + tcx.mk_args_from_iter( + std::iter::once(ty::GenericArg::from(self_ty)).chain(own_args.iter().copied()), + ) + } + /// Produces warning on the given node, if the current point in the /// function is unreachable, and there hasn't been another warning. pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) { @@ -1281,8 +1313,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }); + let args_for_user_type = if let Res::Def(DefKind::AssocConst, def_id) = res { + self.transform_args_for_inherent_type_const(def_id, args_raw) + } else { + args_raw + }; + // First, store the "user args" for later. - self.write_user_type_annotation_from_args(hir_id, def_id, args_raw, user_self_ty); + self.write_user_type_annotation_from_args(hir_id, def_id, args_for_user_type, user_self_ty); // Normalize only after registering type annotations. let args = self.normalize(span, args_raw); @@ -1322,6 +1360,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_instantiated); + + let args = if let Res::Def(DefKind::AssocConst, def_id) = res { + self.transform_args_for_inherent_type_const(def_id, args) + } else { + args + }; + self.write_args(hir_id, args); (ty_instantiated, res) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 2e85410c8960..54d8306936dd 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -5,6 +5,7 @@ use itertools::Itertools; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, listify, pluralize}; +use rustc_hir::attrs::DivergingBlockBehavior; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -47,29 +48,6 @@ rustc_index::newtype_index! { pub(crate) struct GenericIdx {} } -#[derive(Clone, Copy, Default)] -pub(crate) enum DivergingBlockBehavior { - /// This is the current stable behavior: - /// - /// ```rust - /// { - /// return; - /// } // block has type = !, even though we are supposedly dropping it with `;` - /// ``` - #[default] - Never, - - /// Alternative behavior: - /// - /// ```ignore (very-unstable-new-attribute) - /// #![rustc_never_type_options(diverging_block_default = "unit")] - /// { - /// return; - /// } // block has type = (), since we are dropping `!` from `return` with `;` - /// ``` - Unit, -} - impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { let mut deferred_cast_checks = self.root_ctxt.deferred_cast_checks.borrow_mut(); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index c875e2e50d70..412df9162e9f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -10,8 +10,9 @@ use std::ops::Deref; use hir::def_id::CRATE_DEF_ID; use rustc_errors::DiagCtxtHandle; +use rustc_hir::attrs::{AttributeKind, DivergingBlockBehavior, DivergingFallbackBehavior}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, HirId, ItemLocalMap}; +use rustc_hir::{self as hir, HirId, ItemLocalMap, find_attr}; use rustc_hir_analysis::hir_ty_lowering::{ HirTyLowerer, InherentAssocCandidate, RegionInferReason, }; @@ -19,15 +20,13 @@ use rustc_infer::infer::{self, RegionVariableOrigin}; use rustc_infer::traits::{DynCompatibilityViolation, Obligation}; use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; -use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym}; +use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span}; use rustc_trait_selection::error_reporting::TypeErrCtxt; use rustc_trait_selection::traits::{ self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, }; use crate::coercion::CoerceMany; -use crate::fallback::DivergingFallbackBehavior; -use crate::fn_ctxt::checks::DivergingBlockBehavior; use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt}; /// The `FnCtxt` stores type-checking context needed to type-check bodies of @@ -517,51 +516,5 @@ fn parse_never_type_options_attr( // Error handling is dubious here (unwraps), but that's probably fine for an internal attribute. // Just don't write incorrect attributes <3 - let mut fallback = None; - let mut block = None; - - let items = if tcx.features().rustc_attrs() { - tcx.get_attr(CRATE_DEF_ID, sym::rustc_never_type_options) - .map(|attr| attr.meta_item_list().unwrap()) - } else { - None - }; - let items = items.unwrap_or_default(); - - for item in items { - if item.has_name(sym::fallback) && fallback.is_none() { - let mode = item.value_str().unwrap(); - match mode { - sym::unit => fallback = Some(DivergingFallbackBehavior::ToUnit), - sym::never => fallback = Some(DivergingFallbackBehavior::ToNever), - sym::no => fallback = Some(DivergingFallbackBehavior::NoFallback), - _ => { - tcx.dcx().span_err(item.span(), format!("unknown never type fallback mode: `{mode}` (supported: `unit`, `niko`, `never` and `no`)")); - } - }; - continue; - } - - if item.has_name(sym::diverging_block_default) && block.is_none() { - let default = item.value_str().unwrap(); - match default { - sym::unit => block = Some(DivergingBlockBehavior::Unit), - sym::never => block = Some(DivergingBlockBehavior::Never), - _ => { - tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{default}` (supported: `unit` and `never`)")); - } - }; - continue; - } - - tcx.dcx().span_err( - item.span(), - format!( - "unknown or duplicate never type option: `{}` (supported: `fallback`, `diverging_block_default`)", - item.name().unwrap() - ), - ); - } - - (fallback, block) + find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcNeverTypeOptions {fallback, diverging_block_default} => (*fallback, *diverging_block_default)).unwrap_or_default() } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index ac3db41a4d66..8692720529d5 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -1,5 +1,5 @@ // tidy-alphabetical-start -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_intersperse)] diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index bf829c2d645d..746678e2865f 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -7,7 +7,7 @@ mod prelude_edition_lints; pub(crate) mod probe; mod suggest; -use rustc_errors::{Applicability, Diag, SubdiagMessage}; +use rustc_errors::{Applicability, Diag, DiagMessage}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::DefId; @@ -127,7 +127,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_method_call( &self, err: &mut Diag<'_>, - msg: impl Into + std::fmt::Debug, + msg: impl Into + std::fmt::Debug, method_name: Ident, self_ty: Ty<'tcx>, call_expr: &hir::Expr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index f054145dc7e9..13f87c092352 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -3,6 +3,7 @@ use std::collections::hash_map::Entry::{Occupied, Vacant}; use rustc_abi::FieldIdx; use rustc_ast as ast; +use rustc_data_structures::assert_matches; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{ @@ -24,7 +25,6 @@ use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; -use rustc_span::source_map::Spanned; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; @@ -611,7 +611,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_ty(*hir_id, ty); ty } - PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, &pat_info.top_info), + PatKind::Expr(expr @ PatExpr { kind: PatExprKind::Lit { lit, .. }, .. }) => { + self.check_pat_lit(pat.span, expr, &lit.node, expected, &pat_info.top_info) + } PatKind::Range(lhs, rhs, _) => { self.check_pat_range(pat.span, lhs, rhs, expected, &pat_info.top_info) } @@ -938,23 +940,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_lit( &self, span: Span, - lt: &hir::PatExpr<'tcx>, + expr: &hir::PatExpr<'tcx>, + lit_kind: &ast::LitKind, expected: Ty<'tcx>, ti: &TopInfo<'tcx>, ) -> Ty<'tcx> { + assert_matches!(expr.kind, hir::PatExprKind::Lit { .. }); + // We've already computed the type above (when checking for a non-ref pat), // so avoid computing it again. - let ty = self.node_ty(lt.hir_id); + let ty = self.node_ty(expr.hir_id); // Byte string patterns behave the same way as array patterns // They can denote both statically and dynamically-sized byte arrays. // Additionally, when `deref_patterns` is enabled, byte string literal patterns may have // types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec`. let mut pat_ty = ty; - if let hir::PatExprKind::Lit { - lit: Spanned { node: ast::LitKind::ByteStr(..), .. }, .. - } = lt.kind - { + if matches!(lit_kind, ast::LitKind::ByteStr(..)) { let tcx = self.tcx; let expected = self.structurally_resolve_type(span, expected); match *expected.kind() { @@ -962,7 +964,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Ref(_, inner_ty, _) if self.try_structurally_resolve_type(span, inner_ty).is_slice() => { - trace!(?lt.hir_id.local_id, "polymorphic byte string lit"); + trace!(?expr.hir_id.local_id, "polymorphic byte string lit"); pat_ty = Ty::new_imm_ref( tcx, tcx.lifetimes.re_static, @@ -988,9 +990,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // When `deref_patterns` is enabled, in order to allow `deref!("..."): String`, we allow // string literal patterns to have type `str`. This is accounted for when lowering to MIR. if self.tcx.features().deref_patterns() - && let hir::PatExprKind::Lit { - lit: Spanned { node: ast::LitKind::Str(..), .. }, .. - } = lt.kind + && matches!(lit_kind, ast::LitKind::Str(..)) && self.try_structurally_resolve_type(span, expected).is_str() { pat_ty = self.tcx.types.str_; diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 1a2b76485f35..767913ba5261 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -36,10 +36,10 @@ use rustc_abi::FIRST_VARIANT; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::{ExtendUnord, UnordSet}; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::HirId; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{self as hir, HirId, find_attr}; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind}; use rustc_middle::mir::FakeReadCause; use rustc_middle::traits::ObligationCauseCode; @@ -1743,7 +1743,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn should_log_capture_analysis(&self, closure_def_id: LocalDefId) -> bool { - self.has_rustc_attrs && self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) + self.has_rustc_attrs + && find_attr!( + self.tcx.get_all_attrs(closure_def_id), + AttributeKind::RustcCaptureAnalysis + ) } fn log_capture_analysis_first_pass( diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs index 9b33b49d4406..3354689d0ca3 100644 --- a/compiler/rustc_incremental/src/errors.rs +++ b/compiler/rustc_incremental/src/errors.rs @@ -281,7 +281,7 @@ pub(crate) struct DeleteWorkProduct<'a> { #[derive(Diagnostic)] #[diag( - "corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant." + "corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant" )] pub(crate) struct CorruptFile<'a> { pub path: &'a Path, diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index be16b543e824..996ae162607d 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -2,7 +2,7 @@ use std::fs; use std::sync::Arc; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::sync::join; +use rustc_data_structures::sync::par_join; use rustc_middle::dep_graph::{ DepGraph, SerializedDepGraph, WorkProduct, WorkProductId, WorkProductMap, }; @@ -44,7 +44,7 @@ pub(crate) fn save_dep_graph(tcx: TyCtxt<'_>) { sess.time("assert_dep_graph", || assert_dep_graph(tcx)); sess.time("check_clean", || clean::check_clean_annotations(tcx)); - join( + par_join( move || { sess.time("incr_comp_persist_dep_graph", || { if let Err(err) = fs::rename(&staging_dep_graph_path, &dep_graph_path) { diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 2a1733ef63cb..008b5c94a5ea 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -14,7 +14,7 @@ // tidy-alphabetical-start #![allow(rustc::direct_use_of_rustc_type_ir)] -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(extend_one)] #![recursion_limit = "512"] // For rustdoc // tidy-alphabetical-end diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 9fcc5908952d..6cbc184e8f1b 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -7,7 +7,6 @@ use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::jobserver::{self, Proxy}; use rustc_data_structures::stable_hasher::StableHasher; -use rustc_errors::registry::Registry; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; use rustc_lint::LintStore; use rustc_middle::ty; @@ -17,8 +16,7 @@ use rustc_parse::lexer::StripTokens; use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::Recovery; use rustc_parse::parser::attr::AllowLeadingUnsafe; -use rustc_query_impl::QueryCtxt; -use rustc_query_system::query::print_query_stack; +use rustc_query_impl::{QueryCtxt, print_query_stack}; use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName}; use rustc_session::parse::ParseSess; use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, lint}; @@ -54,10 +52,9 @@ pub struct Compiler { pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec) -> Cfg { cfgs.into_iter() .map(|s| { - let psess = ParseSess::emitter_with_note( - vec![rustc_parse::DEFAULT_LOCALE_RESOURCE], - format!("this occurred on the command line: `--cfg={s}`"), - ); + let psess = ParseSess::emitter_with_note(format!( + "this occurred on the command line: `--cfg={s}`" + )); let filename = FileName::cfg_spec_source_code(&s); macro_rules! error { @@ -126,10 +123,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() }; for s in specs { - let psess = ParseSess::emitter_with_note( - vec![rustc_parse::DEFAULT_LOCALE_RESOURCE], - format!("this occurred on the command line: `--check-cfg={s}`"), - ); + let psess = ParseSess::emitter_with_note(format!( + "this occurred on the command line: `--check-cfg={s}`" + )); let filename = FileName::cfg_spec_source_code(&s); const VISIT: &str = @@ -335,9 +331,6 @@ pub struct Config { /// bjorn3 for "hooking rust-analyzer's VFS into rustc at some point for /// running rustc without having to save". (See #102759.) pub file_loader: Option>, - /// The list of fluent resources, used for lints declared with - /// [`Diagnostic`](rustc_errors::Diagnostic) and [`LintDiagnostic`](rustc_errors::LintDiagnostic). - pub locale_resources: Vec<&'static str>, pub lint_caps: FxHashMap, @@ -374,9 +367,6 @@ pub struct Config { pub make_codegen_backend: Option Box + Send>>, - /// Registry of diagnostics codes. - pub registry: Registry, - /// The inner atomic value is set to true when a feature marked as `internal` is /// enabled. Makes it so that "please report a bug" is hidden, as ICEs with /// internal features are wontfix, and they are usually the cause of the ICEs. @@ -464,8 +454,6 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se temps_dir, }, bundle, - config.registry, - config.locale_resources, config.lint_caps, target, util::rustc_version_str().unwrap_or("unknown"), diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index f5fb8031ab0f..a2c11c608330 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -12,17 +12,17 @@ use rustc_codegen_ssa::{CodegenResults, CrateInfo}; use rustc_data_structures::indexmap::IndexMap; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal}; -use rustc_data_structures::{parallel, thousands}; +use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal, par_fns}; +use rustc_data_structures::thousands; use rustc_errors::timings::TimingSection; use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_feature::Features; use rustc_fs_util::try_canonicalize; -use rustc_hir::Attribute; use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; use rustc_hir::limit::Limit; +use rustc_hir::{Attribute, find_attr}; use rustc_incremental::setup_dep_graph; use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_store}; use rustc_metadata::EncodedMetadata; @@ -582,7 +582,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P let deps_output = outputs.path(OutputType::DepInfo); let deps_filename = deps_output.as_path(); - let result: io::Result<()> = try { + let result = try { // Build a list of files used to compile the output and // write Makefile-compatible dependency rules let mut files: IndexMap)> = sess @@ -1052,8 +1052,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { let sess = tcx.sess; sess.time("misc_checking_1", || { - parallel!( - { + par_fns(&mut [ + &mut || { sess.time("looking_for_entry_point", || tcx.ensure_ok().entry_fn(())); sess.time("check_externally_implementable_items", || { tcx.ensure_ok().check_externally_implementable_items(()) @@ -1065,7 +1065,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { CStore::from_tcx(tcx).report_unused_deps(tcx); }, - { + &mut || { tcx.ensure_ok().exportable_items(LOCAL_CRATE); tcx.ensure_ok().stable_order_of_exportable_impls(LOCAL_CRATE); tcx.par_hir_for_each_module(|module| { @@ -1073,14 +1073,14 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { tcx.ensure_ok().check_mod_unstable_api_usage(module); }); }, - { + &mut || { // We force these queries to run, // since they might not otherwise get called. // This marks the corresponding crate-level attributes // as used, and ensures that their values are valid. tcx.ensure_ok().limits(()); - } - ); + }, + ]); }); rustc_hir_analysis::check_crate(tcx); @@ -1116,18 +1116,14 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { { tcx.ensure_ok().mir_drops_elaborated_and_const_checked(def_id); } - if tcx.is_coroutine(def_id.to_def_id()) { - tcx.ensure_ok().mir_coroutine_witnesses(def_id); - let _ = tcx.ensure_ok().check_coroutine_obligations( - tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(), + if tcx.is_coroutine(def_id.to_def_id()) + && (!tcx.is_async_drop_in_place_coroutine(def_id.to_def_id())) + { + // Eagerly check the unsubstituted layout for cycles. + tcx.ensure_ok().layout_of( + ty::TypingEnv::post_analysis(tcx, def_id.to_def_id()) + .as_query_input(tcx.type_of(def_id).instantiate_identity()), ); - if !tcx.is_async_drop_in_place_coroutine(def_id.to_def_id()) { - // Eagerly check the unsubstituted layout for cycles. - tcx.ensure_ok().layout_of( - ty::TypingEnv::post_analysis(tcx, def_id.to_def_id()) - .as_query_input(tcx.type_of(def_id).instantiate_identity()), - ); - } } }); }); @@ -1156,39 +1152,39 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) { } sess.time("misc_checking_3", || { - parallel!( - { + par_fns(&mut [ + &mut || { tcx.ensure_ok().effective_visibilities(()); - parallel!( - { + par_fns(&mut [ + &mut || { tcx.par_hir_for_each_module(|module| { tcx.ensure_ok().check_private_in_public(module) }) }, - { + &mut || { tcx.par_hir_for_each_module(|module| { tcx.ensure_ok().check_mod_deathness(module) }); }, - { + &mut || { sess.time("lint_checking", || { rustc_lint::check_crate(tcx); }); }, - { + &mut || { tcx.ensure_ok().clashing_extern_declarations(()); - } - ); + }, + ]); }, - { + &mut || { sess.time("privacy_checking_modules", || { tcx.par_hir_for_each_module(|module| { tcx.ensure_ok().check_mod_privacy(module); }); }); - } - ); + }, + ]); // This check has to be run after all lints are done processing. We don't // define a lint filter, as all lint checks should have finished at this point. @@ -1227,7 +1223,7 @@ pub(crate) fn start_codegen<'tcx>( // Hook for tests. if let Some((def_id, _)) = tcx.entry_fn(()) - && tcx.has_attr(def_id, sym::rustc_delayed_bug_from_inside_query) + && find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcDelayedBugFromInsideQuery) { tcx.ensure_ok().trigger_delayed_bug(def_id); } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index b0272d726bc3..5ebf898f45a3 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -6,8 +6,8 @@ use std::sync::atomic::AtomicBool; use rustc_abi::Align; use rustc_data_structures::profiling::TimePassesFormat; +use rustc_errors::ColorConfig; use rustc_errors::emitter::HumanReadableErrorType; -use rustc_errors::{ColorConfig, registry}; use rustc_hir::attrs::{CollapseMacroDebuginfo, NativeLibKind}; use rustc_session::config::{ AnnotateMoves, AutoDiff, BranchProtection, CFGuard, Cfg, CoverageLevel, CoverageOptions, @@ -72,8 +72,6 @@ where sessopts, io, None, - registry::Registry::new(&[]), - vec![], Default::default(), target, "", diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 458e7ad32143..613b3c64efed 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -184,8 +184,7 @@ pub(crate) fn run_in_thread_pool_with_globals< use rustc_data_structures::defer; use rustc_data_structures::sync::FromDyn; use rustc_middle::ty::tls; - use rustc_query_impl::QueryCtxt; - use rustc_query_system::query::{QueryContext, break_query_cycles}; + use rustc_query_impl::{QueryCtxt, break_query_cycles}; let thread_stack_size = init_stack_size(thread_builder_diag); @@ -249,7 +248,7 @@ internal compiler error: query cycle handler thread panicked, aborting process"; tls::with(|tcx| { // Accessing session globals is sound as they outlive `GlobalCtxt`. // They are needed to hash query keys containing spans or symbols. - let query_map = rustc_span::set_session_globals_then( + let job_map = rustc_span::set_session_globals_then( unsafe { &*(session_globals as *const SessionGlobals) }, || { // Ensure there were no errors collecting all active jobs. @@ -259,7 +258,7 @@ internal compiler error: query cycle handler thread panicked, aborting process"; ) }, ); - break_query_cycles(query_map, ®istry); + break_query_cycles(job_map, ®istry); }) }) }); diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs deleted file mode 100644 index 165262b82c75..000000000000 --- a/compiler/rustc_lexer/src/cursor.rs +++ /dev/null @@ -1,125 +0,0 @@ -use std::str::Chars; - -pub enum FrontmatterAllowed { - Yes, - No, -} - -/// Peekable iterator over a char sequence. -/// -/// Next characters can be peeked via `first` method, -/// and position can be shifted forward via `bump` method. -pub struct Cursor<'a> { - len_remaining: usize, - /// Iterator over chars. Slightly faster than a &str. - chars: Chars<'a>, - pub(crate) frontmatter_allowed: FrontmatterAllowed, - #[cfg(debug_assertions)] - prev: char, -} - -pub(crate) const EOF_CHAR: char = '\0'; - -impl<'a> Cursor<'a> { - pub fn new(input: &'a str, frontmatter_allowed: FrontmatterAllowed) -> Cursor<'a> { - Cursor { - len_remaining: input.len(), - chars: input.chars(), - frontmatter_allowed, - #[cfg(debug_assertions)] - prev: EOF_CHAR, - } - } - - pub fn as_str(&self) -> &'a str { - self.chars.as_str() - } - - /// Returns the last eaten symbol (or `'\0'` in release builds). - /// (For debug assertions only.) - pub(crate) fn prev(&self) -> char { - #[cfg(debug_assertions)] - { - self.prev - } - - #[cfg(not(debug_assertions))] - { - EOF_CHAR - } - } - - /// Peeks the next symbol from the input stream without consuming it. - /// If requested position doesn't exist, `EOF_CHAR` is returned. - /// However, getting `EOF_CHAR` doesn't always mean actual end of file, - /// it should be checked with `is_eof` method. - pub fn first(&self) -> char { - // `.next()` optimizes better than `.nth(0)` - self.chars.clone().next().unwrap_or(EOF_CHAR) - } - - /// Peeks the second symbol from the input stream without consuming it. - pub(crate) fn second(&self) -> char { - // `.next()` optimizes better than `.nth(1)` - let mut iter = self.chars.clone(); - iter.next(); - iter.next().unwrap_or(EOF_CHAR) - } - - /// Peeks the third symbol from the input stream without consuming it. - pub fn third(&self) -> char { - // `.next()` optimizes better than `.nth(2)` - let mut iter = self.chars.clone(); - iter.next(); - iter.next(); - iter.next().unwrap_or(EOF_CHAR) - } - - /// Checks if there is nothing more to consume. - pub(crate) fn is_eof(&self) -> bool { - self.chars.as_str().is_empty() - } - - /// Returns amount of already consumed symbols. - pub(crate) fn pos_within_token(&self) -> u32 { - (self.len_remaining - self.chars.as_str().len()) as u32 - } - - /// Resets the number of bytes consumed to 0. - pub(crate) fn reset_pos_within_token(&mut self) { - self.len_remaining = self.chars.as_str().len(); - } - - /// Moves to the next character. - pub(crate) fn bump(&mut self) -> Option { - let c = self.chars.next()?; - - #[cfg(debug_assertions)] - { - self.prev = c; - } - - Some(c) - } - - /// Moves to a substring by a number of bytes. - pub(crate) fn bump_bytes(&mut self, n: usize) { - self.chars = self.as_str()[n..].chars(); - } - - /// Eats symbols while predicate returns true or until the end of file is reached. - pub(crate) fn eat_while(&mut self, mut predicate: impl FnMut(char) -> bool) { - // It was tried making optimized version of this for eg. line comments, but - // LLVM can inline all of this and compile it down to fast iteration over bytes. - while predicate(self.first()) && !self.is_eof() { - self.bump(); - } - } - - pub(crate) fn eat_until(&mut self, byte: u8) { - self.chars = match memchr::memchr(byte, self.as_str().as_bytes()) { - Some(index) => self.as_str()[index..].chars(), - None => "".chars(), - } - } -} diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 27ffcbc943bd..9d3da6ef4930 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -25,15 +25,13 @@ #![deny(unstable_features)] // tidy-alphabetical-end -mod cursor; - #[cfg(test)] mod tests; +use std::str::Chars; + use LiteralKind::*; use TokenKind::*; -use cursor::EOF_CHAR; -pub use cursor::{Cursor, FrontmatterAllowed}; pub use unicode_ident::UNICODE_VERSION; use unicode_properties::UnicodeEmoji; @@ -407,7 +405,129 @@ pub fn is_ident(string: &str) -> bool { } } -impl Cursor<'_> { +pub enum FrontmatterAllowed { + Yes, + No, +} + +/// Peekable iterator over a char sequence. +/// +/// Next characters can be peeked via `first` method, +/// and position can be shifted forward via `bump` method. +pub struct Cursor<'a> { + len_remaining: usize, + /// Iterator over chars. Slightly faster than a &str. + chars: Chars<'a>, + pub(crate) frontmatter_allowed: FrontmatterAllowed, + #[cfg(debug_assertions)] + prev: char, +} + +const EOF_CHAR: char = '\0'; + +impl<'a> Cursor<'a> { + pub fn new(input: &'a str, frontmatter_allowed: FrontmatterAllowed) -> Cursor<'a> { + Cursor { + len_remaining: input.len(), + chars: input.chars(), + frontmatter_allowed, + #[cfg(debug_assertions)] + prev: EOF_CHAR, + } + } + + pub fn as_str(&self) -> &'a str { + self.chars.as_str() + } + + /// Returns the last eaten symbol (or `'\0'` in release builds). + /// (For debug assertions only.) + pub(crate) fn prev(&self) -> char { + #[cfg(debug_assertions)] + { + self.prev + } + + #[cfg(not(debug_assertions))] + { + EOF_CHAR + } + } + + /// Peeks the next symbol from the input stream without consuming it. + /// If requested position doesn't exist, `EOF_CHAR` is returned. + /// However, getting `EOF_CHAR` doesn't always mean actual end of file, + /// it should be checked with `is_eof` method. + pub fn first(&self) -> char { + // `.next()` optimizes better than `.nth(0)` + self.chars.clone().next().unwrap_or(EOF_CHAR) + } + + /// Peeks the second symbol from the input stream without consuming it. + pub(crate) fn second(&self) -> char { + // `.next()` optimizes better than `.nth(1)` + let mut iter = self.chars.clone(); + iter.next(); + iter.next().unwrap_or(EOF_CHAR) + } + + /// Peeks the third symbol from the input stream without consuming it. + pub fn third(&self) -> char { + // `.next()` optimizes better than `.nth(2)` + let mut iter = self.chars.clone(); + iter.next(); + iter.next(); + iter.next().unwrap_or(EOF_CHAR) + } + + /// Checks if there is nothing more to consume. + pub(crate) fn is_eof(&self) -> bool { + self.chars.as_str().is_empty() + } + + /// Returns amount of already consumed symbols. + pub(crate) fn pos_within_token(&self) -> u32 { + (self.len_remaining - self.chars.as_str().len()) as u32 + } + + /// Resets the number of bytes consumed to 0. + pub(crate) fn reset_pos_within_token(&mut self) { + self.len_remaining = self.chars.as_str().len(); + } + + /// Moves to the next character. + pub(crate) fn bump(&mut self) -> Option { + let c = self.chars.next()?; + + #[cfg(debug_assertions)] + { + self.prev = c; + } + + Some(c) + } + + /// Moves to a substring by a number of bytes. + pub(crate) fn bump_bytes(&mut self, n: usize) { + self.chars = self.as_str()[n..].chars(); + } + + /// Eats symbols while predicate returns true or until the end of file is reached. + pub(crate) fn eat_while(&mut self, mut predicate: impl FnMut(char) -> bool) { + // It was tried making optimized version of this for eg. line comments, but + // LLVM can inline all of this and compile it down to fast iteration over bytes. + while predicate(self.first()) && !self.is_eof() { + self.bump(); + } + } + + pub(crate) fn eat_until(&mut self, byte: u8) { + self.chars = match memchr::memchr(byte, self.as_str().as_bytes()) { + Some(index) => self.as_str()[index..].chars(), + None => "".chars(), + } + } + /// Parses a token from the input string. pub fn advance_token(&mut self) -> Token { let Some(first_char) = self.bump() else { diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 035d8b4903fa..758d2762a6af 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -14,7 +14,6 @@ rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl deleted file mode 100644 index 2f5b7ed26952..000000000000 --- a/compiler/rustc_lint/messages.ftl +++ /dev/null @@ -1,1081 +0,0 @@ -lint_abs_path_with_module = absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition - .suggestion = use `crate` - -lint_ambiguous_glob_reexport = ambiguous glob re-exports - .label_first_reexport = the name `{$name}` in the {$namespace} namespace is first re-exported here - .label_duplicate_reexport = but the name `{$name}` in the {$namespace} namespace is also re-exported here - -lint_ambiguous_negative_literals = `-` has lower precedence than method calls, which might be unexpected - .example = e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4` - .negative_literal = add parentheses around the `-` and the literal to call the method on a negative literal - .current_behavior = add parentheses around the literal and the method call to keep the current behavior - -lint_ambiguous_wide_pointer_comparisons = ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - .addr_metadata_suggestion = use explicit `std::ptr::eq` method to compare metadata and addresses - .addr_suggestion = use `std::ptr::addr_eq` or untyped pointers to only compare their addresses - .cast_suggestion = use untyped pointers to only compare their addresses - .expect_suggestion = or expect the lint to compare the pointers metadata and addresses - -lint_associated_const_elided_lifetime = {$elided -> - [true] `&` without an explicit lifetime name cannot be used here - *[false] `'_` cannot be used here - } - .suggestion = use the `'static` lifetime - .note = cannot automatically infer `'static` because of other lifetimes in scope - -lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified - .note = you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future` - .suggestion = you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send`, but these cannot be relaxed without a breaking API change - -lint_atomic_ordering_fence = memory fences cannot have `Relaxed` ordering - .help = consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst` - -lint_atomic_ordering_invalid = `{$method}`'s failure ordering may not be `Release` or `AcqRel`, since a failed `{$method}` does not result in a write - .label = invalid failure ordering - .help = consider using `Acquire` or `Relaxed` failure ordering instead - -lint_atomic_ordering_load = atomic loads cannot have `Release` or `AcqRel` ordering - .help = consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -lint_atomic_ordering_store = atomic stores cannot have `Acquire` or `AcqRel` ordering - .help = consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -lint_attr_crate_level = - this attribute can only be applied at the crate level - .suggestion = to apply to the crate, use an inner attribute - .note = read for more information - -lint_bad_attribute_argument = bad attribute argument - -lint_bad_opt_access = {$msg} - -lint_break_with_label_and_loop = this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression - .suggestion = wrap this expression in parentheses - -lint_builtin_allow_internal_unsafe = - `allow_internal_unsafe` allows defining macros using unsafe without triggering the `unsafe_code` lint at their call site - -lint_builtin_anonymous_params = anonymous parameters are deprecated and will be removed in the next edition - .suggestion = try naming the parameter or explicitly ignoring it - -lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature - .previous_decl_label = `{$orig}` previously declared here - .mismatch_label = this signature doesn't match the previous declaration - -lint_builtin_clashing_extern_same_name = `{$this}` redeclared with a different signature - .previous_decl_label = `{$orig}` previously declared here - .mismatch_label = this signature doesn't match the previous declaration -lint_builtin_const_no_mangle = const items should never be `#[no_mangle]` - .suggestion = try a static value - -lint_builtin_decl_unsafe_fn = declaration of an `unsafe` function -lint_builtin_decl_unsafe_method = declaration of an `unsafe` method - -lint_builtin_deref_nullptr = dereferencing a null pointer - .label = this code causes undefined behavior when executed - -lint_builtin_double_negations = use of a double negation - .note = the prefix `--` could be misinterpreted as a decrement operator which exists in other languages - .note_decrement = use `-= 1` if you meant to decrement the value - .add_parens_suggestion = add parentheses for clarity - -lint_builtin_ellipsis_inclusive_range_patterns = `...` range patterns are deprecated - .suggestion = use `..=` for an inclusive range - -lint_builtin_explicit_outlives = outlives requirements can be inferred - .suggestion = remove {$count -> - [one] this bound - *[other] these bounds - } - -lint_builtin_export_name_fn = declaration of a function with `export_name` -lint_builtin_export_name_method = declaration of a method with `export_name` -lint_builtin_export_name_static = declaration of a static with `export_name` - -lint_builtin_global_asm = usage of `core::arch::global_asm` -lint_builtin_global_macro_unsafety = using this macro is unsafe even though it does not need an `unsafe` block - -lint_builtin_impl_unsafe_method = implementation of an `unsafe` method - -lint_builtin_incomplete_features = the feature `{$name}` is incomplete and may not be safe to use and/or cause compiler crashes - .note = see issue #{$n} for more information - .help = consider using `min_{$name}` instead, which is more stable and complete - -lint_builtin_internal_features = the feature `{$name}` is internal to the compiler or standard library - .note = using it is strongly discouraged - -lint_builtin_keyword_idents = `{$kw}` is a keyword in the {$next} edition - .suggestion = you can use a raw identifier to stay compatible - -lint_builtin_link_section_fn = declaration of a function with `link_section` - -lint_builtin_link_section_static = declaration of a static with `link_section` - -lint_builtin_missing_copy_impl = type could implement `Copy`; consider adding `impl Copy` - -lint_builtin_missing_debug_impl = - type does not implement `{$debug}`; consider adding `#[derive(Debug)]` or a manual implementation - -lint_builtin_missing_doc = missing documentation for {$article} {$desc} - -lint_builtin_mutable_transmutes = - transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell - -lint_builtin_no_mangle_fn = declaration of a `no_mangle` function -lint_builtin_no_mangle_generic = functions generic over types or consts must be mangled - .suggestion = remove this attribute - -lint_builtin_no_mangle_method = declaration of a `no_mangle` method -lint_builtin_no_mangle_static = declaration of a `no_mangle` static -lint_builtin_non_shorthand_field_patterns = the `{$ident}:` in this pattern is redundant - .suggestion = use shorthand field pattern - -lint_builtin_overridden_symbol_name = - the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them - -lint_builtin_overridden_symbol_section = - the program's behavior with overridden link sections on items is unpredictable and Rust cannot provide guarantees when you manually override them - -lint_builtin_special_module_name_used_lib = found module declaration for lib.rs - .note = lib.rs is the root of this crate's library target - .help = to refer to it from other targets, use the library's name as the path - -lint_builtin_special_module_name_used_main = found module declaration for main.rs - .note = a binary crate cannot be used as library - -lint_builtin_trivial_bounds = {$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters - -lint_builtin_type_alias_bounds_enable_feat_help = add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -lint_builtin_type_alias_bounds_label = will not be checked at usage sites of the type alias -lint_builtin_type_alias_bounds_limitation_note = this is a known limitation of the type checker that may be lifted in a future edition. - see issue #112792 for more information -lint_builtin_type_alias_bounds_param_bounds = bounds on generic parameters in type aliases are not enforced - .suggestion = remove {$count -> - [one] this bound - *[other] these bounds - } -lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg = fully qualify this associated type -lint_builtin_type_alias_bounds_where_clause = where clauses on type aliases are not enforced - .suggestion = remove this where clause - -lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed -lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done - -lint_builtin_unpermitted_type_init_uninit = the type `{$ty}` does not permit being left uninitialized - -lint_builtin_unpermitted_type_init_zeroed = the type `{$ty}` does not permit zero-initialization -lint_builtin_unreachable_pub = unreachable `pub` {$what} - .suggestion = consider restricting its visibility - .help = or consider exporting it for use by other crates - -lint_builtin_unsafe_block = usage of an `unsafe` block - -lint_builtin_unsafe_extern_block = usage of an `unsafe extern` block - -lint_builtin_unsafe_impl = implementation of an `unsafe` trait - -lint_builtin_unsafe_trait = declaration of an `unsafe` trait - -lint_builtin_unstable_features = use of an unstable feature - -lint_builtin_unused_doc_comment = unused doc comment - .label = rustdoc does not generate documentation for {$kind} - .plain_help = use `//` for a plain comment - .block_help = use `/* */` for a plain comment - -lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}` - .suggestion = use `loop` - -lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` - -lint_closure_returning_async_block = closure returning async block can be made into an async closure - .label = this async block can be removed, and the closure can be turned into an async closure - .suggestion = turn this into an async closure - -lint_command_line_source = `forbid` lint level was set on command line - -lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as identifiers, which look alike - .current_use = this identifier can be confused with `{$existing_sym}` - .other_use = other identifier used here - -lint_const_item_interior_mutations = - mutation of an interior mutable `const` item with call to `{$method_name}` - .label = `{$const_name}` is a interior mutable `const` item of type `{$const_ty}` - .temporary = each usage of a `const` item creates a new temporary - .never_original = only the temporaries and never the original `const {$const_name}` will be modified - .suggestion_static = for a shared instance of `{$const_name}`, consider making it a `static` item instead - .help = for more details on interior mutability see - -lint_dangling_pointers_from_locals = {$fn_kind} returns a dangling pointer to dropped local variable `{$local_var_name}` - .ret_ty = return type is `{$ret_ty}` - .local_var = local variable `{$local_var_name}` is dropped at the end of the {$fn_kind} - .created_at = dangling pointer created here - .note_safe = a dangling pointer is safe, but dereferencing one is undefined behavior - .note_more_info = for more information, see - -lint_dangling_pointers_from_temporaries = this creates a dangling pointer because temporary `{$ty}` is dropped at end of statement - .label_ptr = pointer created here - .label_temporary = this `{$ty}` is dropped at end of statement - .help_bind = bind the `{$ty}` to a variable such that it outlives the pointer returned by `{$callee}` - .note_safe = a dangling pointer is safe, but dereferencing one is undefined behavior - .note_return = returning a pointer to a local variable will always result in a dangling pointer - .note_more_info = for more information, see - - -lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance - .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary - -lint_default_source = `forbid` lint level is the default for {$id} - -lint_deprecated_lint_name = - lint name `{$name}` is deprecated and may not have an effect in the future - .suggestion = change it to - .help = change it to {$replace} - -lint_deprecated_where_clause_location = where clause not allowed here - .note = see issue #89122 for more information - .suggestion_move_to_end = move it to the end of the type declaration - .suggestion_remove_where = remove this `where` - -lint_doc_alias_duplicated = doc alias is duplicated - .label = first defined here - -lint_doc_auto_cfg_expects_hide_or_show = - only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]` - -lint_doc_auto_cfg_hide_show_expects_list = - `#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items - -lint_doc_auto_cfg_hide_show_unexpected_item = - `#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items - -lint_doc_auto_cfg_wrong_literal = - expected boolean for `#[doc(auto_cfg = ...)]` - -lint_doc_invalid = - invalid `doc` attribute - -lint_doc_test_literal = `#![doc(test(...)]` does not take a literal - -lint_doc_test_takes_list = - `#[doc(test(...)]` takes a list of attributes - -lint_doc_test_unknown = - unknown `doc(test)` attribute `{$name}` - -lint_doc_unknown_any = - unknown `doc` attribute `{$name}` - -lint_doc_unknown_include = - unknown `doc` attribute `include` - .suggestion = use `doc = include_str!` instead - -lint_doc_unknown_passes = - unknown `doc` attribute `{$name}` - .note = `doc` attribute `{$name}` no longer functions; see issue #44136 - .label = no longer functions - .no_op_note = `doc({$name})` is now a no-op - -lint_doc_unknown_plugins = - unknown `doc` attribute `plugins` - .note = `doc` attribute `plugins` no longer functions; see issue #44136 and CVE-2018-1000622 - .label = no longer functions - .no_op_note = `doc(plugins)` is now a no-op - -lint_doc_unknown_spotlight = - unknown `doc` attribute `spotlight` - .note = `doc(spotlight)` was renamed to `doc(notable_trait)` - .suggestion = use `notable_trait` instead - .no_op_note = `doc(spotlight)` is now a no-op - - -lint_drop_glue = - types that do not implement `Drop` can still have drop glue, consider instead using `{$needs_drop}` to detect whether a type is trivially dropped - -lint_drop_trait_constraints = - bounds on `{$predicate}` are most likely incorrect, consider instead using `{$needs_drop}` to detect whether a type can be trivially dropped - -lint_dropping_copy_types = calls to `std::mem::drop` with a value that implements `Copy` does nothing - .label = argument has type `{$arg_ty}` - -lint_dropping_references = calls to `std::mem::drop` with a reference instead of an owned value does nothing - .label = argument has type `{$arg_ty}` - -lint_empty_attribute = - unused attribute - .suggestion = {$valid_without_list -> - [true] remove these parentheses - *[other] remove this attribute - } - .note = {$valid_without_list -> - [true] using `{$attr_path}` with an empty list is equivalent to not using a list at all - *[other] using `{$attr_path}` with an empty list has no effect - } - --lint_previously_accepted = - this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -lint_enum_intrinsics_mem_discriminant = - the return value of `mem::discriminant` is unspecified when called with a non-enum type - .note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum - -lint_enum_intrinsics_mem_variant = - the return value of `mem::variant_count` is unspecified when called with a non-enum type - .note = the type parameter of `variant_count` should be an enum, but it was instantiated with the type `{$ty_param}`, which is not an enum - -lint_expectation = this lint expectation is unfulfilled - .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message - .rationale = {$rationale} - -lint_expected_name_value = - expected this to be of the form `... = "..."` - .warn = {-lint_previously_accepted} - -lint_expected_no_args = - didn't expect any arguments here - .warn = {-lint_previously_accepted} - -lint_for_loops_over_fallibles = - for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement - .suggestion = consider using `if let` to clear intent - .remove_next = to iterate over `{$recv_snip}` remove the call to `next` - .use_while_let = to check pattern in a loop use `while let` - .use_question_mark = consider unwrapping the `Result` with `?` to iterate over its contents - -lint_forgetting_copy_types = calls to `std::mem::forget` with a value that implements `Copy` does nothing - .label = argument has type `{$arg_ty}` - -lint_forgetting_references = calls to `std::mem::forget` with a reference instead of an owned value does nothing - .label = argument has type `{$arg_ty}` - -lint_function_casts_as_integer = direct cast of function item into an integer - .cast_as_fn = first cast to a pointer `as *const ()` - -lint_hidden_glob_reexport = private item shadows public glob re-export - .note_glob_reexport = the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here - .note_private_item = but the private item here shadows it - -lint_hidden_lifetime_parameters = hidden lifetime parameters in types are deprecated - -lint_identifier_non_ascii_char = identifier contains non-ASCII characters - -lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len -> - [one] { $identifier_type -> - [Exclusion] a character from an archaic script - [Technical] a character that is for non-linguistic, specialized usage - [Limited_Use] a character from a script in limited use - [Not_NFKC] a non normalized (NFKC) character - *[other] an uncommon character - } - *[other] { $identifier_type -> - [Exclusion] {$codepoints_len} characters from archaic scripts - [Technical] {$codepoints_len} characters that are for non-linguistic, specialized usage - [Limited_Use] {$codepoints_len} characters from scripts in limited use - [Not_NFKC] {$codepoints_len} non normalized (NFKC) characters - *[other] uncommon characters - } -}: {$codepoints} - .note = {$codepoints_len -> - [one] this character is - *[other] these characters are - } included in the{$identifier_type -> - [Restricted] {""} - *[other] {" "}{$identifier_type} - } Unicode general security profile - -lint_if_let_dtor = {$dtor_kind -> - [dyn] value may invoke a custom destructor because it contains a trait object - *[concrete] value invokes this custom destructor - } - -lint_if_let_rescope = `if let` assigns a shorter lifetime since Edition 2024 - .label = this value has a significant drop implementation which may observe a major change in drop order and requires your discretion - .help = the value is now dropped here in Edition 2024 - .suggestion = a `match` with a single arm can preserve the drop order up to Edition 2021 - -lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level - -lint_ill_formed_attribute_input = {$num_suggestions -> - [1] attribute must be of the form {$suggestions} - *[other] valid forms for the attribute are {$suggestions} - } - .note = for more information, visit <{$docs}> - -lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than possibly intended in edition 2024 - .note = specifically, {$num_captured -> - [one] this lifetime is - *[other] these lifetimes are - } in scope but not mentioned in the type's bounds - .note2 = all lifetimes in scope will be captured by `impl Trait`s in edition 2024 - -lint_impl_trait_redundant_captures = all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - .suggestion = remove the `use<...>` syntax - -lint_implicit_sysroot_crate_import = dangerous use of `extern crate {$name}` which is not guaranteed to exist exactly once in the sysroot - .help = try using a cargo dependency or using a re-export of the dependency provided by a rustc_* crate - -lint_implicit_unsafe_autorefs = implicit autoref creates a reference to the dereference of a raw pointer - .note = creating a reference requires the pointer target to be valid and imposes aliasing requirements - .raw_ptr = this raw pointer has type `{$raw_ptr_ty}` - .autoref = autoref is being applied to this expression, resulting in: `{$autoref_ty}` - .overloaded_deref = references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations - .method_def = method calls to `{$method_name}` require a reference - .suggestion = try using a raw pointer method instead; or if this reference is intentional, make it explicit - -lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe - .label = not FFI-safe - .note = the type is defined here - -lint_improper_ctypes_array_help = consider passing a pointer to the array - -lint_improper_ctypes_array_reason = passing raw arrays by value is not FFI-safe -lint_improper_ctypes_box = box cannot be represented as a single pointer - -lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead - -lint_improper_ctypes_char_reason = the `char` type has no C equivalent - -lint_improper_ctypes_cstr_help = - consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` -lint_improper_ctypes_cstr_reason = `CStr`/`CString` do not have a guaranteed layout - -lint_improper_ctypes_dyn = trait objects have no C equivalent - -lint_improper_ctypes_enum_repr_help = - consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum - -lint_improper_ctypes_enum_repr_reason = enum has no representation hint -lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead - -lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention -lint_improper_ctypes_non_exhaustive = this enum is non-exhaustive -lint_improper_ctypes_non_exhaustive_variant = this enum has non-exhaustive variants - -lint_improper_ctypes_only_phantomdata = composed only of `PhantomData` - -lint_improper_ctypes_opaque = opaque types have no C equivalent - -lint_improper_ctypes_slice_help = consider using a raw pointer instead - -lint_improper_ctypes_slice_reason = slices have no C equivalent -lint_improper_ctypes_str_help = consider using `*const u8` and a length instead - -lint_improper_ctypes_str_reason = string slices have no C equivalent -lint_improper_ctypes_struct_fieldless_help = consider adding a member to this struct - -lint_improper_ctypes_struct_fieldless_reason = this struct has no fields -lint_improper_ctypes_struct_layout_help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - -lint_improper_ctypes_struct_layout_reason = this struct has unspecified layout -lint_improper_ctypes_struct_non_exhaustive = this struct is non-exhaustive -lint_improper_ctypes_struct_zst = this struct contains only zero-sized fields - -lint_improper_ctypes_tuple_help = consider using a struct instead - -lint_improper_ctypes_tuple_reason = tuples have unspecified layout -lint_improper_ctypes_union_fieldless_help = consider adding a member to this union - -lint_improper_ctypes_union_fieldless_reason = this union has no fields -lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union - -lint_improper_ctypes_union_layout_reason = this union has unspecified layout -lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive - -lint_improper_ctypes_unsafe_binder = unsafe binders are incompatible with foreign function interfaces - -lint_improper_gpu_kernel_arg = passing type `{$ty}` to a function with "gpu-kernel" ABI may have unexpected behavior - .help = use primitive types and raw pointers to get reliable behavior - -lint_incorrect_do_not_recommend_args = - `#[diagnostic::do_not_recommend]` does not expect any arguments - -lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a pointer without provenance - .note = this is dangerous because dereferencing the resulting pointer is undefined behavior - .note_exposed_provenance = exposed provenance semantics can be used to create a pointer based on some previously exposed provenance - .help_transmute = for more information about transmute, see - .help_exposed_provenance = for more information about exposed provenance, see - .suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance - .suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` - -lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly - .label = use a different label that doesn't start with `0` or `1` - .help = start numbering with `2` instead - .note1 = an LLVM bug makes these labels ambiguous with a binary literal number on x86 - .note2 = see for more information - -lint_invalid_asm_label_format_arg = avoid using named labels in inline assembly - .help = only local labels of the form `:` should be used in inline asm - .note1 = format arguments may expand to a non-numeric value - .note2 = see the asm section of Rust By Example for more information -lint_invalid_asm_label_named = avoid using named labels in inline assembly - .help = only local labels of the form `:` should be used in inline asm - .note = see the asm section of Rust By Example for more information -lint_invalid_asm_label_no_span = the label may be declared in the expansion of a macro - -lint_invalid_crate_type_value = invalid `crate_type` value - .suggestion = did you mean - -# FIXME: we should ordinalize $valid_up_to when we add support for doing so -lint_invalid_from_utf8_checked = calls to `{$method}` with an invalid literal always return an error - .label = the literal was valid UTF-8 up to the {$valid_up_to} bytes - -# FIXME: we should ordinalize $valid_up_to when we add support for doing so -lint_invalid_from_utf8_unchecked = calls to `{$method}` with an invalid literal are undefined behavior - .label = the literal was valid UTF-8 up to the {$valid_up_to} bytes - -lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be directly compared to itself - .suggestion = use `f32::is_nan()` or `f64::is_nan()` instead - -lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable - -lint_invalid_null_arguments = calling this function with a null pointer is undefined behavior, even if the result of the function is unused - .origin = null pointer originates from here - .doc = for more information, visit and - -lint_invalid_reference_casting_assign_to_ref = assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - .label = casting happened here - -lint_invalid_reference_casting_bigger_layout = casting references to a bigger memory layout than the backing allocation is undefined behavior, even if the reference is unused - .label = casting happened here - .alloc = backing allocation comes from here - .layout = casting from `{$from_ty}` ({$from_size} bytes) to `{$to_ty}` ({$to_size} bytes) - -lint_invalid_reference_casting_borrow_as_mut = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - .label = casting happened here - -lint_invalid_reference_casting_note_book = for more information, visit - -lint_invalid_reference_casting_note_ty_has_interior_mutability = even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get` - -lint_invalid_style = {$is_used_as_inner -> - [false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]` - *[other] the `#![{$name}]` attribute can only be used at the crate root - } - .note = this attribute does not have an `!`, which means it is applied to this {$target} - -lint_invalid_target = `#[{$name}]` attribute cannot be used on {$target} - .warn = {-lint_previously_accepted} - .help = `#[{$name}]` can {$only}be applied to {$applied} - .suggestion = remove the attribute - -lint_lintpass_by_hand = implementing `LintPass` by hand - .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead - -lint_macro_expr_fragment_specifier_2024_migration = - the `expr` fragment specifier will accept more expressions in the 2024 edition - .suggestion = to keep the existing behavior, use the `expr_2021` fragment specifier - -lint_malformed_attribute = malformed lint attribute input - -lint_malformed_doc = - malformed `doc` attribute input - .warn = {-lint_previously_accepted} - -lint_map_unit_fn = `Iterator::map` call that discard the iterator's values - .note = `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated - .function_label = this function returns `()`, which is likely not what you wanted - .argument_label = called `Iterator::map` with callable that returns `()` - .map_label = after this call to map, the resulting iterator is `impl Iterator`, which means the only information carried by the iterator is the number of items - .suggestion = you might have meant to use `Iterator::for_each` - -lint_mismatched_lifetime_syntaxes_eliding_while_named = - eliding a lifetime that's named elsewhere is confusing - -lint_mismatched_lifetime_syntaxes_help = - the same lifetime is referred to in inconsistent ways, making the signature confusing - -lint_mismatched_lifetime_syntaxes_hiding_and_eliding_while_named = - hiding or eliding a lifetime that's named elsewhere is confusing - -lint_mismatched_lifetime_syntaxes_hiding_while_elided = - hiding a lifetime that's elided elsewhere is confusing - -lint_mismatched_lifetime_syntaxes_hiding_while_named = - hiding a lifetime that's named elsewhere is confusing - -lint_mismatched_lifetime_syntaxes_input_elided = - the lifetime is elided here - -lint_mismatched_lifetime_syntaxes_input_hidden = - the lifetime is hidden here - -lint_mismatched_lifetime_syntaxes_input_named = - the lifetime is named here - -lint_mismatched_lifetime_syntaxes_output_elided = - the same lifetime is elided here - -lint_mismatched_lifetime_syntaxes_output_hidden = - the same lifetime is hidden here - -lint_mismatched_lifetime_syntaxes_output_named = - the same lifetime is named here - -lint_mismatched_lifetime_syntaxes_suggestion_explicit = - consistently use `{$lifetime_name}` - -lint_mismatched_lifetime_syntaxes_suggestion_implicit = - remove the lifetime name from references - -lint_mismatched_lifetime_syntaxes_suggestion_mixed = - remove the lifetime name from references and use `'_` for type paths - -lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths = - use `'_` for type paths - -lint_missing_gpu_kernel_export_name = function with the "gpu-kernel" ABI has a mangled name - .note = mangled names make it hard to find the kernel, this is usually not intended - .help = use `unsafe(no_mangle)` or `unsafe(export_name = "")` - -lint_mixed_script_confusables = - the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables - .includes_note = the usage includes {$includes} - .note = please recheck to make sure their usages are indeed what you want - -lint_multiple_supertrait_upcastable = `{$ident}` is dyn-compatible and has multiple supertraits - -lint_named_argument_used_positionally = named argument `{$named_arg_name}` is not used by name - .label_named_arg = this named argument is referred to by position in formatting string - .label_position_arg = this formatting argument uses named argument `{$named_arg_name}` by position - .suggestion = use the named argument by name to avoid ambiguity - -lint_node_source = `forbid` level set here - .note = {$reason} - -lint_non_binding_let_multi_drop_fn = - consider immediately dropping the value using `drop(..)` after the `let` statement - -lint_non_binding_let_multi_suggestion = - consider immediately dropping the value - -lint_non_binding_let_on_drop_type = - non-binding let on a type that has a destructor - -lint_non_binding_let_on_sync_lock = non-binding let on a synchronization lock - .label = this lock is not assigned to a binding and is immediately dropped - -lint_non_binding_let_suggestion = - consider binding to an unused variable to avoid immediately dropping the value - -lint_non_camel_case_type = {$sort} `{$name}` should have an upper camel case name - .suggestion = convert the identifier to upper camel case - .label = should have an UpperCamelCase name - -lint_non_fmt_panic = panic message is not a string literal - .note = this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021 - .more_info_note = for more information, see - .supports_fmt_note = the `{$name}!()` macro supports formatting, so there's no need for the `format!()` macro here - .supports_fmt_suggestion = remove the `format!(..)` macro call - .display_suggestion = add a "{"{"}{"}"}" format string to `Display` the message - .debug_suggestion = - add a "{"{"}:?{"}"}" format string to use the `Debug` implementation of `{$ty}` - .panic_suggestion = {$already_suggested -> - [true] or use - *[false] use - } std::panic::panic_any instead - -lint_non_fmt_panic_braces = - panic message contains {$count -> - [one] a brace - *[other] braces - } - .note = this message is not used as a format string, but will be in Rust 2021 - .suggestion = add a "{"{"}{"}"}" format string to use the message literally - -lint_non_fmt_panic_unused = - panic message contains {$count -> - [one] an unused - *[other] unused - } formatting {$count -> - [one] placeholder - *[other] placeholders - } - .note = this message is not used as a format string when given without arguments, but will be in Rust 2021 - .add_args_suggestion = add the missing {$count -> - [one] argument - *[other] arguments - } - .add_fmt_suggestion = or add a "{"{"}{"}"}" format string to use the message literally - -lint_non_glob_import_type_ir_inherent = non-glob import of `rustc_type_ir::inherent` - .suggestion = try using a glob import instead - -lint_non_local_definitions_cargo_update = the {$macro_kind} `{$macro_name}` may come from an old version of the `{$crate_name}` crate, try updating your dependency with `cargo update -p {$crate_name}` - -lint_non_local_definitions_impl = non-local `impl` definition, `impl` blocks should be written at the same level as their item - .non_local = an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` - .doctest = make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}` - .exception = items in an anonymous const item (`const _: () = {"{"} ... {"}"}`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint - .const_anon = use a const-anon item to suppress this lint - .macro_to_change = the {$macro_kind} `{$macro_to_change}` defines the non-local `impl`, and may need to be changed - -lint_non_local_definitions_impl_move_help = - move the `impl` block outside of this {$body_kind_descr} {$depth -> - [one] `{$body_name}` - *[other] `{$body_name}` and up {$depth} bodies - } - -lint_non_local_definitions_macro_rules = non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module - .help = - remove the `#[macro_export]` or move this `macro_rules!` outside the of the current {$body_kind_descr} {$depth -> - [one] `{$body_name}` - *[other] `{$body_name}` and up {$depth} bodies - } - .help_doctest = - remove the `#[macro_export]` or make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}` - .non_local = a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute - -lint_non_snake_case = {$sort} `{$name}` should have a snake case name - .rename_or_convert_suggestion = rename the identifier or convert it to a snake case raw identifier - .cannot_convert_note = `{$sc}` cannot be used as a raw identifier - .rename_suggestion = rename the identifier - .convert_suggestion = convert the identifier to snake case - .help = convert the identifier to snake case: `{$sc}` - .label = should have a snake_case name - -lint_non_upper_case_global = {$sort} `{$name}` should have an upper case name - .suggestion = convert the identifier to upper case - .label = should have an UPPER_CASE name - -lint_noop_method_call = call to `.{$method}()` on a reference in this situation does nothing - .suggestion = remove this redundant call - .note = the type `{$orig_ty}` does not implement `{$trait_}`, so calling `{$method}` on `&{$orig_ty}` copies the reference, which does not do anything and can be removed - .derive_suggestion = if you meant to clone `{$orig_ty}`, implement `Clone` for it - -lint_only_cast_u8_to_char = only `u8` can be cast into `char` - .suggestion = use a `char` literal instead - -lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its associated type bounds - .specifically = this associated type bound is unsatisfied for `{$proj_ty}` - -lint_opaque_hidden_inferred_bound_sugg = add this bound - -lint_overflowing_bin_hex = literal out of range for `{$ty}` - .negative_note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` - .negative_becomes_note = and the value `-{$lit}` will become `{$actually}{$ty}` - .positive_note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` and will become `{$actually}{$ty}` - .suggestion = consider using the type `{$suggestion_ty}` instead - .sign_bit_suggestion = to use as a negative number (decimal `{$negative_val}`), consider using the type `{$uint_ty}` for the literal and cast it to `{$int_ty}` - .help = consider using the type `{$suggestion_ty}` instead - -lint_overflowing_int = literal out of range for `{$ty}` - .note = the literal `{$lit}` does not fit into the type `{$ty}` whose range is `{$min}..={$max}` - .help = consider using the type `{$suggestion_ty}` instead - -lint_overflowing_literal = literal out of range for `{$ty}` - .note = the literal `{$lit}` does not fit into the type `{$ty}` and will be converted to `{$ty}::INFINITY` - -lint_overflowing_uint = literal out of range for `{$ty}` - .note = the literal `{$lit}` does not fit into the type `{$ty}` whose range is `{$min}..={$max}` - -lint_overruled_attribute = {$lint_level}({$lint_source}) incompatible with previous forbid - .label = overruled by previous forbid - -lint_pass_by_value = passing `{$ty}` by reference - .suggestion = try passing by value - -lint_path_statement_drop = path statement drops value - .suggestion = use `drop` to clarify the intent - -lint_path_statement_no_effect = path statement with no effect - -lint_pattern_in_bodiless = patterns aren't allowed in functions without bodies - .label = pattern not allowed in function without body - -lint_pattern_in_foreign = patterns aren't allowed in foreign function declarations - .label = pattern not allowed in foreign function - -lint_query_instability = using `{$query}` can result in unstable query results - .note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale - -lint_query_untracked = `{$method}` accesses information that is not tracked by the query system - .note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale - -lint_range_endpoint_out_of_range = range endpoint is out of range for `{$ty}` - -lint_range_use_inclusive_range = use an inclusive range instead - - -lint_raw_prefix = prefix `'r` is reserved - .label = reserved prefix - .suggestion = insert whitespace here to avoid this being parsed as a prefix in Rust 2021 - -lint_reason_must_be_string_literal = reason must be a string literal - -lint_reason_must_come_last = reason in lint attribute must come last - -lint_redundant_import = the item `{$ident}` is imported redundantly - .label_imported_here = the item `{$ident}` is already imported here - .label_defined_here = the item `{$ident}` is already defined here - .label_imported_prelude = the item `{$ident}` is already imported by the extern prelude - .label_defined_prelude = the item `{$ident}` is already defined by the extern prelude - -lint_redundant_semicolons = - unnecessary trailing {$multiple -> - [true] semicolons - *[false] semicolon - } - -lint_redundant_semicolons_suggestion = remove {$multiple_semicolons -> - [true] these semicolons - *[false] this semicolon - } - -lint_remove_mut_from_pattern = remove `mut` from the parameter - -lint_removed_lint = lint `{$name}` has been removed: {$reason} - -lint_renamed_lint = lint `{$name}` has been renamed to `{$replace}` - .suggestion = use the new name - .help = use the new name `{$replace}` - -lint_requested_level = requested on the command line with `{$level} {$lint_name}` - -lint_reserved_multihash = reserved token in Rust 2024 - .suggestion = insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 - -lint_reserved_prefix = prefix `{$prefix}` is unknown - .label = unknown prefix - .suggestion = insert whitespace here to avoid this being parsed as a prefix in Rust 2021 - -lint_reserved_string = will be parsed as a guarded string in Rust 2024 - .suggestion = insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 - -lint_shadowed_into_iter = - this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<{$target} as IntoIterator>::into_iter` in Rust {$edition} - .use_iter_suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity - .remove_into_iter_suggestion = or remove `.into_iter()` to iterate by value - .use_explicit_into_iter_suggestion = - or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value - -lint_single_use_lifetime = lifetime parameter `{$ident}` only used once - .label_param = this lifetime... - .label_use = ...is used only here - .suggestion = elide the single-use lifetime - -lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` - -lint_static_mut_refs_lint = creating a {$shared_label}reference to mutable static - .label = {$shared_label}reference to mutable static - .suggestion = use `&raw const` instead to create a raw pointer - .suggestion_mut = use `&raw mut` instead to create a raw pointer - .shared_note = shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - .mut_note = mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives - -lint_supertrait_as_deref_target = this `Deref` implementation is covered by an implicit supertrait coercion - .label = `{$self_ty}` implements `Deref` which conflicts with supertrait `{$supertrait_principal}` - .label2 = target type is a supertrait of `{$self_ty}` - .help = consider removing this implementation or replacing it with a method instead - -lint_surrogate_char_cast = surrogate values are not valid for `char` - .note = `0xD800..=0xDFFF` are reserved for Unicode surrogates and are not valid `char` values - -lint_suspicious_double_ref_clone = - using `.clone()` on a double reference, which returns `{$ty}` instead of cloning the inner type - -lint_suspicious_double_ref_deref = - using `.deref()` on a double reference, which returns `{$ty}` instead of dereferencing the inner type - -lint_symbol_intern_string_literal = using `Symbol::intern` on a string literal - .help = consider adding the symbol to `compiler/rustc_span/src/symbol.rs` - -lint_too_large_char_cast = value exceeds maximum `char` value - .note = maximum valid `char` value is `0x10FFFF` - -lint_ty_qualified = usage of qualified `ty::{$ty}` - .suggestion = try importing it and using it unqualified - -lint_tykind = usage of `ty::TyKind` - .help = try using `Ty` instead - -lint_tykind_kind = usage of `ty::TyKind::` - .suggestion = try using `ty::` directly - -lint_type_ir_direct_use = do not use `rustc_type_ir` unless you are implementing type system internals - .note = use `rustc_middle::ty` instead - -lint_type_ir_inherent_usage = do not use `rustc_type_ir::inherent` unless you're inside of the trait solver - .note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler - -lint_type_ir_trait_usage = do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver - .note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler - -lint_undefined_transmute = pointers cannot be transmuted to integers during const eval - .note = at compile-time, pointers do not have an integer value - .note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior - .help = for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html - -lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing - .label = argument has type `{$arg_ty}` - .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value - -lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_println}` to the top of the `build.rs` -lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead -lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg} -lint_unexpected_cfg_add_cmdline_arg = to expect this configuration use `{$cmdline_arg}` -lint_unexpected_cfg_boolean = you may have meant to use `{$literal}` (notice the capitalization). Doing so makes this predicate evaluate to `{$literal}` unconditionally -lint_unexpected_cfg_cargo_update = the {$macro_kind} `{$macro_name}` may come from an old version of the `{$crate_name}` crate, try updating your dependency with `cargo update -p {$crate_name}` - -lint_unexpected_cfg_define_features = consider defining some features in `Cargo.toml` -lint_unexpected_cfg_doc_cargo = see for more information about checking conditional configuration -lint_unexpected_cfg_doc_rustc = see for more information about checking conditional configuration - -lint_unexpected_cfg_from_external_macro_origin = using a cfg inside a {$macro_kind} will use the cfgs from the destination crate and not the ones from the defining crate -lint_unexpected_cfg_from_external_macro_refer = try referring to `{$macro_name}` crate for guidance on how handle this unexpected cfg -lint_unexpected_cfg_name = unexpected `cfg` condition name: `{$name}` -lint_unexpected_cfg_name_expected_names = expected names are: {$possibilities}{$and_more -> - [0] {""} - *[other] {" "}and {$and_more} more - } -lint_unexpected_cfg_name_expected_values = expected values for `{$best_match}` are: {$possibilities} -lint_unexpected_cfg_name_similar_name = there is a config with a similar name -lint_unexpected_cfg_name_similar_name_different_values = there is a config with a similar name and different values -lint_unexpected_cfg_name_similar_name_no_value = there is a config with a similar name and no value -lint_unexpected_cfg_name_similar_name_value = there is a config with a similar name and value -lint_unexpected_cfg_name_version_syntax = there is a similar config predicate: `version("..")` -lint_unexpected_cfg_name_with_similar_value = found config with similar value - -lint_unexpected_cfg_value = unexpected `cfg` condition value: {$has_value -> - [true] `{$value}` - *[false] (none) - } -lint_unexpected_cfg_value_add_feature = consider adding `{$value}` as a feature in `Cargo.toml` -lint_unexpected_cfg_value_expected_values = expected values for `{$name}` are: {$have_none_possibility -> - [true] {"(none), "} - *[false] {""} - }{$possibilities}{$and_more -> - [0] {""} - *[other] {" "}and {$and_more} more - } -lint_unexpected_cfg_value_no_expected_value = no expected value for `{$name}` -lint_unexpected_cfg_value_no_expected_values = no expected values for `{$name}` -lint_unexpected_cfg_value_remove_condition = remove the condition -lint_unexpected_cfg_value_remove_value = remove the value -lint_unexpected_cfg_value_similar_name = there is a expected value with a similar name -lint_unexpected_cfg_value_specify_value = specify a config value - -lint_ungated_async_fn_track_caller = `#[track_caller]` on async functions is a no-op - .label = this function will not propagate the caller location - -lint_unicode_text_flow = unicode codepoint changing visible direction of text present in comment - .label = {$num_codepoints -> - [1] this comment contains an invisible unicode text flow control codepoint - *[other] this comment contains invisible unicode text flow control codepoints - } - .note = these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen - .suggestion = if their presence wasn't intentional, you can remove them - .label_comment_char = {$c_debug} - - -lint_unit_bindings = binding has unit type `()` - .label = this pattern is inferred to be the unit type `()` - -lint_unknown_gated_lint = - unknown lint: `{$name}` - .note = the `{$name}` lint is unstable - -lint_unknown_lint = - unknown lint: `{$name}` - .suggestion = {$from_rustc -> - [true] a lint with a similar name exists in `rustc` lints - *[false] did you mean - } - .help = {$from_rustc -> - [true] a lint with a similar name exists in `rustc` lints: `{$replace}` - *[false] did you mean: `{$replace}` - } - -lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}` - .help = add `#![register_tool({$tool_name})]` to the crate root - -lint_unnecessary_qualification = unnecessary qualification - .suggestion = remove the unnecessary path segments - -lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique - .note_duplicated_fn = the address of the same function can vary between different codegen units - .note_deduplicated_fn = furthermore, different functions could have the same address after being merged together - .note_visit_fn_addr_eq = for more information visit - .fn_addr_eq_suggestion = refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint - -lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::` - -lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe - .label = usage of unsafe attribute -lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` - -lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´ - -lint_unused_allocation = unnecessary allocation, use `&` instead -lint_unused_allocation_mut = unnecessary allocation, use `&mut` instead - -lint_unused_closure = - unused {$pre}{$count -> - [one] closure - *[other] closures - }{$post} that must be used - .note = closures are lazy and do nothing unless called - -lint_unused_comparisons = comparison is useless due to type limits - -lint_unused_coroutine = - unused {$pre}{$count -> - [one] coroutine - *[other] coroutine - }{$post} that must be used - .note = coroutines are lazy and do nothing unless resumed - -lint_unused_crate_dependency = extern crate `{$extern_crate}` is unused in crate `{$local_crate}` - .help = remove the dependency or add `use {$extern_crate} as _;` to the crate root - -lint_unused_def = unused {$pre}`{$def}`{$post} that must be used - .suggestion = use `let _ = ...` to ignore the resulting value - -lint_unused_delim = unnecessary {$delim} around {$item} - .suggestion = remove these {$delim} - -lint_unused_duplicate = - unused attribute - .suggestion = remove this attribute - .note = attribute also specified here - .warn = {-lint_previously_accepted} - -lint_unused_import_braces = braces around {$node} is unnecessary - -lint_unused_imports = {$num_snippets -> - [one] unused import: {$span_snippets} - *[other] unused imports: {$span_snippets} - } - .suggestion_remove_whole_use = remove the whole `use` item - .suggestion_remove_imports = {$num_to_remove -> - [one] remove the unused import - *[other] remove the unused imports - } - .help = if this is a test module, consider adding a `#[cfg(test)]` to the containing module - -lint_unused_lifetime = lifetime parameter `{$ident}` never used - .suggestion = elide the unused lifetime - -lint_unused_op = unused {$op} that must be used - .label = the {$op} produces a value - .suggestion = use `let _ = ...` to ignore the resulting value - -lint_unused_result = unused result of type `{$ty}` - -lint_unused_visibilities = visibility qualifiers have no effect on `const _` declarations - .note = `const _` does not declare a name, so there is nothing for the qualifier to apply to - .suggestion = remove the qualifier - -lint_use_let_underscore_ignore_suggestion = use `let _ = ...` to ignore the expression or result - -lint_useless_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false - .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value - .label = expression has type `{$orig_ty}` - -lint_useless_ptr_null_checks_fn_ret = returned pointer of `{$fn_name}` call is never null, so checking it for null will always return false - -lint_useless_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false - .label = expression has type `{$orig_ty}` - -lint_uses_power_alignment = repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - -lint_variant_size_differences = - enum variant is more than three times larger ({$largest} bytes) than the next largest diff --git a/compiler/rustc_lint/src/async_closures.rs b/compiler/rustc_lint/src/async_closures.rs index 02fb22bf782e..0d3a954667f7 100644 --- a/compiler/rustc_lint/src/async_closures.rs +++ b/compiler/rustc_lint/src/async_closures.rs @@ -108,16 +108,18 @@ impl<'tcx> LateLintPass<'tcx> for AsyncClosureUsage { } #[derive(LintDiagnostic)] -#[diag(lint_closure_returning_async_block)] +#[diag("closure returning async block can be made into an async closure")] struct ClosureReturningAsyncBlock { - #[label] + #[label( + "this async block can be removed, and the closure can be turned into an async closure" + )] async_decl_span: Span, #[subdiagnostic] sugg: AsyncClosureSugg, } #[derive(Subdiagnostic)] -#[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")] +#[multipart_suggestion("turn this into an async closure", applicability = "maybe-incorrect")] struct AsyncClosureSugg { #[suggestion_part(code = "")] deletion_span: Span, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index e2a061cab680..ef21b3f573be 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -22,7 +22,7 @@ use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust::expr_to_string; use rustc_attr_parsing::AttributeParser; -use rustc_errors::{Applicability, LintDiagnostic}; +use rustc_errors::{Applicability, LintDiagnostic, inline_fluent}; use rustc_feature::GateIssue; use rustc_hir as hir; use rustc_hir::attrs::{AttributeKind, DocAttribute}; @@ -61,10 +61,7 @@ use crate::lints::{ BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, }; -use crate::{ - EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, - fluent_generated as fluent, -}; +use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext}; declare_lint! { /// The `while_true` lint detects `while true { }`. /// @@ -2655,8 +2652,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { let conjured_ty = cx.typeck_results().expr_ty(expr); if let Some(err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) { let msg = match init { - InitKind::Zeroed => fluent::lint_builtin_unpermitted_type_init_zeroed, - InitKind::Uninit => fluent::lint_builtin_unpermitted_type_init_uninit, + InitKind::Zeroed => { + inline_fluent!("the type `{$ty}` does not permit zero-initialization") + } + InitKind::Uninit => { + inline_fluent!("the type `{$ty}` does not permit being left uninitialized") + } }; let sub = BuiltinUnpermittedTypeInitSub { err }; cx.emit_span_lint( diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index c0ab0d1f0040..3da2b1bf4069 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -293,6 +293,14 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } + BuiltinLintDiag::UnreachableCfg { span, wildcard_span } => match wildcard_span { + Some(wildcard_span) => { + lints::UnreachableCfgSelectPredicateWildcard { span, wildcard_span } + .decorate_lint(diag) + } + None => lints::UnreachableCfgSelectPredicate { span }.decorate_lint(diag), + }, + BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) } @@ -375,6 +383,10 @@ pub fn decorate_attribute_lint( lints::DocAutoCfgExpectsHideOrShow.decorate_lint(diag) } + &AttributeLintKind::AmbiguousDeriveHelpers => { + lints::AmbiguousDeriveHelpers.decorate_lint(diag) + } + &AttributeLintKind::DocAutoCfgHideShowUnexpectedItem { attr_name } => { lints::DocAutoCfgHideShowUnexpectedItem { attr_name }.decorate_lint(diag) } diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index 8f2f3594161a..b0154bed1a51 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -1,17 +1,15 @@ use rustc_errors::codes::*; -use rustc_errors::{Diag, EmissionGuarantee, Subdiagnostic}; +use rustc_errors::{Diag, EmissionGuarantee, Subdiagnostic, inline_fluent}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::lint::Level; use rustc_span::{Span, Symbol}; -use crate::fluent_generated as fluent; - #[derive(Diagnostic)] -#[diag(lint_overruled_attribute, code = E0453)] +#[diag("{$lint_level}({$lint_source}) incompatible with previous forbid", code = E0453)] pub(crate) struct OverruledAttribute<'a> { #[primary_span] pub span: Span, - #[label] + #[label("overruled by previous forbid")] pub overruled: Span, pub lint_level: &'a str, pub lint_source: Symbol, @@ -29,24 +27,24 @@ impl Subdiagnostic for OverruledAttributeSub { fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { OverruledAttributeSub::DefaultSource { id } => { - diag.note(fluent::lint_default_source); + diag.note(inline_fluent!("`forbid` lint level is the default for {$id}")); diag.arg("id", id); } OverruledAttributeSub::NodeSource { span, reason } => { - diag.span_label(span, fluent::lint_node_source); + diag.span_label(span, inline_fluent!("`forbid` level set here")); if let Some(rationale) = reason { diag.note(rationale.to_string()); } } OverruledAttributeSub::CommandLineSource => { - diag.note(fluent::lint_command_line_source); + diag.note(inline_fluent!("`forbid` lint level was set on command line")); } } } } #[derive(Diagnostic)] -#[diag(lint_malformed_attribute, code = E0452)] +#[diag("malformed lint attribute input", code = E0452)] pub(crate) struct MalformedAttribute { #[primary_span] pub span: Span, @@ -56,50 +54,55 @@ pub(crate) struct MalformedAttribute { #[derive(Subdiagnostic)] pub(crate) enum MalformedAttributeSub { - #[label(lint_bad_attribute_argument)] + #[label("bad attribute argument")] BadAttributeArgument(#[primary_span] Span), - #[label(lint_reason_must_be_string_literal)] + #[label("reason must be a string literal")] ReasonMustBeStringLiteral(#[primary_span] Span), - #[label(lint_reason_must_come_last)] + #[label("reason in lint attribute must come last")] ReasonMustComeLast(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(lint_unknown_tool_in_scoped_lint, code = E0710)] +#[diag("unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`", code = E0710)] pub(crate) struct UnknownToolInScopedLint { #[primary_span] pub span: Option, pub tool_name: Symbol, pub lint_name: String, - #[help] + #[help("add `#![register_tool({$tool_name})]` to the crate root")] pub is_nightly_build: bool, } #[derive(Diagnostic)] -#[diag(lint_builtin_ellipsis_inclusive_range_patterns, code = E0783)] +#[diag("`...` range patterns are deprecated", code = E0783)] pub(crate) struct BuiltinEllipsisInclusiveRangePatterns { #[primary_span] pub span: Span, - #[suggestion(style = "short", code = "{replace}", applicability = "machine-applicable")] + #[suggestion( + "use `..=` for an inclusive range", + style = "short", + code = "{replace}", + applicability = "machine-applicable" + )] pub suggestion: Span, pub replace: String, } #[derive(Subdiagnostic)] -#[note(lint_requested_level)] +#[note("requested on the command line with `{$level} {$lint_name}`")] pub(crate) struct RequestedLevel<'a> { pub level: Level, pub lint_name: &'a str, } #[derive(Diagnostic)] -#[diag(lint_unsupported_group, code = E0602)] +#[diag("`{$lint_group}` lint group is not supported with ´--force-warn´", code = E0602)] pub(crate) struct UnsupportedGroup { pub lint_group: String, } #[derive(Diagnostic)] -#[diag(lint_check_name_unknown_tool, code = E0602)] +#[diag("unknown lint tool: `{$tool_name}`", code = E0602)] pub(crate) struct CheckNameUnknownTool<'a> { pub tool_name: Symbol, #[subdiagnostic] diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 612d542a27de..8569070a43ca 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -3,7 +3,9 @@ use std::ops::ControlFlow; use hir::intravisit::{self, Visitor}; use rustc_ast::Recovered; -use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic, SuggestionStyle}; +use rustc_errors::{ + Applicability, Diag, EmissionGuarantee, Subdiagnostic, SuggestionStyle, inline_fluent, +}; use rustc_hir::{self as hir, HirIdSet}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::adjustment::Adjust; @@ -303,13 +305,15 @@ impl<'tcx> LateLintPass<'tcx> for IfLetRescope { } #[derive(LintDiagnostic)] -#[diag(lint_if_let_rescope)] +#[diag("`if let` assigns a shorter lifetime since Edition 2024")] struct IfLetRescopeLint { #[subdiagnostic] destructors: Vec, - #[label] + #[label( + "this value has a significant drop implementation which may observe a major change in drop order and requires your discretion" + )] significant_droppers: Vec, - #[help] + #[help("the value is now dropped here in Edition 2024")] lifetime_ends: Vec, #[subdiagnostic] rewrite: Option, @@ -352,7 +356,9 @@ impl Subdiagnostic for IfLetRescopeRewrite { .chain(repeat_n('}', closing_brackets.count)) .collect(), )); - let msg = diag.eagerly_translate(crate::fluent_generated::lint_suggestion); + let msg = diag.eagerly_translate(inline_fluent!( + "a `match` with a single arm can preserve the drop order up to Edition 2021" + )); diag.multipart_suggestion_with_style( msg, suggestions, @@ -363,7 +369,12 @@ impl Subdiagnostic for IfLetRescopeRewrite { } #[derive(Subdiagnostic)] -#[note(lint_if_let_dtor)] +#[note( + "{$dtor_kind -> + [dyn] value may invoke a custom destructor because it contains a trait object + *[concrete] value invokes this custom destructor + }" +)] struct DestructorLabel { #[primary_span] span: Span, diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 1cdc5e4a1b36..087403719968 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -3,7 +3,7 @@ use std::cell::LazyCell; use rustc_data_structures::debug_assert_matches; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{LintDiagnostic, Subdiagnostic}; +use rustc_errors::{LintDiagnostic, Subdiagnostic, inline_fluent}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -28,7 +28,7 @@ use rustc_trait_selection::errors::{ use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt; use rustc_trait_selection::traits::ObligationCtxt; -use crate::{LateContext, LateLintPass, fluent_generated as fluent}; +use crate::{LateContext, LateLintPass}; declare_lint! { /// The `impl_trait_overcaptures` lint warns against cases where lifetime @@ -435,11 +435,23 @@ struct ImplTraitOvercapturesLint<'tcx> { impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { - diag.primary_message(fluent::lint_impl_trait_overcaptures); + diag.primary_message(inline_fluent!( + "`{$self_ty}` will capture more lifetimes than possibly intended in edition 2024" + )); diag.arg("self_ty", self.self_ty.to_string()) .arg("num_captured", self.num_captured) - .span_note(self.uncaptured_spans, fluent::lint_note) - .note(fluent::lint_note2); + .span_note( + self.uncaptured_spans, + inline_fluent!( + "specifically, {$num_captured -> + [one] this lifetime is + *[other] these lifetimes are + } in scope but not mentioned in the type's bounds" + ), + ) + .note(inline_fluent!( + "all lifetimes in scope will be captured by `impl Trait`s in edition 2024" + )); if let Some(suggestion) = self.suggestion { suggestion.add_to_diag(diag); } @@ -447,9 +459,9 @@ impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { } #[derive(LintDiagnostic)] -#[diag(lint_impl_trait_redundant_captures)] +#[diag("all possible in-scope parameters are already captured, so `use<...>` syntax is redundant")] struct ImplTraitRedundantCapturesLint { - #[suggestion(lint_suggestion, code = "", applicability = "machine-applicable")] + #[suggestion("remove the `use<...>` syntax", code = "", applicability = "machine-applicable")] capturing_span: Span, } diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index ccfba715a1be..3cc0d46d8541 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -7,7 +7,7 @@ use std::any::Any; use std::cell::Cell; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::join; +use rustc_data_structures::sync::par_join; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit}; use rustc_middle::hir::nested_filter; @@ -461,7 +461,7 @@ fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>( /// Performs lint checking on a crate. pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>) { - join( + par_join( || { tcx.sess.time("crate_lints", || { // Run whole crate non-incremental lints diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 57dacbc11e05..cebe1a5dc373 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -2,7 +2,7 @@ use rustc_ast::attr::AttributeExt; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; +use rustc_errors::{Diag, LintDiagnostic, MultiSpan, inline_fluent}; use rustc_feature::{Features, GateIssue}; use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; @@ -31,7 +31,6 @@ use crate::errors::{ CheckNameUnknownTool, MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup, }; -use crate::fluent_generated as fluent; use crate::late::unerased_lint_store; use crate::lints::{ DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified, @@ -942,9 +941,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let lint = builtin::UNKNOWN_LINTS; let level = self.lint_level(builtin::UNKNOWN_LINTS); lint_level(self.sess, lint, level, Some(span.into()), |lint| { - lint.primary_message(fluent::lint_unknown_gated_lint); + lint.primary_message(inline_fluent!("unknown lint: `{$name}`")); lint.arg("name", lint_id.lint.name_lower()); - lint.note(fluent::lint_note); + lint.note(inline_fluent!("the `{$name}` lint is unstable")); rustc_session::parse::add_feature_diagnostics_for_issue( lint, &self.sess, diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 80b32645a895..94dc566d75f1 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -21,7 +21,7 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_order_by)] @@ -139,8 +139,6 @@ pub use rustc_errors::BufferedEarlyLint; pub use rustc_session::lint::Level::{self, *}; pub use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec}; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub fn provide(providers: &mut Providers) { levels::provide(providers); expect::provide(providers); @@ -297,6 +295,9 @@ fn register_builtins(store: &mut LintStore) { UNUSED_ASSIGNMENTS, DEAD_CODE, UNUSED_MUT, + // FIXME: add this lint when it becomes stable, + // see https://github.com/rust-lang/rust/issues/115585. + // UNREACHABLE_CFG_SELECT_PREDICATES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, UNUSED_MUST_USE, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 1a87cc013e79..e290098238e9 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -5,7 +5,7 @@ use std::num::NonZero; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag, - EmissionGuarantee, LintDiagnostic, MultiSpan, Subdiagnostic, SuggestionStyle, + EmissionGuarantee, LintDiagnostic, MultiSpan, Subdiagnostic, SuggestionStyle, inline_fluent, }; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -17,18 +17,24 @@ use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::{Ident, Span, Symbol, sym}; +use crate::LateContext; use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; use crate::errors::{OverruledAttributeSub, RequestedLevel}; use crate::lifetime_syntax::LifetimeSyntaxCategories; -use crate::{LateContext, fluent_generated as fluent}; // array_into_iter.rs #[derive(LintDiagnostic)] -#[diag(lint_shadowed_into_iter)] +#[diag( + "this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<{$target} as IntoIterator>::into_iter` in Rust {$edition}" +)] pub(crate) struct ShadowedIntoIterDiag { pub target: &'static str, pub edition: &'static str, - #[suggestion(lint_use_iter_suggestion, code = "iter", applicability = "machine-applicable")] + #[suggestion( + "use `.iter()` instead of `.into_iter()` to avoid ambiguity", + code = "iter", + applicability = "machine-applicable" + )] pub suggestion: Span, #[subdiagnostic] pub sub: Option, @@ -36,13 +42,17 @@ pub(crate) struct ShadowedIntoIterDiag { #[derive(Subdiagnostic)] pub(crate) enum ShadowedIntoIterDiagSub { - #[suggestion(lint_remove_into_iter_suggestion, code = "", applicability = "maybe-incorrect")] + #[suggestion( + "or remove `.into_iter()` to iterate by value", + code = "", + applicability = "maybe-incorrect" + )] RemoveIntoIter { #[primary_span] span: Span, }, #[multipart_suggestion( - lint_use_explicit_into_iter_suggestion, + "or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value", applicability = "maybe-incorrect" )] UseExplicitIntoIter { @@ -55,10 +65,12 @@ pub(crate) enum ShadowedIntoIterDiagSub { // autorefs.rs #[derive(LintDiagnostic)] -#[diag(lint_implicit_unsafe_autorefs)] -#[note] +#[diag("implicit autoref creates a reference to the dereference of a raw pointer")] +#[note( + "creating a reference requires the pointer target to be valid and imposes aliasing requirements" +)] pub(crate) struct ImplicitUnsafeAutorefsDiag<'a> { - #[label(lint_raw_ptr)] + #[label("this raw pointer has type `{$raw_ptr_ty}`")] pub raw_ptr_span: Span, pub raw_ptr_ty: Ty<'a>, #[subdiagnostic] @@ -71,18 +83,20 @@ pub(crate) struct ImplicitUnsafeAutorefsDiag<'a> { #[derive(Subdiagnostic)] pub(crate) enum ImplicitUnsafeAutorefsOrigin<'a> { - #[note(lint_autoref)] + #[note("autoref is being applied to this expression, resulting in: `{$autoref_ty}`")] Autoref { #[primary_span] autoref_span: Span, autoref_ty: Ty<'a>, }, - #[note(lint_overloaded_deref)] + #[note( + "references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations" + )] OverloadedDeref, } #[derive(Subdiagnostic)] -#[note(lint_method_def)] +#[note("method calls to `{$method_name}` require a reference")] pub(crate) struct ImplicitUnsafeAutorefsMethodNote { #[primary_span] pub def_span: Span, @@ -90,7 +104,10 @@ pub(crate) struct ImplicitUnsafeAutorefsMethodNote { } #[derive(Subdiagnostic)] -#[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")] +#[multipart_suggestion( + "try using a raw pointer method instead; or if this reference is intentional, make it explicit", + applicability = "maybe-incorrect" +)] pub(crate) struct ImplicitUnsafeAutorefsSuggestion { pub mutbl: &'static str, pub deref: &'static str, @@ -102,78 +119,105 @@ pub(crate) struct ImplicitUnsafeAutorefsSuggestion { // builtin.rs #[derive(LintDiagnostic)] -#[diag(lint_builtin_while_true)] +#[diag("denote infinite loops with `loop {\"{\"} ... {\"}\"}`")] pub(crate) struct BuiltinWhileTrue { - #[suggestion(style = "short", code = "{replace}", applicability = "machine-applicable")] + #[suggestion( + "use `loop`", + style = "short", + code = "{replace}", + applicability = "machine-applicable" + )] pub suggestion: Span, pub replace: String, } #[derive(LintDiagnostic)] -#[diag(lint_builtin_non_shorthand_field_patterns)] +#[diag("the `{$ident}:` in this pattern is redundant")] pub(crate) struct BuiltinNonShorthandFieldPatterns { pub ident: Ident, - #[suggestion(code = "{prefix}{ident}", applicability = "machine-applicable")] + #[suggestion( + "use shorthand field pattern", + code = "{prefix}{ident}", + applicability = "machine-applicable" + )] pub suggestion: Span, pub prefix: &'static str, } #[derive(LintDiagnostic)] pub(crate) enum BuiltinUnsafe { - #[diag(lint_builtin_allow_internal_unsafe)] + #[diag( + "`allow_internal_unsafe` allows defining macros using unsafe without triggering the `unsafe_code` lint at their call site" + )] AllowInternalUnsafe, - #[diag(lint_builtin_unsafe_block)] + #[diag("usage of an `unsafe` block")] UnsafeBlock, - #[diag(lint_builtin_unsafe_extern_block)] + #[diag("usage of an `unsafe extern` block")] UnsafeExternBlock, - #[diag(lint_builtin_unsafe_trait)] + #[diag("declaration of an `unsafe` trait")] UnsafeTrait, - #[diag(lint_builtin_unsafe_impl)] + #[diag("implementation of an `unsafe` trait")] UnsafeImpl, - #[diag(lint_builtin_no_mangle_fn)] - #[note(lint_builtin_overridden_symbol_name)] + #[diag("declaration of a `no_mangle` function")] + #[note( + "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them" + )] NoMangleFn, - #[diag(lint_builtin_export_name_fn)] - #[note(lint_builtin_overridden_symbol_name)] + #[diag("declaration of a function with `export_name`")] + #[note( + "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them" + )] ExportNameFn, - #[diag(lint_builtin_link_section_fn)] - #[note(lint_builtin_overridden_symbol_section)] + #[diag("declaration of a function with `link_section`")] + #[note( + "the program's behavior with overridden link sections on items is unpredictable and Rust cannot provide guarantees when you manually override them" + )] LinkSectionFn, - #[diag(lint_builtin_no_mangle_static)] - #[note(lint_builtin_overridden_symbol_name)] + #[diag("declaration of a `no_mangle` static")] + #[note( + "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them" + )] NoMangleStatic, - #[diag(lint_builtin_export_name_static)] - #[note(lint_builtin_overridden_symbol_name)] + #[diag("declaration of a static with `export_name`")] + #[note( + "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them" + )] ExportNameStatic, - #[diag(lint_builtin_link_section_static)] - #[note(lint_builtin_overridden_symbol_section)] + #[diag("declaration of a static with `link_section`")] + #[note( + "the program's behavior with overridden link sections on items is unpredictable and Rust cannot provide guarantees when you manually override them" + )] LinkSectionStatic, - #[diag(lint_builtin_no_mangle_method)] - #[note(lint_builtin_overridden_symbol_name)] + #[diag("declaration of a `no_mangle` method")] + #[note( + "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them" + )] NoMangleMethod, - #[diag(lint_builtin_export_name_method)] - #[note(lint_builtin_overridden_symbol_name)] + #[diag("declaration of a method with `export_name`")] + #[note( + "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them" + )] ExportNameMethod, - #[diag(lint_builtin_decl_unsafe_fn)] + #[diag("declaration of an `unsafe` function")] DeclUnsafeFn, - #[diag(lint_builtin_decl_unsafe_method)] + #[diag("declaration of an `unsafe` method")] DeclUnsafeMethod, - #[diag(lint_builtin_impl_unsafe_method)] + #[diag("implementation of an `unsafe` method")] ImplUnsafeMethod, - #[diag(lint_builtin_global_asm)] - #[note(lint_builtin_global_macro_unsafety)] + #[diag("usage of `core::arch::global_asm`")] + #[note("using this macro is unsafe even though it does not need an `unsafe` block")] GlobalAsm, } #[derive(LintDiagnostic)] -#[diag(lint_builtin_missing_doc)] +#[diag("missing documentation for {$article} {$desc}")] pub(crate) struct BuiltinMissingDoc<'a> { pub article: &'a str, pub desc: &'a str, } #[derive(LintDiagnostic)] -#[diag(lint_builtin_missing_copy_impl)] +#[diag("type could implement `Copy`; consider adding `impl Copy`")] pub(crate) struct BuiltinMissingCopyImpl; pub(crate) struct BuiltinMissingDebugImpl<'a> { @@ -184,24 +228,24 @@ pub(crate) struct BuiltinMissingDebugImpl<'a> { // Needed for def_path_str impl<'a> LintDiagnostic<'a, ()> for BuiltinMissingDebugImpl<'_> { fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { - diag.primary_message(fluent::lint_builtin_missing_debug_impl); + diag.primary_message(inline_fluent!("type does not implement `{$debug}`; consider adding `#[derive(Debug)]` or a manual implementation")); diag.arg("debug", self.tcx.def_path_str(self.def_id)); } } #[derive(LintDiagnostic)] -#[diag(lint_builtin_anonymous_params)] +#[diag("anonymous parameters are deprecated and will be removed in the next edition")] pub(crate) struct BuiltinAnonymousParams<'a> { - #[suggestion(code = "_: {ty_snip}")] + #[suggestion("try naming the parameter or explicitly ignoring it", code = "_: {ty_snip}")] pub suggestion: (Span, Applicability), pub ty_snip: &'a str, } #[derive(LintDiagnostic)] -#[diag(lint_builtin_unused_doc_comment)] +#[diag("unused doc comment")] pub(crate) struct BuiltinUnusedDocComment<'a> { pub kind: &'a str, - #[label] + #[label("rustdoc does not generate documentation for {$kind}")] pub label: Span, #[subdiagnostic] pub sub: BuiltinUnusedDocCommentSub, @@ -209,34 +253,41 @@ pub(crate) struct BuiltinUnusedDocComment<'a> { #[derive(Subdiagnostic)] pub(crate) enum BuiltinUnusedDocCommentSub { - #[help(lint_plain_help)] + #[help("use `//` for a plain comment")] PlainHelp, - #[help(lint_block_help)] + #[help("use `/* */` for a plain comment")] BlockHelp, } #[derive(LintDiagnostic)] -#[diag(lint_builtin_no_mangle_generic)] +#[diag("functions generic over types or consts must be mangled")] pub(crate) struct BuiltinNoMangleGeneric { // Use of `#[no_mangle]` suggests FFI intent; correct // fix may be to monomorphize source by hand - #[suggestion(style = "short", code = "", applicability = "maybe-incorrect")] + #[suggestion( + "remove this attribute", + style = "short", + code = "", + applicability = "maybe-incorrect" + )] pub suggestion: Span, } #[derive(LintDiagnostic)] -#[diag(lint_builtin_const_no_mangle)] +#[diag("const items should never be `#[no_mangle]`")] pub(crate) struct BuiltinConstNoMangle { - #[suggestion(code = "pub static ", applicability = "machine-applicable")] + #[suggestion("try a static value", code = "pub static ", applicability = "machine-applicable")] pub suggestion: Option, } #[derive(LintDiagnostic)] -#[diag(lint_builtin_mutable_transmutes)] +#[diag( + "transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell" +)] pub(crate) struct BuiltinMutablesTransmutes; #[derive(LintDiagnostic)] -#[diag(lint_builtin_unstable_features)] +#[diag("use of an unstable feature")] pub(crate) struct BuiltinUnstableFeatures; // lint_ungated_async_fn_track_caller @@ -247,8 +298,11 @@ pub(crate) struct BuiltinUngatedAsyncFnTrackCaller<'a> { impl<'a> LintDiagnostic<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(fluent::lint_ungated_async_fn_track_caller); - diag.span_label(self.label, fluent::lint_label); + diag.primary_message(inline_fluent!("`#[track_caller]` on async functions is a no-op")); + diag.span_label( + self.label, + inline_fluent!("this function will not propagate the caller location"), + ); rustc_session::parse::add_feature_diagnostics( diag, self.session, @@ -258,20 +312,24 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> { } #[derive(LintDiagnostic)] -#[diag(lint_builtin_unreachable_pub)] +#[diag("unreachable `pub` {$what}")] pub(crate) struct BuiltinUnreachablePub<'a> { pub what: &'a str, pub new_vis: &'a str, - #[suggestion(code = "{new_vis}")] + #[suggestion("consider restricting its visibility", code = "{new_vis}")] pub suggestion: (Span, Applicability), - #[help] + #[help("or consider exporting it for use by other crates")] pub help: bool, } #[derive(LintDiagnostic)] -#[diag(lint_macro_expr_fragment_specifier_2024_migration)] +#[diag("the `expr` fragment specifier will accept more expressions in the 2024 edition")] pub(crate) struct MacroExprFragment2024 { - #[suggestion(code = "expr_2021", applicability = "machine-applicable")] + #[suggestion( + "to keep the existing behavior, use the `expr_2021` fragment specifier", + code = "expr_2021", + applicability = "machine-applicable" + )] pub suggestion: Span, } @@ -287,14 +345,18 @@ pub(crate) struct BuiltinTypeAliasBounds<'hir> { impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { diag.primary_message(if self.in_where_clause { - fluent::lint_builtin_type_alias_bounds_where_clause + inline_fluent!("where clauses on type aliases are not enforced") } else { - fluent::lint_builtin_type_alias_bounds_param_bounds + inline_fluent!("bounds on generic parameters in type aliases are not enforced") }); - diag.span_label(self.label, fluent::lint_builtin_type_alias_bounds_label); - diag.note(fluent::lint_builtin_type_alias_bounds_limitation_note); + diag.span_label( + self.label, + inline_fluent!("will not be checked at usage sites of the type alias"), + ); + diag.note(inline_fluent!("this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information")); if self.enable_feat_help { - diag.help(fluent::lint_builtin_type_alias_bounds_enable_feat_help); + diag.help(inline_fluent!("add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics")); } // We perform the walk in here instead of in `` to @@ -319,7 +381,20 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> { }; diag.arg("count", self.suggestions.len()); - diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, applicability); + diag.multipart_suggestion( + if self.in_where_clause { + inline_fluent!("remove this where clause") + } else { + inline_fluent!( + "remove {$count -> + [one] this bound + *[other] these bounds + }" + ) + }, + self.suggestions, + applicability, + ); // Suggest fully qualifying paths of the form `T::Assoc` with `T` type param via // `::Assoc` to remove their reliance on any type param bounds. @@ -333,7 +408,7 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> { // (We could employ some simple heuristics but that's likely not worth it). for qself in collector.qselves { diag.multipart_suggestion( - fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg, + inline_fluent!("fully qualify this associated type"), vec![ (qself.shrink_to_lo(), "<".into()), (qself.shrink_to_hi(), " as /* Trait */>".into()), @@ -345,23 +420,27 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> { } #[derive(LintDiagnostic)] -#[diag(lint_builtin_trivial_bounds)] +#[diag( + "{$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters" +)] pub(crate) struct BuiltinTrivialBounds<'a> { pub predicate_kind_name: &'a str, pub predicate: Clause<'a>, } #[derive(LintDiagnostic)] -#[diag(lint_builtin_double_negations)] -#[note(lint_note)] -#[note(lint_note_decrement)] +#[diag("use of a double negation")] +#[note( + "the prefix `--` could be misinterpreted as a decrement operator which exists in other languages" +)] +#[note("use `-= 1` if you meant to decrement the value")] pub(crate) struct BuiltinDoubleNegations { #[subdiagnostic] pub add_parens: BuiltinDoubleNegationsAddParens, } #[derive(Subdiagnostic)] -#[multipart_suggestion(lint_add_parens_suggestion, applicability = "maybe-incorrect")] +#[multipart_suggestion("add parentheses for clarity", applicability = "maybe-incorrect")] pub(crate) struct BuiltinDoubleNegationsAddParens { #[suggestion_part(code = "(")] pub start_span: Span, @@ -371,31 +450,44 @@ pub(crate) struct BuiltinDoubleNegationsAddParens { #[derive(LintDiagnostic)] pub(crate) enum BuiltinEllipsisInclusiveRangePatternsLint { - #[diag(lint_builtin_ellipsis_inclusive_range_patterns)] + #[diag("`...` range patterns are deprecated")] Parenthesise { - #[suggestion(code = "{replace}", applicability = "machine-applicable")] + #[suggestion( + "use `..=` for an inclusive range", + code = "{replace}", + applicability = "machine-applicable" + )] suggestion: Span, replace: String, }, - #[diag(lint_builtin_ellipsis_inclusive_range_patterns)] + #[diag("`...` range patterns are deprecated")] NonParenthesise { - #[suggestion(style = "short", code = "..=", applicability = "machine-applicable")] + #[suggestion( + "use `..=` for an inclusive range", + style = "short", + code = "..=", + applicability = "machine-applicable" + )] suggestion: Span, }, } #[derive(LintDiagnostic)] -#[diag(lint_builtin_keyword_idents)] +#[diag("`{$kw}` is a keyword in the {$next} edition")] pub(crate) struct BuiltinKeywordIdents { pub kw: Ident, pub next: Edition, - #[suggestion(code = "{prefix}r#{kw}", applicability = "machine-applicable")] + #[suggestion( + "you can use a raw identifier to stay compatible", + code = "{prefix}r#{kw}", + applicability = "machine-applicable" + )] pub suggestion: Span, pub prefix: &'static str, } #[derive(LintDiagnostic)] -#[diag(lint_builtin_explicit_outlives)] +#[diag("outlives requirements can be inferred")] pub(crate) struct BuiltinExplicitOutlives { pub count: usize, #[subdiagnostic] @@ -403,7 +495,12 @@ pub(crate) struct BuiltinExplicitOutlives { } #[derive(Subdiagnostic)] -#[multipart_suggestion(lint_suggestion)] +#[multipart_suggestion( + "remove {$count -> + [one] this bound + *[other] these bounds + }" +)] pub(crate) struct BuiltinExplicitOutlivesSuggestion { #[suggestion_part(code = "")] pub spans: Vec, @@ -412,7 +509,9 @@ pub(crate) struct BuiltinExplicitOutlivesSuggestion { } #[derive(LintDiagnostic)] -#[diag(lint_builtin_incomplete_features)] +#[diag( + "the feature `{$name}` is incomplete and may not be safe to use and/or cause compiler crashes" +)] pub(crate) struct BuiltinIncompleteFeatures { pub name: Symbol, #[subdiagnostic] @@ -422,18 +521,18 @@ pub(crate) struct BuiltinIncompleteFeatures { } #[derive(LintDiagnostic)] -#[diag(lint_builtin_internal_features)] -#[note] +#[diag("the feature `{$name}` is internal to the compiler or standard library")] +#[note("using it is strongly discouraged")] pub(crate) struct BuiltinInternalFeatures { pub name: Symbol, } #[derive(Subdiagnostic)] -#[help(lint_help)] +#[help("consider using `min_{$name}` instead, which is more stable and complete")] pub(crate) struct BuiltinIncompleteFeaturesHelp; #[derive(Subdiagnostic)] -#[note(lint_note)] +#[note("see issue #{$n} for more information")] pub(crate) struct BuiltinFeatureIssueNote { pub n: NonZero, } @@ -450,12 +549,15 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinUnpermittedTypeInit<'_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { diag.primary_message(self.msg); diag.arg("ty", self.ty); - diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label); + diag.span_label( + self.label, + inline_fluent!("this code causes undefined behavior when executed"), + ); if let InhabitedPredicate::True = self.ty.inhabited_predicate(self.tcx) { // Only suggest late `MaybeUninit::assume_init` initialization if the type is inhabited. diag.span_label( self.label, - fluent::lint_builtin_unpermitted_type_init_label_suggestion, + inline_fluent!("help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done"), ); } self.sub.add_to_diag(diag); @@ -487,24 +589,24 @@ impl Subdiagnostic for BuiltinUnpermittedTypeInitSub { #[derive(LintDiagnostic)] pub(crate) enum BuiltinClashingExtern<'a> { - #[diag(lint_builtin_clashing_extern_same_name)] + #[diag("`{$this}` redeclared with a different signature")] SameName { this: Symbol, orig: Symbol, - #[label(lint_previous_decl_label)] + #[label("`{$orig}` previously declared here")] previous_decl_label: Span, - #[label(lint_mismatch_label)] + #[label("this signature doesn't match the previous declaration")] mismatch_label: Span, #[subdiagnostic] sub: BuiltinClashingExternSub<'a>, }, - #[diag(lint_builtin_clashing_extern_diff_name)] + #[diag("`{$this}` redeclares `{$orig}` with a different signature")] DiffName { this: Symbol, orig: Symbol, - #[label(lint_previous_decl_label)] + #[label("`{$orig}` previously declared here")] previous_decl_label: Span, - #[label(lint_mismatch_label)] + #[label("this signature doesn't match the previous declaration")] mismatch_label: Span, #[subdiagnostic] sub: BuiltinClashingExternSub<'a>, @@ -529,9 +631,9 @@ impl Subdiagnostic for BuiltinClashingExternSub<'_> { } #[derive(LintDiagnostic)] -#[diag(lint_builtin_deref_nullptr)] +#[diag("dereferencing a null pointer")] pub(crate) struct BuiltinDerefNullptr { - #[label] + #[label("this code causes undefined behavior when executed")] pub label: Span, } @@ -539,30 +641,32 @@ pub(crate) struct BuiltinDerefNullptr { #[derive(LintDiagnostic)] pub(crate) enum BuiltinSpecialModuleNameUsed { - #[diag(lint_builtin_special_module_name_used_lib)] - #[note] - #[help] + #[diag("found module declaration for lib.rs")] + #[note("lib.rs is the root of this crate's library target")] + #[help("to refer to it from other targets, use the library's name as the path")] Lib, - #[diag(lint_builtin_special_module_name_used_main)] - #[note] + #[diag("found module declaration for main.rs")] + #[note("a binary crate cannot be used as library")] Main, } // deref_into_dyn_supertrait.rs #[derive(LintDiagnostic)] -#[diag(lint_supertrait_as_deref_target)] +#[diag("this `Deref` implementation is covered by an implicit supertrait coercion")] pub(crate) struct SupertraitAsDerefTarget<'a> { pub self_ty: Ty<'a>, pub supertrait_principal: PolyExistentialTraitRef<'a>, pub target_principal: PolyExistentialTraitRef<'a>, - #[label] + #[label( + "`{$self_ty}` implements `Deref` which conflicts with supertrait `{$supertrait_principal}`" + )] pub label: Span, #[subdiagnostic] pub label2: Option, } #[derive(Subdiagnostic)] -#[label(lint_label2)] +#[label("target type is a supertrait of `{$self_ty}`")] pub(crate) struct SupertraitAsDerefTargetLabel { #[primary_span] pub label: Span, @@ -570,32 +674,38 @@ pub(crate) struct SupertraitAsDerefTargetLabel { // enum_intrinsics_non_enums.rs #[derive(LintDiagnostic)] -#[diag(lint_enum_intrinsics_mem_discriminant)] +#[diag("the return value of `mem::discriminant` is unspecified when called with a non-enum type")] pub(crate) struct EnumIntrinsicsMemDiscriminate<'a> { pub ty_param: Ty<'a>, - #[note] + #[note( + "the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum" + )] pub note: Span, } #[derive(LintDiagnostic)] -#[diag(lint_enum_intrinsics_mem_variant)] -#[note] +#[diag("the return value of `mem::variant_count` is unspecified when called with a non-enum type")] +#[note( + "the type parameter of `variant_count` should be an enum, but it was instantiated with the type `{$ty_param}`, which is not an enum" +)] pub(crate) struct EnumIntrinsicsMemVariant<'a> { pub ty_param: Ty<'a>, } // expect.rs #[derive(LintDiagnostic)] -#[diag(lint_expectation)] +#[diag("this lint expectation is unfulfilled")] pub(crate) struct Expectation { #[subdiagnostic] pub rationale: Option, - #[note] + #[note( + "the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message" + )] pub note: bool, } #[derive(Subdiagnostic)] -#[note(lint_rationale)] +#[note("{$rationale}")] pub(crate) struct ExpectationNote { pub rationale: Symbol, } @@ -603,42 +713,58 @@ pub(crate) struct ExpectationNote { // ptr_nulls.rs #[derive(LintDiagnostic)] pub(crate) enum UselessPtrNullChecksDiag<'a> { - #[diag(lint_useless_ptr_null_checks_fn_ptr)] - #[help] + #[diag( + "function pointers are not nullable, so checking them for null will always return false" + )] + #[help( + "wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value" + )] FnPtr { orig_ty: Ty<'a>, - #[label] + #[label("expression has type `{$orig_ty}`")] label: Span, }, - #[diag(lint_useless_ptr_null_checks_ref)] + #[diag("references are not nullable, so checking them for null will always return false")] Ref { orig_ty: Ty<'a>, - #[label] + #[label("expression has type `{$orig_ty}`")] label: Span, }, - #[diag(lint_useless_ptr_null_checks_fn_ret)] + #[diag( + "returned pointer of `{$fn_name}` call is never null, so checking it for null will always return false" + )] FnRet { fn_name: Ident }, } #[derive(LintDiagnostic)] pub(crate) enum InvalidNullArgumentsDiag { - #[diag(lint_invalid_null_arguments)] - #[help(lint_doc)] + #[diag( + "calling this function with a null pointer is undefined behavior, even if the result of the function is unused" + )] + #[help( + "for more information, visit and " + )] NullPtrInline { - #[label(lint_origin)] + #[label("null pointer originates from here")] null_span: Span, }, - #[diag(lint_invalid_null_arguments)] - #[help(lint_doc)] + #[diag( + "calling this function with a null pointer is undefined behavior, even if the result of the function is unused" + )] + #[help( + "for more information, visit and " + )] NullPtrThroughBinding { - #[note(lint_origin)] + #[note("null pointer originates from here")] null_span: Span, }, } // for_loops_over_fallibles.rs #[derive(LintDiagnostic)] -#[diag(lint_for_loops_over_fallibles)] +#[diag( + "for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement" +)] pub(crate) struct ForLoopsOverFalliblesDiag<'a> { pub article: &'static str, pub ref_prefix: &'static str, @@ -653,13 +779,20 @@ pub(crate) struct ForLoopsOverFalliblesDiag<'a> { #[derive(Subdiagnostic)] pub(crate) enum ForLoopsOverFalliblesLoopSub<'a> { - #[suggestion(lint_remove_next, code = ".by_ref()", applicability = "maybe-incorrect")] + #[suggestion( + "to iterate over `{$recv_snip}` remove the call to `next`", + code = ".by_ref()", + applicability = "maybe-incorrect" + )] RemoveNext { #[primary_span] suggestion: Span, recv_snip: String, }, - #[multipart_suggestion(lint_use_while_let, applicability = "maybe-incorrect")] + #[multipart_suggestion( + "to check pattern in a loop use `while let`", + applicability = "maybe-incorrect" + )] UseWhileLet { #[suggestion_part(code = "while let {var}(")] start_span: Span, @@ -670,14 +803,21 @@ pub(crate) enum ForLoopsOverFalliblesLoopSub<'a> { } #[derive(Subdiagnostic)] -#[suggestion(lint_use_question_mark, code = "?", applicability = "maybe-incorrect")] +#[suggestion( + "consider unwrapping the `Result` with `?` to iterate over its contents", + code = "?", + applicability = "maybe-incorrect" +)] pub(crate) struct ForLoopsOverFalliblesQuestionMark { #[primary_span] pub suggestion: Span, } #[derive(Subdiagnostic)] -#[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")] +#[multipart_suggestion( + "consider using `if let` to clear intent", + applicability = "maybe-incorrect" +)] pub(crate) struct ForLoopsOverFalliblesSuggestion<'a> { pub var: &'a str, #[suggestion_part(code = "if let {var}(")] @@ -688,10 +828,10 @@ pub(crate) struct ForLoopsOverFalliblesSuggestion<'a> { #[derive(Subdiagnostic)] pub(crate) enum UseLetUnderscoreIgnoreSuggestion { - #[note(lint_use_let_underscore_ignore_suggestion)] + #[note("use `let _ = ...` to ignore the expression or result")] Note, #[multipart_suggestion( - lint_use_let_underscore_ignore_suggestion, + "use `let _ = ...` to ignore the expression or result", style = "verbose", applicability = "maybe-incorrect" )] @@ -705,57 +845,62 @@ pub(crate) enum UseLetUnderscoreIgnoreSuggestion { // drop_forget_useless.rs #[derive(LintDiagnostic)] -#[diag(lint_dropping_references)] +#[diag("calls to `std::mem::drop` with a reference instead of an owned value does nothing")] pub(crate) struct DropRefDiag<'a> { pub arg_ty: Ty<'a>, - #[label] + #[label("argument has type `{$arg_ty}`")] pub label: Span, #[subdiagnostic] pub sugg: UseLetUnderscoreIgnoreSuggestion, } #[derive(LintDiagnostic)] -#[diag(lint_dropping_copy_types)] +#[diag("calls to `std::mem::drop` with a value that implements `Copy` does nothing")] pub(crate) struct DropCopyDiag<'a> { pub arg_ty: Ty<'a>, - #[label] + #[label("argument has type `{$arg_ty}`")] pub label: Span, #[subdiagnostic] pub sugg: UseLetUnderscoreIgnoreSuggestion, } #[derive(LintDiagnostic)] -#[diag(lint_forgetting_references)] +#[diag("calls to `std::mem::forget` with a reference instead of an owned value does nothing")] pub(crate) struct ForgetRefDiag<'a> { pub arg_ty: Ty<'a>, - #[label] + #[label("argument has type `{$arg_ty}`")] pub label: Span, #[subdiagnostic] pub sugg: UseLetUnderscoreIgnoreSuggestion, } #[derive(LintDiagnostic)] -#[diag(lint_forgetting_copy_types)] +#[diag("calls to `std::mem::forget` with a value that implements `Copy` does nothing")] pub(crate) struct ForgetCopyDiag<'a> { pub arg_ty: Ty<'a>, - #[label] + #[label("argument has type `{$arg_ty}`")] pub label: Span, #[subdiagnostic] pub sugg: UseLetUnderscoreIgnoreSuggestion, } #[derive(LintDiagnostic)] -#[diag(lint_undropped_manually_drops)] +#[diag( + "calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing" +)] pub(crate) struct UndroppedManuallyDropsDiag<'a> { pub arg_ty: Ty<'a>, - #[label] + #[label("argument has type `{$arg_ty}`")] pub label: Span, #[subdiagnostic] pub suggestion: UndroppedManuallyDropsSuggestion, } #[derive(Subdiagnostic)] -#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + "use `std::mem::ManuallyDrop::into_inner` to get the inner value", + applicability = "machine-applicable" +)] pub(crate) struct UndroppedManuallyDropsSuggestion { #[suggestion_part(code = "std::mem::ManuallyDrop::into_inner(")] pub start_span: Span, @@ -766,33 +911,35 @@ pub(crate) struct UndroppedManuallyDropsSuggestion { // invalid_from_utf8.rs #[derive(LintDiagnostic)] pub(crate) enum InvalidFromUtf8Diag { - #[diag(lint_invalid_from_utf8_unchecked)] + #[diag("calls to `{$method}` with an invalid literal are undefined behavior")] Unchecked { method: String, valid_up_to: usize, - #[label] + #[label("the literal was valid UTF-8 up to the {$valid_up_to} bytes")] label: Span, }, - #[diag(lint_invalid_from_utf8_checked)] + #[diag("calls to `{$method}` with an invalid literal always return an error")] Checked { method: String, valid_up_to: usize, - #[label] + #[label("the literal was valid UTF-8 up to the {$valid_up_to} bytes")] label: Span, }, } // interior_mutable_consts.rs #[derive(LintDiagnostic)] -#[diag(lint_const_item_interior_mutations)] -#[note(lint_temporary)] -#[note(lint_never_original)] -#[help] +#[diag("mutation of an interior mutable `const` item with call to `{$method_name}`")] +#[note("each usage of a `const` item creates a new temporary")] +#[note("only the temporaries and never the original `const {$const_name}` will be modified")] +#[help( + "for more details on interior mutability see " +)] pub(crate) struct ConstItemInteriorMutationsDiag<'tcx> { pub method_name: Ident, pub const_name: Ident, pub const_ty: Ty<'tcx>, - #[label] + #[label("`{$const_name}` is a interior mutable `const` item of type `{$const_ty}`")] pub receiver_span: Span, #[subdiagnostic] pub sugg_static: Option, @@ -801,7 +948,7 @@ pub(crate) struct ConstItemInteriorMutationsDiag<'tcx> { #[derive(Subdiagnostic)] pub(crate) enum ConstItemInteriorMutationsSuggestionStatic { #[suggestion( - lint_suggestion_static, + "for a shared instance of `{$const_name}`, consider making it a `static` item instead", code = "{before}static ", style = "verbose", applicability = "maybe-incorrect" @@ -811,35 +958,47 @@ pub(crate) enum ConstItemInteriorMutationsSuggestionStatic { const_: Span, before: &'static str, }, - #[help(lint_suggestion_static)] + #[help("for a shared instance of `{$const_name}`, consider making it a `static` item instead")] Spanless, } // reference_casting.rs #[derive(LintDiagnostic)] pub(crate) enum InvalidReferenceCastingDiag<'tcx> { - #[diag(lint_invalid_reference_casting_borrow_as_mut)] - #[note(lint_invalid_reference_casting_note_book)] + #[diag( + "casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`" + )] + #[note( + "for more information, visit " + )] BorrowAsMut { - #[label] + #[label("casting happened here")] orig_cast: Option, - #[note(lint_invalid_reference_casting_note_ty_has_interior_mutability)] + #[note( + "even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`" + )] ty_has_interior_mutability: bool, }, - #[diag(lint_invalid_reference_casting_assign_to_ref)] - #[note(lint_invalid_reference_casting_note_book)] + #[diag("assigning to `&T` is undefined behavior, consider using an `UnsafeCell`")] + #[note( + "for more information, visit " + )] AssignToRef { - #[label] + #[label("casting happened here")] orig_cast: Option, - #[note(lint_invalid_reference_casting_note_ty_has_interior_mutability)] + #[note( + "even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`" + )] ty_has_interior_mutability: bool, }, - #[diag(lint_invalid_reference_casting_bigger_layout)] - #[note(lint_layout)] + #[diag( + "casting references to a bigger memory layout than the backing allocation is undefined behavior, even if the reference is unused" + )] + #[note("casting from `{$from_ty}` ({$from_size} bytes) to `{$to_ty}` ({$to_size} bytes)")] BiggerLayout { - #[label] + #[label("casting happened here")] orig_cast: Option, - #[label(lint_alloc)] + #[label("backing allocation comes from here")] alloc: Span, from_ty: Ty<'tcx>, from_size: u64, @@ -850,108 +1009,143 @@ pub(crate) enum InvalidReferenceCastingDiag<'tcx> { // map_unit_fn.rs #[derive(LintDiagnostic)] -#[diag(lint_map_unit_fn)] -#[note] +#[diag("`Iterator::map` call that discard the iterator's values")] +#[note( + "`Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated" +)] pub(crate) struct MappingToUnit { - #[label(lint_function_label)] + #[label("this function returns `()`, which is likely not what you wanted")] pub function_label: Span, - #[label(lint_argument_label)] + #[label("called `Iterator::map` with callable that returns `()`")] pub argument_label: Span, - #[label(lint_map_label)] + #[label( + "after this call to map, the resulting iterator is `impl Iterator`, which means the only information carried by the iterator is the number of items" + )] pub map_label: Span, - #[suggestion(style = "verbose", code = "for_each", applicability = "maybe-incorrect")] + #[suggestion( + "you might have meant to use `Iterator::for_each`", + style = "verbose", + code = "for_each", + applicability = "maybe-incorrect" + )] pub suggestion: Span, } // internal.rs #[derive(LintDiagnostic)] -#[diag(lint_default_hash_types)] -#[note] +#[diag("prefer `{$preferred}` over `{$used}`, it has better performance")] +#[note("a `use rustc_data_structures::fx::{$preferred}` may be necessary")] pub(crate) struct DefaultHashTypesDiag<'a> { pub preferred: &'a str, pub used: Symbol, } #[derive(LintDiagnostic)] -#[diag(lint_query_instability)] -#[note] +#[diag("using `{$query}` can result in unstable query results")] +#[note( + "if you believe this case to be fine, allow this lint and add a comment explaining your rationale" +)] pub(crate) struct QueryInstability { pub query: Symbol, } #[derive(LintDiagnostic)] -#[diag(lint_query_untracked)] -#[note] +#[diag("`{$method}` accesses information that is not tracked by the query system")] +#[note( + "if you believe this case to be fine, allow this lint and add a comment explaining your rationale" +)] pub(crate) struct QueryUntracked { pub method: Symbol, } #[derive(LintDiagnostic)] -#[diag(lint_span_use_eq_ctxt)] +#[diag("use `.eq_ctxt()` instead of `.ctxt() == .ctxt()`")] pub(crate) struct SpanUseEqCtxtDiag; #[derive(LintDiagnostic)] -#[diag(lint_symbol_intern_string_literal)] -#[help] +#[diag("using `Symbol::intern` on a string literal")] +#[help("consider adding the symbol to `compiler/rustc_span/src/symbol.rs`")] pub(crate) struct SymbolInternStringLiteralDiag; #[derive(LintDiagnostic)] -#[diag(lint_tykind_kind)] +#[diag("usage of `ty::TyKind::`")] pub(crate) struct TykindKind { - #[suggestion(code = "ty", applicability = "maybe-incorrect")] + #[suggestion( + "try using `ty::` directly", + code = "ty", + applicability = "maybe-incorrect" + )] pub suggestion: Span, } #[derive(LintDiagnostic)] -#[diag(lint_tykind)] -#[help] +#[diag("usage of `ty::TyKind`")] +#[help("try using `Ty` instead")] pub(crate) struct TykindDiag; #[derive(LintDiagnostic)] -#[diag(lint_ty_qualified)] +#[diag("usage of qualified `ty::{$ty}`")] pub(crate) struct TyQualified { pub ty: String, - #[suggestion(code = "{ty}", applicability = "maybe-incorrect")] + #[suggestion( + "try importing it and using it unqualified", + code = "{ty}", + applicability = "maybe-incorrect" + )] pub suggestion: Span, } #[derive(LintDiagnostic)] -#[diag(lint_type_ir_inherent_usage)] -#[note] +#[diag("do not use `rustc_type_ir::inherent` unless you're inside of the trait solver")] +#[note( + "the method or struct you're looking for is likely defined somewhere else downstream in the compiler" +)] pub(crate) struct TypeIrInherentUsage; #[derive(LintDiagnostic)] -#[diag(lint_type_ir_trait_usage)] -#[note] +#[diag( + "do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver" +)] +#[note( + "the method or struct you're looking for is likely defined somewhere else downstream in the compiler" +)] pub(crate) struct TypeIrTraitUsage; #[derive(LintDiagnostic)] -#[diag(lint_type_ir_direct_use)] -#[note] +#[diag("do not use `rustc_type_ir` unless you are implementing type system internals")] +#[note("use `rustc_middle::ty` instead")] pub(crate) struct TypeIrDirectUse; #[derive(LintDiagnostic)] -#[diag(lint_non_glob_import_type_ir_inherent)] +#[diag("non-glob import of `rustc_type_ir::inherent`")] pub(crate) struct NonGlobImportTypeIrInherent { - #[suggestion(code = "{snippet}", applicability = "maybe-incorrect")] + #[suggestion( + "try using a glob import instead", + code = "{snippet}", + applicability = "maybe-incorrect" + )] pub suggestion: Option, pub snippet: &'static str, } #[derive(LintDiagnostic)] -#[diag(lint_lintpass_by_hand)] -#[help] +#[diag("implementing `LintPass` by hand")] +#[help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")] pub(crate) struct LintPassByHand; #[derive(LintDiagnostic)] -#[diag(lint_bad_opt_access)] +#[diag("{$msg}")] pub(crate) struct BadOptAccessDiag<'a> { pub msg: &'a str, } #[derive(LintDiagnostic)] -#[diag(lint_implicit_sysroot_crate_import)] -#[help] +#[diag( + "dangerous use of `extern crate {$name}` which is not guaranteed to exist exactly once in the sysroot" +)] +#[help( + "try using a cargo dependency or using a re-export of the dependency provided by a rustc_* crate" +)] pub(crate) struct ImplicitSysrootCrateImportDiag<'a> { pub name: &'a str, } @@ -959,14 +1153,14 @@ pub(crate) struct ImplicitSysrootCrateImportDiag<'a> { // let_underscore.rs #[derive(LintDiagnostic)] pub(crate) enum NonBindingLet { - #[diag(lint_non_binding_let_on_sync_lock)] + #[diag("non-binding let on a synchronization lock")] SyncLock { - #[label] + #[label("this lock is not assigned to a binding and is immediately dropped")] pat: Span, #[subdiagnostic] sub: NonBindingLetSub, }, - #[diag(lint_non_binding_let_on_drop_type)] + #[diag("non-binding let on a type that has a destructor")] DropType { #[subdiagnostic] sub: NonBindingLetSub, @@ -987,16 +1181,23 @@ impl Subdiagnostic for NonBindingLetSub { let prefix = if self.is_assign_desugar { "let " } else { "" }; diag.span_suggestion_verbose( self.suggestion, - fluent::lint_non_binding_let_suggestion, + inline_fluent!( + "consider binding to an unused variable to avoid immediately dropping the value" + ), format!("{prefix}_unused"), Applicability::MachineApplicable, ); } else { - diag.span_help(self.suggestion, fluent::lint_non_binding_let_suggestion); + diag.span_help( + self.suggestion, + inline_fluent!( + "consider binding to an unused variable to avoid immediately dropping the value" + ), + ); } if let Some(drop_fn_start_end) = self.drop_fn_start_end { diag.multipart_suggestion( - fluent::lint_non_binding_let_multi_suggestion, + inline_fluent!("consider immediately dropping the value"), vec![ (drop_fn_start_end.0, "drop(".to_string()), (drop_fn_start_end.1, ")".to_string()), @@ -1004,16 +1205,18 @@ impl Subdiagnostic for NonBindingLetSub { Applicability::MachineApplicable, ); } else { - diag.help(fluent::lint_non_binding_let_multi_drop_fn); + diag.help(inline_fluent!( + "consider immediately dropping the value using `drop(..)` after the `let` statement" + )); } } } // levels.rs #[derive(LintDiagnostic)] -#[diag(lint_overruled_attribute)] +#[diag("{$lint_level}({$lint_source}) incompatible with previous forbid")] pub(crate) struct OverruledAttributeLint<'a> { - #[label] + #[label("overruled by previous forbid")] pub overruled: Span, pub lint_level: &'a str, pub lint_source: Symbol, @@ -1022,17 +1225,17 @@ pub(crate) struct OverruledAttributeLint<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_deprecated_lint_name)] +#[diag("lint name `{$name}` is deprecated and may not have an effect in the future")] pub(crate) struct DeprecatedLintName<'a> { pub name: String, - #[suggestion(code = "{replace}", applicability = "machine-applicable")] + #[suggestion("change it to", code = "{replace}", applicability = "machine-applicable")] pub suggestion: Span, pub replace: &'a str, } #[derive(LintDiagnostic)] -#[diag(lint_deprecated_lint_name)] -#[help] +#[diag("lint name `{$name}` is deprecated and may not have an effect in the future")] +#[help("change it to {$replace}")] pub(crate) struct DeprecatedLintNameFromCommandLine<'a> { pub name: String, pub replace: &'a str, @@ -1041,7 +1244,7 @@ pub(crate) struct DeprecatedLintNameFromCommandLine<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_renamed_lint)] +#[diag("lint `{$name}` has been renamed to `{$replace}`")] pub(crate) struct RenamedLint<'a> { pub name: &'a str, pub replace: &'a str, @@ -1051,18 +1254,18 @@ pub(crate) struct RenamedLint<'a> { #[derive(Subdiagnostic)] pub(crate) enum RenamedLintSuggestion<'a> { - #[suggestion(lint_suggestion, code = "{replace}", applicability = "machine-applicable")] + #[suggestion("use the new name", code = "{replace}", applicability = "machine-applicable")] WithSpan { #[primary_span] suggestion: Span, replace: &'a str, }, - #[help(lint_help)] + #[help("use the new name `{$replace}`")] WithoutSpan { replace: &'a str }, } #[derive(LintDiagnostic)] -#[diag(lint_renamed_lint)] +#[diag("lint `{$name}` has been renamed to `{$replace}`")] pub(crate) struct RenamedLintFromCommandLine<'a> { pub name: &'a str, pub replace: &'a str, @@ -1073,14 +1276,14 @@ pub(crate) struct RenamedLintFromCommandLine<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_removed_lint)] +#[diag("lint `{$name}` has been removed: {$reason}")] pub(crate) struct RemovedLint<'a> { pub name: &'a str, pub reason: &'a str, } #[derive(LintDiagnostic)] -#[diag(lint_removed_lint)] +#[diag("lint `{$name}` has been removed: {$reason}")] pub(crate) struct RemovedLintFromCommandLine<'a> { pub name: &'a str, pub reason: &'a str, @@ -1089,7 +1292,7 @@ pub(crate) struct RemovedLintFromCommandLine<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_unknown_lint)] +#[diag("unknown lint: `{$name}`")] pub(crate) struct UnknownLint { pub name: String, #[subdiagnostic] @@ -1098,19 +1301,31 @@ pub(crate) struct UnknownLint { #[derive(Subdiagnostic)] pub(crate) enum UnknownLintSuggestion { - #[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")] + #[suggestion( + "{$from_rustc -> + [true] a lint with a similar name exists in `rustc` lints + *[false] did you mean + }", + code = "{replace}", + applicability = "maybe-incorrect" + )] WithSpan { #[primary_span] suggestion: Span, replace: Symbol, from_rustc: bool, }, - #[help(lint_help)] + #[help( + "{$from_rustc -> + [true] a lint with a similar name exists in `rustc` lints: `{$replace}` + *[false] did you mean: `{$replace}` + }" + )] WithoutSpan { replace: Symbol, from_rustc: bool }, } #[derive(LintDiagnostic)] -#[diag(lint_unknown_lint, code = E0602)] +#[diag("unknown lint: `{$name}`", code = E0602)] pub(crate) struct UnknownLintFromCommandLine<'a> { pub name: String, #[subdiagnostic] @@ -1120,7 +1335,7 @@ pub(crate) struct UnknownLintFromCommandLine<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_ignored_unless_crate_specified)] +#[diag("{$level}({$name}) is ignored unless specified at crate level")] pub(crate) struct IgnoredUnlessCrateSpecified<'a> { pub level: &'a str, pub name: Symbol, @@ -1128,53 +1343,78 @@ pub(crate) struct IgnoredUnlessCrateSpecified<'a> { // dangling.rs #[derive(LintDiagnostic)] -#[diag(lint_dangling_pointers_from_temporaries)] -#[help(lint_help_bind)] -#[note(lint_note_safe)] -#[note(lint_note_return)] -#[note(lint_note_more_info)] +#[diag("this creates a dangling pointer because temporary `{$ty}` is dropped at end of statement")] +#[help("bind the `{$ty}` to a variable such that it outlives the pointer returned by `{$callee}`")] +#[note("a dangling pointer is safe, but dereferencing one is undefined behavior")] +#[note("returning a pointer to a local variable will always result in a dangling pointer")] +#[note("for more information, see ")] // FIXME: put #[primary_span] on `ptr_span` once it does not cause conflicts pub(crate) struct DanglingPointersFromTemporaries<'tcx> { pub callee: Ident, pub ty: Ty<'tcx>, - #[label(lint_label_ptr)] + #[label("pointer created here")] pub ptr_span: Span, - #[label(lint_label_temporary)] + #[label("this `{$ty}` is dropped at end of statement")] pub temporary_span: Span, } #[derive(LintDiagnostic)] -#[diag(lint_dangling_pointers_from_locals)] -#[note(lint_note_safe)] -#[note(lint_note_more_info)] +#[diag("{$fn_kind} returns a dangling pointer to dropped local variable `{$local_var_name}`")] +#[note("a dangling pointer is safe, but dereferencing one is undefined behavior")] +#[note("for more information, see ")] pub(crate) struct DanglingPointersFromLocals<'tcx> { pub ret_ty: Ty<'tcx>, - #[label(lint_ret_ty)] + #[label("return type is `{$ret_ty}`")] pub ret_ty_span: Span, pub fn_kind: &'static str, - #[label(lint_local_var)] + #[label("local variable `{$local_var_name}` is dropped at the end of the {$fn_kind}")] pub local_var: Span, pub local_var_name: Ident, pub local_var_ty: Ty<'tcx>, - #[label(lint_created_at)] + #[label("dangling pointer created here")] pub created_at: Option, } // multiple_supertrait_upcastable.rs #[derive(LintDiagnostic)] -#[diag(lint_multiple_supertrait_upcastable)] +#[diag("`{$ident}` is dyn-compatible and has multiple supertraits")] pub(crate) struct MultipleSupertraitUpcastable { pub ident: Ident, } // non_ascii_idents.rs #[derive(LintDiagnostic)] -#[diag(lint_identifier_non_ascii_char)] +#[diag("identifier contains non-ASCII characters")] pub(crate) struct IdentifierNonAsciiChar; #[derive(LintDiagnostic)] -#[diag(lint_identifier_uncommon_codepoints)] -#[note] +#[diag( + "identifier contains {$codepoints_len -> + [one] { $identifier_type -> + [Exclusion] a character from an archaic script + [Technical] a character that is for non-linguistic, specialized usage + [Limited_Use] a character from a script in limited use + [Not_NFKC] a non normalized (NFKC) character + *[other] an uncommon character + } + *[other] { $identifier_type -> + [Exclusion] {$codepoints_len} characters from archaic scripts + [Technical] {$codepoints_len} characters that are for non-linguistic, specialized usage + [Limited_Use] {$codepoints_len} characters from scripts in limited use + [Not_NFKC] {$codepoints_len} non normalized (NFKC) characters + *[other] uncommon characters + } + }: {$codepoints}" +)] +#[note( + r#"{$codepoints_len -> + [one] this character is + *[other] these characters are + } included in the{$identifier_type -> + [Restricted] {""} + *[other] {" "}{$identifier_type} + } Unicode general security profile"# +)] pub(crate) struct IdentifierUncommonCodepoints { pub codepoints: Vec, pub codepoints_len: usize, @@ -1182,20 +1422,22 @@ pub(crate) struct IdentifierUncommonCodepoints { } #[derive(LintDiagnostic)] -#[diag(lint_confusable_identifier_pair)] +#[diag("found both `{$existing_sym}` and `{$sym}` as identifiers, which look alike")] pub(crate) struct ConfusableIdentifierPair { pub existing_sym: Symbol, pub sym: Symbol, - #[label(lint_other_use)] + #[label("other identifier used here")] pub label: Span, - #[label(lint_current_use)] + #[label("this identifier can be confused with `{$existing_sym}`")] pub main_label: Span, } #[derive(LintDiagnostic)] -#[diag(lint_mixed_script_confusables)] -#[note(lint_includes_note)] -#[note] +#[diag( + "the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables" +)] +#[note("the usage includes {$includes}")] +#[note("please recheck to make sure their usages are indeed what you want")] pub(crate) struct MixedScriptConfusables { pub set: String, pub includes: String, @@ -1210,19 +1452,34 @@ pub(crate) struct NonFmtPanicUnused { // Used because of two suggestions based on one Option impl<'a> LintDiagnostic<'a, ()> for NonFmtPanicUnused { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(fluent::lint_non_fmt_panic_unused); + diag.primary_message(inline_fluent!( + "panic message contains {$count -> + [one] an unused + *[other] unused + } formatting {$count -> + [one] placeholder + *[other] placeholders + }" + )); diag.arg("count", self.count); - diag.note(fluent::lint_note); + diag.note(inline_fluent!("this message is not used as a format string when given without arguments, but will be in Rust 2021")); if let Some(span) = self.suggestion { diag.span_suggestion( span.shrink_to_hi(), - fluent::lint_add_args_suggestion, + inline_fluent!( + "add the missing {$count -> + [one] argument + *[other] arguments + }" + ), ", ...", Applicability::HasPlaceholders, ); diag.span_suggestion( span.shrink_to_lo(), - fluent::lint_add_fmt_suggestion, + inline_fluent!( + r#"or add a "{"{"}{"}"}" format string to use the message literally"# + ), "\"{}\", ", Applicability::MachineApplicable, ); @@ -1231,17 +1488,26 @@ impl<'a> LintDiagnostic<'a, ()> for NonFmtPanicUnused { } #[derive(LintDiagnostic)] -#[diag(lint_non_fmt_panic_braces)] -#[note] +#[diag( + "panic message contains {$count -> + [one] a brace + *[other] braces + }" +)] +#[note("this message is not used as a format string, but will be in Rust 2021")] pub(crate) struct NonFmtPanicBraces { pub count: usize, - #[suggestion(code = "\"{{}}\", ", applicability = "machine-applicable")] + #[suggestion( + "add a \"{\"{\"}{\"}\"}\" format string to use the message literally", + code = "\"{{}}\", ", + applicability = "machine-applicable" + )] pub suggestion: Option, } // nonstandard_style.rs #[derive(LintDiagnostic)] -#[diag(lint_non_camel_case_type)] +#[diag("{$sort} `{$name}` should have an upper camel case name")] pub(crate) struct NonCamelCaseType<'a> { pub sort: &'a str, pub name: &'a str, @@ -1251,12 +1517,16 @@ pub(crate) struct NonCamelCaseType<'a> { #[derive(Subdiagnostic)] pub(crate) enum NonCamelCaseTypeSub { - #[label(lint_label)] + #[label("should have an UpperCamelCase name")] Label { #[primary_span] span: Span, }, - #[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")] + #[suggestion( + "convert the identifier to upper camel case", + code = "{replace}", + applicability = "maybe-incorrect" + )] Suggestion { #[primary_span] span: Span, @@ -1265,7 +1535,7 @@ pub(crate) enum NonCamelCaseTypeSub { } #[derive(LintDiagnostic)] -#[diag(lint_non_snake_case)] +#[diag("{$sort} `{$name}` should have a snake case name")] pub(crate) struct NonSnakeCaseDiag<'a> { pub sort: &'a str, pub name: &'a str, @@ -1286,15 +1556,15 @@ impl Subdiagnostic for NonSnakeCaseDiagSub { fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { NonSnakeCaseDiagSub::Label { span } => { - diag.span_label(span, fluent::lint_label); + diag.span_label(span, inline_fluent!("should have a snake_case name")); } NonSnakeCaseDiagSub::Help => { - diag.help(fluent::lint_help); + diag.help(inline_fluent!("convert the identifier to snake case: `{$sc}`")); } NonSnakeCaseDiagSub::ConvertSuggestion { span, suggestion } => { diag.span_suggestion( span, - fluent::lint_convert_suggestion, + inline_fluent!("convert the identifier to snake case"), suggestion, Applicability::MaybeIncorrect, ); @@ -1302,16 +1572,18 @@ impl Subdiagnostic for NonSnakeCaseDiagSub { NonSnakeCaseDiagSub::RenameOrConvertSuggestion { span, suggestion } => { diag.span_suggestion( span, - fluent::lint_rename_or_convert_suggestion, + inline_fluent!( + "rename the identifier or convert it to a snake case raw identifier" + ), suggestion, Applicability::MaybeIncorrect, ); } NonSnakeCaseDiagSub::SuggestionAndNote { span } => { - diag.note(fluent::lint_cannot_convert_note); + diag.note(inline_fluent!("`{$sc}` cannot be used as a raw identifier")); diag.span_suggestion( span, - fluent::lint_rename_suggestion, + inline_fluent!("rename the identifier"), "", Applicability::MaybeIncorrect, ); @@ -1321,7 +1593,7 @@ impl Subdiagnostic for NonSnakeCaseDiagSub { } #[derive(LintDiagnostic)] -#[diag(lint_non_upper_case_global)] +#[diag("{$sort} `{$name}` should have an upper case name")] pub(crate) struct NonUpperCaseGlobal<'a> { pub sort: &'a str, pub name: &'a str, @@ -1333,12 +1605,12 @@ pub(crate) struct NonUpperCaseGlobal<'a> { #[derive(Subdiagnostic)] pub(crate) enum NonUpperCaseGlobalSub { - #[label(lint_label)] + #[label("should have an UPPER_CASE name")] Label { #[primary_span] span: Span, }, - #[suggestion(lint_suggestion, code = "{replace}")] + #[suggestion("convert the identifier to upper case", code = "{replace}")] Suggestion { #[primary_span] span: Span, @@ -1350,7 +1622,7 @@ pub(crate) enum NonUpperCaseGlobalSub { #[derive(Subdiagnostic)] #[suggestion( - lint_suggestion, + "convert the identifier to upper case", code = "{replace}", applicability = "machine-applicable", style = "tool-only" @@ -1363,16 +1635,18 @@ pub(crate) struct NonUpperCaseGlobalSubTool { // noop_method_call.rs #[derive(LintDiagnostic)] -#[diag(lint_noop_method_call)] -#[note] +#[diag("call to `.{$method}()` on a reference in this situation does nothing")] +#[note( + "the type `{$orig_ty}` does not implement `{$trait_}`, so calling `{$method}` on `&{$orig_ty}` copies the reference, which does not do anything and can be removed" +)] pub(crate) struct NoopMethodCallDiag<'a> { pub method: Ident, pub orig_ty: Ty<'a>, pub trait_: Symbol, - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion("remove this redundant call", code = "", applicability = "machine-applicable")] pub label: Span, #[suggestion( - lint_derive_suggestion, + "if you meant to clone `{$orig_ty}`, implement `Clone` for it", code = "#[derive(Clone)]\n", applicability = "maybe-incorrect" )] @@ -1380,13 +1654,17 @@ pub(crate) struct NoopMethodCallDiag<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_suspicious_double_ref_deref)] +#[diag( + "using `.deref()` on a double reference, which returns `{$ty}` instead of dereferencing the inner type" +)] pub(crate) struct SuspiciousDoubleRefDerefDiag<'a> { pub ty: Ty<'a>, } #[derive(LintDiagnostic)] -#[diag(lint_suspicious_double_ref_clone)] +#[diag( + "using `.clone()` on a double reference, which returns `{$ty}` instead of cloning the inner type" +)] pub(crate) struct SuspiciousDoubleRefCloneDiag<'a> { pub ty: Ty<'a>, } @@ -1423,7 +1701,7 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag { doctest, macro_to_change, } => { - diag.primary_message(fluent::lint_non_local_definitions_impl); + diag.primary_message(inline_fluent!("non-local `impl` definition, `impl` blocks should be written at the same level as their item")); diag.arg("depth", depth); diag.arg("body_kind_descr", body_kind_descr); diag.arg("body_name", body_name); @@ -1431,24 +1709,24 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag { if let Some((macro_to_change, macro_kind)) = macro_to_change { diag.arg("macro_to_change", macro_to_change); diag.arg("macro_kind", macro_kind); - diag.note(fluent::lint_macro_to_change); + diag.note(inline_fluent!("the {$macro_kind} `{$macro_to_change}` defines the non-local `impl`, and may need to be changed")); } if let Some(cargo_update) = cargo_update { diag.subdiagnostic(cargo_update); } - diag.note(fluent::lint_non_local); + diag.note(inline_fluent!("an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl`")); if doctest { - diag.help(fluent::lint_doctest); + diag.help(inline_fluent!("make this doc-test a standalone test with its own `fn main() {\"{\"} ... {\"}\"}`")); } if let Some(const_anon) = const_anon { - diag.note(fluent::lint_exception); + diag.note(inline_fluent!("items in an anonymous const item (`const _: () = {\"{\"} ... {\"}\"}`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint")); if let Some(const_anon) = const_anon { diag.span_suggestion( const_anon, - fluent::lint_const_anon, + inline_fluent!("use a const-anon item to suppress this lint"), "_", Applicability::MachineApplicable, ); @@ -1462,18 +1740,21 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag { doctest, cargo_update, } => { - diag.primary_message(fluent::lint_non_local_definitions_macro_rules); + diag.primary_message(inline_fluent!("non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module")); diag.arg("depth", depth); diag.arg("body_kind_descr", body_kind_descr); diag.arg("body_name", body_name); if doctest { - diag.help(fluent::lint_help_doctest); + diag.help(inline_fluent!(r#"remove the `#[macro_export]` or make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}`"#)); } else { - diag.help(fluent::lint_help); + diag.help(inline_fluent!("remove the `#[macro_export]` or move this `macro_rules!` outside the of the current {$body_kind_descr} {$depth -> + [one] `{$body_name}` + *[other] `{$body_name}` and up {$depth} bodies + }")); } - diag.note(fluent::lint_non_local); + diag.note(inline_fluent!("a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute")); if let Some(cargo_update) = cargo_update { diag.subdiagnostic(cargo_update); @@ -1484,7 +1765,9 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag { } #[derive(Subdiagnostic)] -#[note(lint_non_local_definitions_cargo_update)] +#[note( + "the {$macro_kind} `{$macro_name}` may come from an old version of the `{$crate_name}` crate, try updating your dependency with `cargo update -p {$crate_name}`" +)] pub(crate) struct NonLocalDefinitionsCargoUpdateNote { pub macro_kind: &'static str, pub macro_name: Symbol, @@ -1493,8 +1776,8 @@ pub(crate) struct NonLocalDefinitionsCargoUpdateNote { // precedence.rs #[derive(LintDiagnostic)] -#[diag(lint_ambiguous_negative_literals)] -#[note(lint_example)] +#[diag("`-` has lower precedence than method calls, which might be unexpected")] +#[note("e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4`")] pub(crate) struct AmbiguousNegativeLiteralsDiag { #[subdiagnostic] pub negative_literal: AmbiguousNegativeLiteralsNegativeLiteralSuggestion, @@ -1503,7 +1786,10 @@ pub(crate) struct AmbiguousNegativeLiteralsDiag { } #[derive(Subdiagnostic)] -#[multipart_suggestion(lint_negative_literal, applicability = "maybe-incorrect")] +#[multipart_suggestion( + "add parentheses around the `-` and the literal to call the method on a negative literal", + applicability = "maybe-incorrect" +)] pub(crate) struct AmbiguousNegativeLiteralsNegativeLiteralSuggestion { #[suggestion_part(code = "(")] pub start_span: Span, @@ -1512,7 +1798,10 @@ pub(crate) struct AmbiguousNegativeLiteralsNegativeLiteralSuggestion { } #[derive(Subdiagnostic)] -#[multipart_suggestion(lint_current_behavior, applicability = "maybe-incorrect")] +#[multipart_suggestion( + "add parentheses around the literal and the method call to keep the current behavior", + applicability = "maybe-incorrect" +)] pub(crate) struct AmbiguousNegativeLiteralsCurrentBehaviorSuggestion { #[suggestion_part(code = "(")] pub start_span: Span, @@ -1522,16 +1811,21 @@ pub(crate) struct AmbiguousNegativeLiteralsCurrentBehaviorSuggestion { // pass_by_value.rs #[derive(LintDiagnostic)] -#[diag(lint_pass_by_value)] +#[diag("passing `{$ty}` by reference")] pub(crate) struct PassByValueDiag { pub ty: String, - #[suggestion(code = "{ty}", applicability = "maybe-incorrect")] + #[suggestion("try passing by value", code = "{ty}", applicability = "maybe-incorrect")] pub suggestion: Span, } // redundant_semicolon.rs #[derive(LintDiagnostic)] -#[diag(lint_redundant_semicolons)] +#[diag( + "unnecessary trailing {$multiple -> + [true] semicolons + *[false] semicolon + }" +)] pub(crate) struct RedundantSemicolonsDiag { pub multiple: bool, #[subdiagnostic] @@ -1539,7 +1833,14 @@ pub(crate) struct RedundantSemicolonsDiag { } #[derive(Subdiagnostic)] -#[suggestion(lint_redundant_semicolons_suggestion, code = "", applicability = "maybe-incorrect")] +#[suggestion( + "remove {$multiple_semicolons -> + [true] these semicolons + *[false] this semicolon + }", + code = "", + applicability = "maybe-incorrect" +)] pub(crate) struct RedundantSemicolonsSuggestion { pub multiple_semicolons: bool, #[primary_span] @@ -1556,7 +1857,7 @@ pub(crate) struct DropTraitConstraintsDiag<'a> { // Needed for def_path_str impl<'a> LintDiagnostic<'a, ()> for DropTraitConstraintsDiag<'_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(fluent::lint_drop_trait_constraints); + diag.primary_message(inline_fluent!("bounds on `{$predicate}` are most likely incorrect, consider instead using `{$needs_drop}` to detect whether a type can be trivially dropped")); diag.arg("predicate", self.predicate); diag.arg("needs_drop", self.tcx.def_path_str(self.def_id)); } @@ -1570,19 +1871,27 @@ pub(crate) struct DropGlue<'a> { // Needed for def_path_str impl<'a> LintDiagnostic<'a, ()> for DropGlue<'_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(fluent::lint_drop_glue); + diag.primary_message(inline_fluent!("types that do not implement `Drop` can still have drop glue, consider instead using `{$needs_drop}` to detect whether a type is trivially dropped")); diag.arg("needs_drop", self.tcx.def_path_str(self.def_id)); } } // transmute.rs #[derive(LintDiagnostic)] -#[diag(lint_int_to_ptr_transmutes)] -#[note] -#[note(lint_note_exposed_provenance)] -#[help(lint_suggestion_without_provenance_mut)] -#[help(lint_help_transmute)] -#[help(lint_help_exposed_provenance)] +#[diag("transmuting an integer to a pointer creates a pointer without provenance")] +#[note("this is dangerous because dereferencing the resulting pointer is undefined behavior")] +#[note( + "exposed provenance semantics can be used to create a pointer based on some previously exposed provenance" +)] +#[help( + "if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut`" +)] +#[help( + "for more information about transmute, see " +)] +#[help( + "for more information about exposed provenance, see " +)] pub(crate) struct IntegerToPtrTransmutes<'tcx> { #[subdiagnostic] pub suggestion: Option>, @@ -1591,7 +1900,7 @@ pub(crate) struct IntegerToPtrTransmutes<'tcx> { #[derive(Subdiagnostic)] pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> { #[multipart_suggestion( - lint_suggestion_with_exposed_provenance, + "use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance", applicability = "machine-applicable", style = "verbose" )] @@ -1602,7 +1911,7 @@ pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> { start_call: Span, }, #[multipart_suggestion( - lint_suggestion_with_exposed_provenance, + "use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance", applicability = "machine-applicable", style = "verbose" )] @@ -1619,7 +1928,7 @@ pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> { // types.rs #[derive(LintDiagnostic)] -#[diag(lint_range_endpoint_out_of_range)] +#[diag("range endpoint is out of range for `{$ty}`")] pub(crate) struct RangeEndpointOutOfRange<'a> { pub ty: &'a str, #[subdiagnostic] @@ -1629,7 +1938,7 @@ pub(crate) struct RangeEndpointOutOfRange<'a> { #[derive(Subdiagnostic)] pub(crate) enum UseInclusiveRange<'a> { #[suggestion( - lint_range_use_inclusive_range, + "use an inclusive range instead", code = "{start}..={literal}{suffix}", applicability = "machine-applicable" )] @@ -1640,7 +1949,7 @@ pub(crate) enum UseInclusiveRange<'a> { literal: u128, suffix: &'a str, }, - #[multipart_suggestion(lint_range_use_inclusive_range, applicability = "machine-applicable")] + #[multipart_suggestion("use an inclusive range instead", applicability = "machine-applicable")] WithParen { #[suggestion_part(code = "=")] eq_sugg: Span, @@ -1652,7 +1961,7 @@ pub(crate) enum UseInclusiveRange<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_overflowing_bin_hex)] +#[diag("literal out of range for `{$ty}`")] pub(crate) struct OverflowingBinHex<'a> { pub ty: &'a str, pub lit: String, @@ -1666,29 +1975,21 @@ pub(crate) struct OverflowingBinHex<'a> { pub sign_bit_sub: Option>, } +#[derive(Subdiagnostic)] pub(crate) enum OverflowingBinHexSign { + #[note( + "the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` and will become `{$actually}{$ty}`" + )] Positive, + #[note("the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}`")] + #[note("and the value `-{$lit}` will become `{$actually}{$ty}`")] Negative, } -impl Subdiagnostic for OverflowingBinHexSign { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { - match self { - OverflowingBinHexSign::Positive => { - diag.note(fluent::lint_positive_note); - } - OverflowingBinHexSign::Negative => { - diag.note(fluent::lint_negative_note); - diag.note(fluent::lint_negative_becomes_note); - } - } - } -} - #[derive(Subdiagnostic)] pub(crate) enum OverflowingBinHexSub<'a> { #[suggestion( - lint_suggestion, + "consider using the type `{$suggestion_ty}` instead", code = "{sans_suffix}{suggestion_ty}", applicability = "machine-applicable" )] @@ -1698,13 +1999,13 @@ pub(crate) enum OverflowingBinHexSub<'a> { suggestion_ty: &'a str, sans_suffix: &'a str, }, - #[help(lint_help)] + #[help("consider using the type `{$suggestion_ty}` instead")] Help { suggestion_ty: &'a str }, } #[derive(Subdiagnostic)] #[suggestion( - lint_sign_bit_suggestion, + "to use as a negative number (decimal `{$negative_val}`), consider using the type `{$uint_ty}` for the literal and cast it to `{$int_ty}`", code = "{lit_no_suffix}{uint_ty} as {int_ty}", applicability = "maybe-incorrect" )] @@ -1718,8 +2019,8 @@ pub(crate) struct OverflowingBinHexSignBitSub<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_overflowing_int)] -#[note] +#[diag("literal out of range for `{$ty}`")] +#[note("the literal `{$lit}` does not fit into the type `{$ty}` whose range is `{$min}..={$max}`")] pub(crate) struct OverflowingInt<'a> { pub ty: &'a str, pub lit: String, @@ -1730,22 +2031,26 @@ pub(crate) struct OverflowingInt<'a> { } #[derive(Subdiagnostic)] -#[help(lint_help)] +#[help("consider using the type `{$suggestion_ty}` instead")] pub(crate) struct OverflowingIntHelp<'a> { pub suggestion_ty: &'a str, } #[derive(LintDiagnostic)] -#[diag(lint_only_cast_u8_to_char)] +#[diag("only `u8` can be cast into `char`")] pub(crate) struct OnlyCastu8ToChar { - #[suggestion(code = "'\\u{{{literal:X}}}'", applicability = "machine-applicable")] + #[suggestion( + "use a `char` literal instead", + code = "'\\u{{{literal:X}}}'", + applicability = "machine-applicable" + )] pub span: Span, pub literal: u128, } #[derive(LintDiagnostic)] -#[diag(lint_overflowing_uint)] -#[note] +#[diag("literal out of range for `{$ty}`")] +#[note("the literal `{$lit}` does not fit into the type `{$ty}` whose range is `{$min}..={$max}`")] pub(crate) struct OverflowingUInt<'a> { pub ty: &'a str, pub lit: String, @@ -1754,50 +2059,54 @@ pub(crate) struct OverflowingUInt<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_overflowing_literal)] -#[note] +#[diag("literal out of range for `{$ty}`")] +#[note( + "the literal `{$lit}` does not fit into the type `{$ty}` and will be converted to `{$ty}::INFINITY`" +)] pub(crate) struct OverflowingLiteral<'a> { pub ty: &'a str, pub lit: String, } #[derive(LintDiagnostic)] -#[diag(lint_surrogate_char_cast)] -#[note] +#[diag("surrogate values are not valid for `char`")] +#[note("`0xD800..=0xDFFF` are reserved for Unicode surrogates and are not valid `char` values")] pub(crate) struct SurrogateCharCast { pub literal: u128, } #[derive(LintDiagnostic)] -#[diag(lint_too_large_char_cast)] -#[note] +#[diag("value exceeds maximum `char` value")] +#[note("maximum valid `char` value is `0x10FFFF`")] pub(crate) struct TooLargeCharCast { pub literal: u128, } #[derive(LintDiagnostic)] -#[diag(lint_uses_power_alignment)] +#[diag( + "repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type" +)] pub(crate) struct UsesPowerAlignment; #[derive(LintDiagnostic)] -#[diag(lint_unused_comparisons)] +#[diag("comparison is useless due to type limits")] pub(crate) struct UnusedComparisons; #[derive(LintDiagnostic)] pub(crate) enum InvalidNanComparisons { - #[diag(lint_invalid_nan_comparisons_eq_ne)] + #[diag("incorrect NaN comparison, NaN cannot be directly compared to itself")] EqNe { #[subdiagnostic] suggestion: InvalidNanComparisonsSuggestion, }, - #[diag(lint_invalid_nan_comparisons_lt_le_gt_ge)] + #[diag("incorrect NaN comparison, NaN is not orderable")] LtLeGtGe, } #[derive(Subdiagnostic)] pub(crate) enum InvalidNanComparisonsSuggestion { #[multipart_suggestion( - lint_suggestion, + "use `f32::is_nan()` or `f64::is_nan()` instead", style = "verbose", applicability = "machine-applicable" )] @@ -1809,35 +2118,41 @@ pub(crate) enum InvalidNanComparisonsSuggestion { #[suggestion_part(code = "")] nan_plus_binop: Span, }, - #[help(lint_suggestion)] + #[help("use `f32::is_nan()` or `f64::is_nan()` instead")] Spanless, } #[derive(LintDiagnostic)] pub(crate) enum AmbiguousWidePointerComparisons<'a> { - #[diag(lint_ambiguous_wide_pointer_comparisons)] + #[diag( + "ambiguous wide pointer comparison, the comparison includes metadata which may not be expected" + )] SpanfulEq { #[subdiagnostic] addr_suggestion: AmbiguousWidePointerComparisonsAddrSuggestion<'a>, #[subdiagnostic] addr_metadata_suggestion: Option>, }, - #[diag(lint_ambiguous_wide_pointer_comparisons)] + #[diag( + "ambiguous wide pointer comparison, the comparison includes metadata which may not be expected" + )] SpanfulCmp { #[subdiagnostic] cast_suggestion: AmbiguousWidePointerComparisonsCastSuggestion<'a>, #[subdiagnostic] expect_suggestion: AmbiguousWidePointerComparisonsExpectSuggestion<'a>, }, - #[diag(lint_ambiguous_wide_pointer_comparisons)] - #[help(lint_addr_metadata_suggestion)] - #[help(lint_addr_suggestion)] + #[diag( + "ambiguous wide pointer comparison, the comparison includes metadata which may not be expected" + )] + #[help("use explicit `std::ptr::eq` method to compare metadata and addresses")] + #[help("use `std::ptr::addr_eq` or untyped pointers to only compare their addresses")] Spanless, } #[derive(Subdiagnostic)] #[multipart_suggestion( - lint_addr_metadata_suggestion, + "use explicit `std::ptr::eq` method to compare metadata and addresses", style = "verbose", // FIXME(#53934): make machine-applicable again applicability = "maybe-incorrect" @@ -1858,7 +2173,7 @@ pub(crate) struct AmbiguousWidePointerComparisonsAddrMetadataSuggestion<'a> { #[derive(Subdiagnostic)] #[multipart_suggestion( - lint_addr_suggestion, + "use `std::ptr::addr_eq` or untyped pointers to only compare their addresses", style = "verbose", // FIXME(#53934): make machine-applicable again applicability = "maybe-incorrect" @@ -1879,7 +2194,7 @@ pub(crate) struct AmbiguousWidePointerComparisonsAddrSuggestion<'a> { #[derive(Subdiagnostic)] #[multipart_suggestion( - lint_cast_suggestion, + "use untyped pointers to only compare their addresses", style = "verbose", // FIXME(#53934): make machine-applicable again applicability = "maybe-incorrect" @@ -1903,7 +2218,7 @@ pub(crate) struct AmbiguousWidePointerComparisonsCastSuggestion<'a> { #[derive(Subdiagnostic)] #[multipart_suggestion( - lint_expect_suggestion, + "or expect the lint to compare the pointers metadata and addresses", style = "verbose", // FIXME(#53934): make machine-applicable again applicability = "maybe-incorrect" @@ -1922,25 +2237,37 @@ pub(crate) struct AmbiguousWidePointerComparisonsExpectSuggestion<'a> { #[derive(LintDiagnostic)] pub(crate) enum UnpredictableFunctionPointerComparisons<'a, 'tcx> { - #[diag(lint_unpredictable_fn_pointer_comparisons)] - #[note(lint_note_duplicated_fn)] - #[note(lint_note_deduplicated_fn)] - #[note(lint_note_visit_fn_addr_eq)] + #[diag( + "function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique" + )] + #[note("the address of the same function can vary between different codegen units")] + #[note( + "furthermore, different functions could have the same address after being merged together" + )] + #[note( + "for more information visit " + )] Suggestion { #[subdiagnostic] sugg: UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx>, }, - #[diag(lint_unpredictable_fn_pointer_comparisons)] - #[note(lint_note_duplicated_fn)] - #[note(lint_note_deduplicated_fn)] - #[note(lint_note_visit_fn_addr_eq)] + #[diag( + "function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique" + )] + #[note("the address of the same function can vary between different codegen units")] + #[note( + "furthermore, different functions could have the same address after being merged together" + )] + #[note( + "for more information visit " + )] Warn, } #[derive(Subdiagnostic)] pub(crate) enum UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx> { #[multipart_suggestion( - lint_fn_addr_eq_suggestion, + "refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint", style = "verbose", applicability = "maybe-incorrect" )] @@ -1956,7 +2283,7 @@ pub(crate) enum UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx> { right: Span, }, #[multipart_suggestion( - lint_fn_addr_eq_suggestion, + "refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint", style = "verbose", applicability = "maybe-incorrect" )] @@ -1986,69 +2313,73 @@ pub(crate) struct ImproperCTypes<'a> { // Used because of the complexity of Option, DiagMessage, and Option impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(fluent::lint_improper_ctypes); + diag.primary_message(inline_fluent!( + "`extern` {$desc} uses type `{$ty}`, which is not FFI-safe" + )); diag.arg("ty", self.ty); diag.arg("desc", self.desc); - diag.span_label(self.label, fluent::lint_label); + diag.span_label(self.label, inline_fluent!("not FFI-safe")); if let Some(help) = self.help { diag.help(help); } diag.note(self.note); if let Some(note) = self.span_note { - diag.span_note(note, fluent::lint_note); + diag.span_note(note, inline_fluent!("the type is defined here")); } } } #[derive(LintDiagnostic)] -#[diag(lint_improper_gpu_kernel_arg)] -#[help] +#[diag("passing type `{$ty}` to a function with \"gpu-kernel\" ABI may have unexpected behavior")] +#[help("use primitive types and raw pointers to get reliable behavior")] pub(crate) struct ImproperGpuKernelArg<'a> { pub ty: Ty<'a>, } #[derive(LintDiagnostic)] -#[diag(lint_missing_gpu_kernel_export_name)] -#[help] -#[note] +#[diag("function with the \"gpu-kernel\" ABI has a mangled name")] +#[help("use `unsafe(no_mangle)` or `unsafe(export_name = \"\")`")] +#[note("mangled names make it hard to find the kernel, this is usually not intended")] pub(crate) struct MissingGpuKernelExportName; #[derive(LintDiagnostic)] -#[diag(lint_variant_size_differences)] +#[diag("enum variant is more than three times larger ({$largest} bytes) than the next largest")] pub(crate) struct VariantSizeDifferencesDiag { pub largest: u64, } #[derive(LintDiagnostic)] -#[diag(lint_atomic_ordering_load)] -#[help] +#[diag("atomic loads cannot have `Release` or `AcqRel` ordering")] +#[help("consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`")] pub(crate) struct AtomicOrderingLoad; #[derive(LintDiagnostic)] -#[diag(lint_atomic_ordering_store)] -#[help] +#[diag("atomic stores cannot have `Acquire` or `AcqRel` ordering")] +#[help("consider using ordering modes `Release`, `SeqCst` or `Relaxed`")] pub(crate) struct AtomicOrderingStore; #[derive(LintDiagnostic)] -#[diag(lint_atomic_ordering_fence)] -#[help] +#[diag("memory fences cannot have `Relaxed` ordering")] +#[help("consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`")] pub(crate) struct AtomicOrderingFence; #[derive(LintDiagnostic)] -#[diag(lint_atomic_ordering_invalid)] -#[help] +#[diag( + "`{$method}`'s failure ordering may not be `Release` or `AcqRel`, since a failed `{$method}` does not result in a write" +)] +#[help("consider using `Acquire` or `Relaxed` failure ordering instead")] pub(crate) struct InvalidAtomicOrderingDiag { pub method: Symbol, - #[label] + #[label("invalid failure ordering")] pub fail_order_arg_span: Span, } // unused.rs #[derive(LintDiagnostic)] -#[diag(lint_unused_op)] +#[diag("unused {$op} that must be used")] pub(crate) struct UnusedOp<'a> { pub op: &'a str, - #[label] + #[label("the {$op} produces a value")] pub label: Span, #[subdiagnostic] pub suggestion: UnusedOpSuggestion, @@ -2057,7 +2388,7 @@ pub(crate) struct UnusedOp<'a> { #[derive(Subdiagnostic)] pub(crate) enum UnusedOpSuggestion { #[suggestion( - lint_suggestion, + "use `let _ = ...` to ignore the resulting value", style = "verbose", code = "let _ = ", applicability = "maybe-incorrect" @@ -2066,7 +2397,11 @@ pub(crate) enum UnusedOpSuggestion { #[primary_span] span: Span, }, - #[multipart_suggestion(lint_suggestion, style = "verbose", applicability = "maybe-incorrect")] + #[multipart_suggestion( + "use `let _ = ...` to ignore the resulting value", + style = "verbose", + applicability = "maybe-incorrect" + )] BlockTailExpr { #[suggestion_part(code = "let _ = ")] before_span: Span, @@ -2076,7 +2411,7 @@ pub(crate) enum UnusedOpSuggestion { } #[derive(LintDiagnostic)] -#[diag(lint_unused_result)] +#[diag("unused result of type `{$ty}`")] pub(crate) struct UnusedResult<'a> { pub ty: Ty<'a>, } @@ -2084,8 +2419,13 @@ pub(crate) struct UnusedResult<'a> { // FIXME(davidtwco): this isn't properly translatable because of the // pre/post strings #[derive(LintDiagnostic)] -#[diag(lint_unused_closure)] -#[note] +#[diag( + "unused {$pre}{$count -> + [one] closure + *[other] closures + }{$post} that must be used" +)] +#[note("closures are lazy and do nothing unless called")] pub(crate) struct UnusedClosure<'a> { pub count: usize, pub pre: &'a str, @@ -2095,8 +2435,13 @@ pub(crate) struct UnusedClosure<'a> { // FIXME(davidtwco): this isn't properly translatable because of the // pre/post strings #[derive(LintDiagnostic)] -#[diag(lint_unused_coroutine)] -#[note] +#[diag( + "unused {$pre}{$count -> + [one] coroutine + *[other] coroutine + }{$post} that must be used" +)] +#[note("coroutines are lazy and do nothing unless resumed")] pub(crate) struct UnusedCoroutine<'a> { pub count: usize, pub pre: &'a str, @@ -2115,10 +2460,9 @@ pub(crate) struct UnusedDef<'a, 'b> { } #[derive(Subdiagnostic)] - pub(crate) enum UnusedDefSuggestion { #[suggestion( - lint_suggestion, + "use `let _ = ...` to ignore the resulting value", style = "verbose", code = "let _ = ", applicability = "maybe-incorrect" @@ -2127,7 +2471,11 @@ pub(crate) enum UnusedDefSuggestion { #[primary_span] span: Span, }, - #[multipart_suggestion(lint_suggestion, style = "verbose", applicability = "maybe-incorrect")] + #[multipart_suggestion( + "use `let _ = ...` to ignore the resulting value", + style = "verbose", + applicability = "maybe-incorrect" + )] BlockTailExpr { #[suggestion_part(code = "let _ = ")] before_span: Span, @@ -2139,7 +2487,7 @@ pub(crate) enum UnusedDefSuggestion { // Needed because of def_path_str impl<'a> LintDiagnostic<'a, ()> for UnusedDef<'_, '_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(fluent::lint_unused_def); + diag.primary_message(inline_fluent!("unused {$pre}`{$def}`{$post} that must be used")); diag.arg("pre", self.pre); diag.arg("post", self.post); diag.arg("def", self.cx.tcx.def_path_str(self.def_id)); @@ -2154,7 +2502,7 @@ impl<'a> LintDiagnostic<'a, ()> for UnusedDef<'_, '_> { } #[derive(LintDiagnostic)] -#[diag(lint_path_statement_drop)] +#[diag("path statement drops value")] pub(crate) struct PathStatementDrop { #[subdiagnostic] pub sub: PathStatementDropSub, @@ -2162,13 +2510,17 @@ pub(crate) struct PathStatementDrop { #[derive(Subdiagnostic)] pub(crate) enum PathStatementDropSub { - #[suggestion(lint_suggestion, code = "drop({snippet});", applicability = "machine-applicable")] + #[suggestion( + "use `drop` to clarify the intent", + code = "drop({snippet});", + applicability = "machine-applicable" + )] Suggestion { #[primary_span] span: Span, snippet: String, }, - #[help(lint_help)] + #[help("use `drop` to clarify the intent")] Help { #[primary_span] span: Span, @@ -2176,11 +2528,11 @@ pub(crate) enum PathStatementDropSub { } #[derive(LintDiagnostic)] -#[diag(lint_path_statement_no_effect)] +#[diag("path statement with no effect")] pub(crate) struct PathStatementNoEffect; #[derive(LintDiagnostic)] -#[diag(lint_unused_delim)] +#[diag("unnecessary {$delim} around {$item}")] pub(crate) struct UnusedDelim<'a> { pub delim: &'static str, pub item: &'a str, @@ -2189,7 +2541,7 @@ pub(crate) struct UnusedDelim<'a> { } #[derive(Subdiagnostic)] -#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion("remove these {$delim}", applicability = "machine-applicable")] pub(crate) struct UnusedDelimSuggestion { #[suggestion_part(code = "{start_replace}")] pub start_span: Span, @@ -2200,17 +2552,17 @@ pub(crate) struct UnusedDelimSuggestion { } #[derive(LintDiagnostic)] -#[diag(lint_unused_import_braces)] +#[diag("braces around {$node} is unnecessary")] pub(crate) struct UnusedImportBracesDiag { pub node: Symbol, } #[derive(LintDiagnostic)] -#[diag(lint_unused_allocation)] +#[diag("unnecessary allocation, use `&` instead")] pub(crate) struct UnusedAllocationDiag; #[derive(LintDiagnostic)] -#[diag(lint_unused_allocation_mut)] +#[diag("unnecessary allocation, use `&mut` instead")] pub(crate) struct UnusedAllocationMutDiag; pub(crate) struct AsyncFnInTraitDiag { @@ -2219,59 +2571,67 @@ pub(crate) struct AsyncFnInTraitDiag { impl<'a> LintDiagnostic<'a, ()> for AsyncFnInTraitDiag { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - diag.primary_message(fluent::lint_async_fn_in_trait); - diag.note(fluent::lint_note); + diag.primary_message(inline_fluent!("use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified")); + diag.note(inline_fluent!("you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`")); if let Some(sugg) = self.sugg { - diag.multipart_suggestion(fluent::lint_suggestion, sugg, Applicability::MaybeIncorrect); + diag.multipart_suggestion(inline_fluent!("you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send`, but these cannot be relaxed without a breaking API change"), sugg, Applicability::MaybeIncorrect); } } } #[derive(LintDiagnostic)] -#[diag(lint_unit_bindings)] +#[diag("binding has unit type `()`")] pub(crate) struct UnitBindingsDiag { - #[label] + #[label("this pattern is inferred to be the unit type `()`")] pub label: Span, } #[derive(LintDiagnostic)] pub(crate) enum InvalidAsmLabel { - #[diag(lint_invalid_asm_label_named)] - #[help] - #[note] + #[diag("avoid using named labels in inline assembly")] + #[help("only local labels of the form `:` should be used in inline asm")] + #[note( + "see the asm section of Rust By Example for more information" + )] Named { - #[note(lint_invalid_asm_label_no_span)] + #[note("the label may be declared in the expansion of a macro")] missing_precise_span: bool, }, - #[diag(lint_invalid_asm_label_format_arg)] - #[help] - #[note(lint_note1)] - #[note(lint_note2)] + #[diag("avoid using named labels in inline assembly")] + #[help("only local labels of the form `:` should be used in inline asm")] + #[note("format arguments may expand to a non-numeric value")] + #[note( + "see the asm section of Rust By Example for more information" + )] FormatArg { - #[note(lint_invalid_asm_label_no_span)] + #[note("the label may be declared in the expansion of a macro")] missing_precise_span: bool, }, - #[diag(lint_invalid_asm_label_binary)] - #[help] - #[note(lint_note1)] - #[note(lint_note2)] + #[diag("avoid using labels containing only the digits `0` and `1` in inline assembly")] + #[help("start numbering with `2` instead")] + #[note("an LLVM bug makes these labels ambiguous with a binary literal number on x86")] + #[note("see for more information")] Binary { - #[note(lint_invalid_asm_label_no_span)] + #[note("the label may be declared in the expansion of a macro")] missing_precise_span: bool, // hack to get a label on the whole span, must match the emitted span - #[label] + #[label("use a different label that doesn't start with `0` or `1`")] span: Span, }, } #[derive(Subdiagnostic)] pub(crate) enum UnexpectedCfgCargoHelp { - #[help(lint_unexpected_cfg_add_cargo_feature)] - #[help(lint_unexpected_cfg_add_cargo_toml_lint_cfg)] + #[help("consider using a Cargo feature instead")] + #[help( + "or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg}" + )] LintCfg { cargo_toml_lint_cfg: String }, - #[help(lint_unexpected_cfg_add_cargo_feature)] - #[help(lint_unexpected_cfg_add_cargo_toml_lint_cfg)] - #[help(lint_unexpected_cfg_add_build_rs_println)] + #[help("consider using a Cargo feature instead")] + #[help( + "or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg}" + )] + #[help("or consider adding `{$build_rs_println}` to the top of the `build.rs`")] LintCfgAndBuildRs { cargo_toml_lint_cfg: String, build_rs_println: String }, } @@ -2297,7 +2657,7 @@ impl UnexpectedCfgCargoHelp { } #[derive(Subdiagnostic)] -#[help(lint_unexpected_cfg_add_cmdline_arg)] +#[help("to expect this configuration use `{$cmdline_arg}`")] pub(crate) struct UnexpectedCfgRustcHelp { pub cmdline_arg: String, } @@ -2309,17 +2669,23 @@ impl UnexpectedCfgRustcHelp { } #[derive(Subdiagnostic)] -#[note(lint_unexpected_cfg_from_external_macro_origin)] -#[help(lint_unexpected_cfg_from_external_macro_refer)] +#[note( + "using a cfg inside a {$macro_kind} will use the cfgs from the destination crate and not the ones from the defining crate" +)] +#[help("try referring to `{$macro_name}` crate for guidance on how handle this unexpected cfg")] pub(crate) struct UnexpectedCfgRustcMacroHelp { pub macro_kind: &'static str, pub macro_name: Symbol, } #[derive(Subdiagnostic)] -#[note(lint_unexpected_cfg_from_external_macro_origin)] -#[help(lint_unexpected_cfg_from_external_macro_refer)] -#[help(lint_unexpected_cfg_cargo_update)] +#[note( + "using a cfg inside a {$macro_kind} will use the cfgs from the destination crate and not the ones from the defining crate" +)] +#[help("try referring to `{$macro_name}` crate for guidance on how handle this unexpected cfg")] +#[help( + "the {$macro_kind} `{$macro_name}` may come from an old version of the `{$crate_name}` crate, try updating your dependency with `cargo update -p {$crate_name}`" +)] pub(crate) struct UnexpectedCfgCargoMacroHelp { pub macro_kind: &'static str, pub macro_name: Symbol, @@ -2327,7 +2693,7 @@ pub(crate) struct UnexpectedCfgCargoMacroHelp { } #[derive(LintDiagnostic)] -#[diag(lint_unexpected_cfg_name)] +#[diag("unexpected `cfg` condition name: `{$name}`")] pub(crate) struct UnexpectedCfgName { #[subdiagnostic] pub code_sugg: unexpected_cfg_name::CodeSuggestion, @@ -2344,10 +2710,10 @@ pub(crate) mod unexpected_cfg_name { #[derive(Subdiagnostic)] pub(crate) enum CodeSuggestion { - #[help(lint_unexpected_cfg_define_features)] + #[help("consider defining some features in `Cargo.toml`")] DefineFeatures, #[multipart_suggestion( - lint_unexpected_cfg_name_version_syntax, + "there is a similar config predicate: `version(\"..\")`", applicability = "machine-applicable" )] VersionSyntax { @@ -2357,7 +2723,7 @@ pub(crate) mod unexpected_cfg_name { after_value: Span, }, #[suggestion( - lint_unexpected_cfg_name_similar_name_value, + "there is a config with a similar name and value", applicability = "maybe-incorrect", code = "{code}" )] @@ -2367,7 +2733,7 @@ pub(crate) mod unexpected_cfg_name { code: String, }, #[suggestion( - lint_unexpected_cfg_name_similar_name_no_value, + "there is a config with a similar name and no value", applicability = "maybe-incorrect", code = "{code}" )] @@ -2377,7 +2743,7 @@ pub(crate) mod unexpected_cfg_name { code: String, }, #[suggestion( - lint_unexpected_cfg_name_similar_name_different_values, + "there is a config with a similar name and different values", applicability = "maybe-incorrect", code = "{code}" )] @@ -2389,7 +2755,7 @@ pub(crate) mod unexpected_cfg_name { expected: Option, }, #[suggestion( - lint_unexpected_cfg_name_similar_name, + "there is a config with a similar name", applicability = "maybe-incorrect", code = "{code}" )] @@ -2407,7 +2773,7 @@ pub(crate) mod unexpected_cfg_name { expected_names: Option, }, #[suggestion( - lint_unexpected_cfg_boolean, + "you may have meant to use `{$literal}` (notice the capitalization). Doing so makes this predicate evaluate to `{$literal}` unconditionally", applicability = "machine-applicable", style = "verbose", code = "{literal}" @@ -2420,7 +2786,7 @@ pub(crate) mod unexpected_cfg_name { } #[derive(Subdiagnostic)] - #[help(lint_unexpected_cfg_name_expected_values)] + #[help("expected values for `{$best_match}` are: {$possibilities}")] pub(crate) struct ExpectedValues { pub best_match: Symbol, pub possibilities: DiagSymbolList, @@ -2428,7 +2794,7 @@ pub(crate) mod unexpected_cfg_name { #[derive(Subdiagnostic)] #[suggestion( - lint_unexpected_cfg_name_with_similar_value, + "found config with similar value", applicability = "maybe-incorrect", code = "{code}" )] @@ -2439,7 +2805,12 @@ pub(crate) mod unexpected_cfg_name { } #[derive(Subdiagnostic)] - #[help_once(lint_unexpected_cfg_name_expected_names)] + #[help_once( + "expected names are: {$possibilities}{$and_more -> + [0] {\"\"} + *[other] {\" \"}and {$and_more} more + }" + )] pub(crate) struct ExpectedNames { pub possibilities: DiagSymbolList, pub and_more: usize, @@ -2447,14 +2818,18 @@ pub(crate) mod unexpected_cfg_name { #[derive(Subdiagnostic)] pub(crate) enum InvocationHelp { - #[note(lint_unexpected_cfg_doc_cargo)] + #[note( + "see for more information about checking conditional configuration" + )] Cargo { #[subdiagnostic] macro_help: Option, #[subdiagnostic] help: Option, }, - #[note(lint_unexpected_cfg_doc_rustc)] + #[note( + "see for more information about checking conditional configuration" + )] Rustc { #[subdiagnostic] macro_help: Option, @@ -2465,7 +2840,12 @@ pub(crate) mod unexpected_cfg_name { } #[derive(LintDiagnostic)] -#[diag(lint_unexpected_cfg_value)] +#[diag( + "unexpected `cfg` condition value: {$has_value -> + [true] `{$value}` + *[false] (none) + }" +)] pub(crate) struct UnexpectedCfgValue { #[subdiagnostic] pub code_sugg: unexpected_cfg_value::CodeSuggestion, @@ -2489,14 +2869,14 @@ pub(crate) mod unexpected_cfg_value { #[subdiagnostic] suggestion: Option, }, - #[note(lint_unexpected_cfg_value_no_expected_value)] + #[note("no expected value for `{$name}`")] RemoveValue { #[subdiagnostic] suggestion: Option, name: Symbol, }, - #[note(lint_unexpected_cfg_value_no_expected_values)] + #[note("no expected values for `{$name}`")] RemoveCondition { #[subdiagnostic] suggestion: RemoveConditionSuggestion, @@ -2508,7 +2888,7 @@ pub(crate) mod unexpected_cfg_value { #[derive(Subdiagnostic)] pub(crate) enum ChangeValueSuggestion { #[suggestion( - lint_unexpected_cfg_value_similar_name, + "there is a expected value with a similar name", code = r#""{best_match}""#, applicability = "maybe-incorrect" )] @@ -2518,7 +2898,7 @@ pub(crate) mod unexpected_cfg_value { best_match: Symbol, }, #[suggestion( - lint_unexpected_cfg_value_specify_value, + "specify a config value", code = r#" = "{first_possibility}""#, applicability = "maybe-incorrect" )] @@ -2530,29 +2910,29 @@ pub(crate) mod unexpected_cfg_value { } #[derive(Subdiagnostic)] - #[suggestion( - lint_unexpected_cfg_value_remove_value, - code = "", - applicability = "maybe-incorrect" - )] + #[suggestion("remove the value", code = "", applicability = "maybe-incorrect")] pub(crate) struct RemoveValueSuggestion { #[primary_span] pub span: Span, } #[derive(Subdiagnostic)] - #[suggestion( - lint_unexpected_cfg_value_remove_condition, - code = "", - applicability = "maybe-incorrect" - )] + #[suggestion("remove the condition", code = "", applicability = "maybe-incorrect")] pub(crate) struct RemoveConditionSuggestion { #[primary_span] pub span: Span, } #[derive(Subdiagnostic)] - #[note(lint_unexpected_cfg_value_expected_values)] + #[note( + "expected values for `{$name}` are: {$have_none_possibility -> + [true] {\"(none), \"} + *[false] {\"\"} + }{$possibilities}{$and_more -> + [0] {\"\"} + *[other] {\" \"}and {$and_more} more + }" + )] pub(crate) struct ExpectedValues { pub name: Symbol, pub have_none_possibility: bool, @@ -2562,14 +2942,18 @@ pub(crate) mod unexpected_cfg_value { #[derive(Subdiagnostic)] pub(crate) enum InvocationHelp { - #[note(lint_unexpected_cfg_doc_cargo)] + #[note( + "see for more information about checking conditional configuration" + )] Cargo { #[subdiagnostic] help: Option, #[subdiagnostic] macro_help: Option, }, - #[note(lint_unexpected_cfg_doc_rustc)] + #[note( + "see for more information about checking conditional configuration" + )] Rustc { #[subdiagnostic] help: Option, @@ -2580,19 +2964,19 @@ pub(crate) mod unexpected_cfg_value { #[derive(Subdiagnostic)] pub(crate) enum CargoHelp { - #[help(lint_unexpected_cfg_value_add_feature)] + #[help("consider adding `{$value}` as a feature in `Cargo.toml`")] AddFeature { value: Symbol, }, - #[help(lint_unexpected_cfg_define_features)] + #[help("consider defining some features in `Cargo.toml`")] DefineFeatures, Other(#[subdiagnostic] super::UnexpectedCfgCargoHelp), } } #[derive(LintDiagnostic)] -#[diag(lint_unused_crate_dependency)] -#[help] +#[diag("extern crate `{$extern_crate}` is unused in crate `{$local_crate}`")] +#[help("remove the dependency or add `use {$extern_crate} as _;` to the crate root")] pub(crate) struct UnusedCrateDependency { pub extern_crate: Symbol, pub local_crate: Symbol, @@ -2600,20 +2984,32 @@ pub(crate) struct UnusedCrateDependency { // FIXME(jdonszelmann): duplicated in rustc_attr_parsing, should be moved there completely. #[derive(LintDiagnostic)] -#[diag(lint_ill_formed_attribute_input)] +#[diag( + "{$num_suggestions -> + [1] attribute must be of the form {$suggestions} + *[other] valid forms for the attribute are {$suggestions} + }" +)] pub(crate) struct IllFormedAttributeInput { pub num_suggestions: usize, pub suggestions: DiagArgValue, - #[note] + #[note("for more information, visit <{$docs}>")] pub has_docs: bool, pub docs: &'static str, } #[derive(LintDiagnostic)] -#[diag(lint_unicode_text_flow)] -#[note] +#[diag("unicode codepoint changing visible direction of text present in comment")] +#[note( + "these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen" +)] pub(crate) struct UnicodeTextFlow { - #[label] + #[label( + "{$num_codepoints -> + [1] this comment contains an invisible unicode text flow control codepoint + *[other] this comment contains invisible unicode text flow control codepoints + }" + )] pub comment_span: Span, #[subdiagnostic] pub characters: Vec, @@ -2624,7 +3020,7 @@ pub(crate) struct UnicodeTextFlow { } #[derive(Subdiagnostic)] -#[label(lint_label_comment_char)] +#[label("{$c_debug}")] pub(crate) struct UnicodeCharNoteSub { #[primary_span] pub span: Span, @@ -2632,21 +3028,27 @@ pub(crate) struct UnicodeCharNoteSub { } #[derive(Subdiagnostic)] -#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable", style = "hidden")] +#[multipart_suggestion( + "if their presence wasn't intentional, you can remove them", + applicability = "machine-applicable", + style = "hidden" +)] pub(crate) struct UnicodeTextFlowSuggestion { #[suggestion_part(code = "")] pub spans: Vec, } #[derive(LintDiagnostic)] -#[diag(lint_abs_path_with_module)] +#[diag( + "absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition" +)] pub(crate) struct AbsPathWithModule { #[subdiagnostic] pub sugg: AbsPathWithModuleSugg, } #[derive(Subdiagnostic)] -#[suggestion(lint_suggestion, code = "{replacement}")] +#[suggestion("use `crate`", code = "{replacement}")] pub(crate) struct AbsPathWithModuleSugg { #[primary_span] pub span: Span, @@ -2656,18 +3058,23 @@ pub(crate) struct AbsPathWithModuleSugg { } #[derive(LintDiagnostic)] -#[diag(lint_hidden_lifetime_parameters)] +#[diag("hidden lifetime parameters in types are deprecated")] pub(crate) struct ElidedLifetimesInPaths { #[subdiagnostic] pub subdiag: ElidedLifetimeInPathSubdiag, } #[derive(LintDiagnostic)] -#[diag(lint_unused_imports)] +#[diag( + "{$num_snippets -> + [one] unused import: {$span_snippets} + *[other] unused imports: {$span_snippets} + }" +)] pub(crate) struct UnusedImports { #[subdiagnostic] pub sugg: UnusedImportsSugg, - #[help] + #[help("if this is a test module, consider adding a `#[cfg(test)]` to the containing module")] pub test_module_span: Option, pub span_snippets: DiagArgValue, @@ -2677,7 +3084,7 @@ pub(crate) struct UnusedImports { #[derive(Subdiagnostic)] pub(crate) enum UnusedImportsSugg { #[suggestion( - lint_suggestion_remove_whole_use, + "remove the whole `use` item", applicability = "machine-applicable", code = "", style = "tool-only" @@ -2687,7 +3094,10 @@ pub(crate) enum UnusedImportsSugg { span: Span, }, #[multipart_suggestion( - lint_suggestion_remove_imports, + "{$num_to_remove -> + [one] remove the unused import + *[other] remove the unused imports + }", applicability = "machine-applicable", style = "tool-only" )] @@ -2699,7 +3109,7 @@ pub(crate) enum UnusedImportsSugg { } #[derive(LintDiagnostic)] -#[diag(lint_redundant_import)] +#[diag("the item `{$ident}` is imported redundantly")] pub(crate) struct RedundantImport { #[subdiagnostic] pub subs: Vec, @@ -2709,24 +3119,24 @@ pub(crate) struct RedundantImport { #[derive(Subdiagnostic)] pub(crate) enum RedundantImportSub { - #[label(lint_label_imported_here)] + #[label("the item `{$ident}` is already imported here")] ImportedHere(#[primary_span] Span), - #[label(lint_label_defined_here)] + #[label("the item `{$ident}` is already defined here")] DefinedHere(#[primary_span] Span), - #[label(lint_label_imported_prelude)] + #[label("the item `{$ident}` is already imported by the extern prelude")] ImportedPrelude(#[primary_span] Span), - #[label(lint_label_defined_prelude)] + #[label("the item `{$ident}` is already defined by the extern prelude")] DefinedPrelude(#[primary_span] Span), } #[derive(LintDiagnostic)] pub(crate) enum PatternsInFnsWithoutBody { - #[diag(lint_pattern_in_foreign)] + #[diag("patterns aren't allowed in foreign function declarations")] Foreign { #[subdiagnostic] sub: PatternsInFnsWithoutBodySub, }, - #[diag(lint_pattern_in_bodiless)] + #[diag("patterns aren't allowed in functions without bodies")] Bodiless { #[subdiagnostic] sub: PatternsInFnsWithoutBodySub, @@ -2734,7 +3144,11 @@ pub(crate) enum PatternsInFnsWithoutBody { } #[derive(Subdiagnostic)] -#[suggestion(lint_remove_mut_from_pattern, code = "{ident}", applicability = "machine-applicable")] +#[suggestion( + "remove `mut` from the parameter", + code = "{ident}", + applicability = "machine-applicable" +)] pub(crate) struct PatternsInFnsWithoutBodySub { #[primary_span] pub span: Span, @@ -2743,34 +3157,44 @@ pub(crate) struct PatternsInFnsWithoutBodySub { } #[derive(LintDiagnostic)] -#[diag(lint_reserved_prefix)] +#[diag("prefix `{$prefix}` is unknown")] pub(crate) struct ReservedPrefix { - #[label] + #[label("unknown prefix")] pub label: Span, - #[suggestion(code = " ", applicability = "machine-applicable")] + #[suggestion( + "insert whitespace here to avoid this being parsed as a prefix in Rust 2021", + code = " ", + applicability = "machine-applicable" + )] pub suggestion: Span, pub prefix: String, } #[derive(LintDiagnostic)] -#[diag(lint_raw_prefix)] +#[diag("prefix `'r` is reserved")] pub(crate) struct RawPrefix { - #[label] + #[label("reserved prefix")] pub label: Span, - #[suggestion(code = " ", applicability = "machine-applicable")] + #[suggestion( + "insert whitespace here to avoid this being parsed as a prefix in Rust 2021", + code = " ", + applicability = "machine-applicable" + )] pub suggestion: Span, } #[derive(LintDiagnostic)] -#[diag(lint_break_with_label_and_loop)] +#[diag( + "this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression" +)] pub(crate) struct BreakWithLabelAndLoop { #[subdiagnostic] pub sub: BreakWithLabelAndLoopSub, } #[derive(Subdiagnostic)] -#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion("wrap this expression in parentheses", applicability = "machine-applicable")] pub(crate) struct BreakWithLabelAndLoopSub { #[suggestion_part(code = "(")] pub left: Span, @@ -2779,8 +3203,8 @@ pub(crate) struct BreakWithLabelAndLoopSub { } #[derive(LintDiagnostic)] -#[diag(lint_deprecated_where_clause_location)] -#[note] +#[diag("where clause not allowed here")] +#[note("see issue #89122 for more information")] pub(crate) struct DeprecatedWhereClauseLocation { #[subdiagnostic] pub suggestion: DeprecatedWhereClauseLocationSugg, @@ -2788,7 +3212,10 @@ pub(crate) struct DeprecatedWhereClauseLocation { #[derive(Subdiagnostic)] pub(crate) enum DeprecatedWhereClauseLocationSugg { - #[multipart_suggestion(lint_suggestion_move_to_end, applicability = "machine-applicable")] + #[multipart_suggestion( + "move it to the end of the type declaration", + applicability = "machine-applicable" + )] MoveToEnd { #[suggestion_part(code = "")] left: Span, @@ -2797,7 +3224,7 @@ pub(crate) enum DeprecatedWhereClauseLocationSugg { sugg: String, }, - #[suggestion(lint_suggestion_remove_where, code = "", applicability = "machine-applicable")] + #[suggestion("remove this `where`", code = "", applicability = "machine-applicable")] RemoveWhere { #[primary_span] span: Span, @@ -2805,11 +3232,11 @@ pub(crate) enum DeprecatedWhereClauseLocationSugg { } #[derive(LintDiagnostic)] -#[diag(lint_single_use_lifetime)] +#[diag("lifetime parameter `{$ident}` only used once")] pub(crate) struct SingleUseLifetime { - #[label(lint_label_param)] + #[label("this lifetime...")] pub param_span: Span, - #[label(lint_label_use)] + #[label("...is used only here")] pub use_span: Span, #[subdiagnostic] pub suggestion: Option, @@ -2818,7 +3245,7 @@ pub(crate) struct SingleUseLifetime { } #[derive(Subdiagnostic)] -#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion("elide the single-use lifetime", applicability = "machine-applicable")] pub(crate) struct SingleUseLifetimeSugg { #[suggestion_part(code = "")] pub deletion_span: Option, @@ -2829,22 +3256,27 @@ pub(crate) struct SingleUseLifetimeSugg { } #[derive(LintDiagnostic)] -#[diag(lint_unused_lifetime)] +#[diag("lifetime parameter `{$ident}` never used")] pub(crate) struct UnusedLifetime { - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion("elide the unused lifetime", code = "", applicability = "machine-applicable")] pub deletion_span: Option, pub ident: Ident, } #[derive(LintDiagnostic)] -#[diag(lint_named_argument_used_positionally)] +#[diag("named argument `{$named_arg_name}` is not used by name")] pub(crate) struct NamedArgumentUsedPositionally { - #[label(lint_label_named_arg)] + #[label("this named argument is referred to by position in formatting string")] pub named_arg_sp: Span, - #[label(lint_label_position_arg)] + #[label("this formatting argument uses named argument `{$named_arg_name}` by position")] pub position_label_sp: Option, - #[suggestion(style = "verbose", code = "{name}", applicability = "maybe-incorrect")] + #[suggestion( + "use the named argument by name to avoid ambiguity", + style = "verbose", + code = "{name}", + applicability = "maybe-incorrect" + )] pub suggestion: Option, pub name: String, @@ -2852,11 +3284,11 @@ pub(crate) struct NamedArgumentUsedPositionally { } #[derive(LintDiagnostic)] -#[diag(lint_ambiguous_glob_reexport)] +#[diag("ambiguous glob re-exports")] pub(crate) struct AmbiguousGlobReexports { - #[label(lint_label_first_reexport)] + #[label("the name `{$name}` in the {$namespace} namespace is first re-exported here")] pub first_reexport: Span, - #[label(lint_label_duplicate_reexport)] + #[label("but the name `{$name}` in the {$namespace} namespace is also re-exported here")] pub duplicate_reexport: Span, pub name: String, @@ -2864,11 +3296,13 @@ pub(crate) struct AmbiguousGlobReexports { } #[derive(LintDiagnostic)] -#[diag(lint_hidden_glob_reexport)] +#[diag("private item shadows public glob re-export")] pub(crate) struct HiddenGlobReexports { - #[note(lint_note_glob_reexport)] + #[note( + "the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here" + )] pub glob_reexport: Span, - #[note(lint_note_private_item)] + #[note("but the private item here shadows it")] pub private_item: Span, pub name: String, @@ -2876,47 +3310,70 @@ pub(crate) struct HiddenGlobReexports { } #[derive(LintDiagnostic)] -#[diag(lint_unnecessary_qualification)] +#[diag("unnecessary qualification")] pub(crate) struct UnusedQualifications { - #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] + #[suggestion( + "remove the unnecessary path segments", + style = "verbose", + code = "", + applicability = "machine-applicable" + )] pub removal_span: Span, } #[derive(LintDiagnostic)] -#[diag(lint_associated_const_elided_lifetime)] +#[diag( + "{$elided -> + [true] `&` without an explicit lifetime name cannot be used here + *[false] `'_` cannot be used here + }" +)] pub(crate) struct AssociatedConstElidedLifetime { - #[suggestion(style = "verbose", code = "{code}", applicability = "machine-applicable")] + #[suggestion( + "use the `'static` lifetime", + style = "verbose", + code = "{code}", + applicability = "machine-applicable" + )] pub span: Span, pub code: &'static str, pub elided: bool, - #[note] + #[note("cannot automatically infer `'static` because of other lifetimes in scope")] pub lifetimes_in_scope: MultiSpan, } #[derive(LintDiagnostic)] -#[diag(lint_static_mut_refs_lint)] +#[diag("creating a {$shared_label}reference to mutable static")] pub(crate) struct RefOfMutStatic<'a> { - #[label] + #[label("{$shared_label}reference to mutable static")] pub span: Span, #[subdiagnostic] pub sugg: Option, pub shared_label: &'a str, - #[note(lint_shared_note)] + #[note( + "shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives" + )] pub shared_note: bool, - #[note(lint_mut_note)] + #[note( + "mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives" + )] pub mut_note: bool, } #[derive(Subdiagnostic)] pub(crate) enum MutRefSugg { - #[multipart_suggestion(lint_suggestion, style = "verbose", applicability = "maybe-incorrect")] + #[multipart_suggestion( + "use `&raw const` instead to create a raw pointer", + style = "verbose", + applicability = "maybe-incorrect" + )] Shared { #[suggestion_part(code = "&raw const ")] span: Span, }, #[multipart_suggestion( - lint_suggestion_mut, + "use `&raw mut` instead to create a raw pointer", style = "verbose", applicability = "maybe-incorrect" )] @@ -2927,25 +3384,33 @@ pub(crate) enum MutRefSugg { } #[derive(LintDiagnostic)] -#[diag(lint_unqualified_local_imports)] +#[diag("`use` of a local item without leading `self::`, `super::`, or `crate::`")] pub(crate) struct UnqualifiedLocalImportsDiag {} #[derive(LintDiagnostic)] -#[diag(lint_reserved_string)] +#[diag("will be parsed as a guarded string in Rust 2024")] pub(crate) struct ReservedString { - #[suggestion(code = " ", applicability = "machine-applicable")] + #[suggestion( + "insert whitespace here to avoid this being parsed as a guarded string in Rust 2024", + code = " ", + applicability = "machine-applicable" + )] pub suggestion: Span, } #[derive(LintDiagnostic)] -#[diag(lint_reserved_multihash)] +#[diag("reserved token in Rust 2024")] pub(crate) struct ReservedMultihash { - #[suggestion(code = " ", applicability = "machine-applicable")] + #[suggestion( + "insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024", + code = " ", + applicability = "machine-applicable" + )] pub suggestion: Span, } #[derive(LintDiagnostic)] -#[diag(lint_function_casts_as_integer)] +#[diag("direct cast of function item into an integer")] pub(crate) struct FunctionCastsAsIntegerDiag<'tcx> { #[subdiagnostic] pub(crate) sugg: FunctionCastsAsIntegerSugg<'tcx>, @@ -2953,7 +3418,7 @@ pub(crate) struct FunctionCastsAsIntegerDiag<'tcx> { #[derive(Subdiagnostic)] #[suggestion( - lint_cast_as_fn, + "first cast to a pointer `as *const ()`", code = " as *const ()", applicability = "machine-applicable", style = "verbose" @@ -2981,44 +3446,46 @@ impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for MismatchedLifetimeSynta } LifetimeSyntaxCategories { hidden: _, elided: _, named: 0 } => { - fluent::lint_mismatched_lifetime_syntaxes_hiding_while_elided + inline_fluent!("hiding a lifetime that's elided elsewhere is confusing") } LifetimeSyntaxCategories { hidden: _, elided: 0, named: _ } => { - fluent::lint_mismatched_lifetime_syntaxes_hiding_while_named + inline_fluent!("hiding a lifetime that's named elsewhere is confusing") } LifetimeSyntaxCategories { hidden: 0, elided: _, named: _ } => { - fluent::lint_mismatched_lifetime_syntaxes_eliding_while_named + inline_fluent!("eliding a lifetime that's named elsewhere is confusing") } LifetimeSyntaxCategories { hidden: _, elided: _, named: _ } => { - fluent::lint_mismatched_lifetime_syntaxes_hiding_and_eliding_while_named + inline_fluent!("hiding or eliding a lifetime that's named elsewhere is confusing") } }; diag.primary_message(message); for s in self.inputs.hidden { - diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_input_hidden); + diag.span_label(s, inline_fluent!("the lifetime is hidden here")); } for s in self.inputs.elided { - diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_input_elided); + diag.span_label(s, inline_fluent!("the lifetime is elided here")); } for s in self.inputs.named { - diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_input_named); + diag.span_label(s, inline_fluent!("the lifetime is named here")); } for s in self.outputs.hidden { - diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_output_hidden); + diag.span_label(s, inline_fluent!("the same lifetime is hidden here")); } for s in self.outputs.elided { - diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_output_elided); + diag.span_label(s, inline_fluent!("the same lifetime is elided here")); } for s in self.outputs.named { - diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_output_named); + diag.span_label(s, inline_fluent!("the same lifetime is named here")); } - diag.help(fluent::lint_mismatched_lifetime_syntaxes_help); + diag.help(inline_fluent!( + "the same lifetime is referred to in inconsistent ways, making the signature confusing" + )); let mut suggestions = self.suggestions.into_iter(); if let Some(s) = suggestions.next() { @@ -3092,7 +3559,7 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { Implicit { suggestions, optional_alternative } => { let suggestions = suggestions.into_iter().map(|s| (s, String::new())).collect(); diag.multipart_suggestion_with_style( - fluent::lint_mismatched_lifetime_syntaxes_suggestion_implicit, + inline_fluent!("remove the lifetime name from references"), suggestions, applicability(optional_alternative), style(optional_alternative), @@ -3105,9 +3572,11 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { optional_alternative, } => { let message = if implicit_suggestions.is_empty() { - fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths + inline_fluent!("use `'_` for type paths") } else { - fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed + inline_fluent!( + "remove the lifetime name from references and use `'_` for type paths" + ) }; let implicit_suggestions = @@ -3126,9 +3595,8 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { Explicit { lifetime_name, suggestions, optional_alternative } => { diag.arg("lifetime_name", lifetime_name); - let msg = diag.eagerly_translate( - fluent::lint_mismatched_lifetime_syntaxes_suggestion_explicit, - ); + let msg = + diag.eagerly_translate(inline_fluent!("consistently use `{$lifetime_name}`")); diag.remove_arg("lifetime_name"); diag.multipart_suggestion_with_style( msg, @@ -3142,78 +3610,107 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { } #[derive(LintDiagnostic)] -#[diag(lint_empty_attribute)] -#[note] +#[diag("unused attribute")] +#[note( + "{$valid_without_list -> + [true] using `{$attr_path}` with an empty list is equivalent to not using a list at all + *[other] using `{$attr_path}` with an empty list has no effect + }" +)] pub(crate) struct EmptyAttributeList { - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion( + "{$valid_without_list -> + [true] remove these parentheses + *[other] remove this attribute + }", + code = "", + applicability = "machine-applicable" + )] pub attr_span: Span, pub attr_path: String, pub valid_without_list: bool, } #[derive(LintDiagnostic)] -#[diag(lint_invalid_target)] -#[warning] -#[help] +#[diag("`#[{$name}]` attribute cannot be used on {$target}")] +#[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] +#[help("`#[{$name}]` can {$only}be applied to {$applied}")] pub(crate) struct InvalidTargetLint { pub name: String, pub target: &'static str, pub applied: DiagArgValue, pub only: &'static str, - #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] + #[suggestion( + "remove the attribute", + code = "", + applicability = "machine-applicable", + style = "tool-only" + )] pub attr_span: Span, } #[derive(LintDiagnostic)] -#[diag(lint_invalid_style)] +#[diag( + "{$is_used_as_inner -> + [false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]` + *[other] the `#![{$name}]` attribute can only be used at the crate root + }" +)] pub(crate) struct InvalidAttrStyle { pub name: String, pub is_used_as_inner: bool, - #[note] + #[note("this attribute does not have an `!`, which means it is applied to this {$target}")] pub target_span: Option, pub target: &'static str, } #[derive(LintDiagnostic)] -#[diag(lint_unused_duplicate)] +#[diag("unused attribute")] pub(crate) struct UnusedDuplicate { - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] pub this: Span, - #[note] + #[note("attribute also specified here")] pub other: Span, - #[warning] + #[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" + )] pub warning: bool, } #[derive(LintDiagnostic)] -#[diag(lint_malformed_doc)] -#[warning] +#[diag("malformed `doc` attribute input")] +#[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] pub(crate) struct MalformedDoc; #[derive(LintDiagnostic)] -#[diag(lint_expected_no_args)] -#[warning] +#[diag("didn't expect any arguments here")] +#[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] pub(crate) struct ExpectedNoArgs; #[derive(LintDiagnostic)] -#[diag(lint_expected_name_value)] -#[warning] +#[diag("expected this to be of the form `... = \"...\"`")] +#[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] pub(crate) struct ExpectedNameValue; #[derive(LintDiagnostic)] -#[diag(lint_unsafe_attr_outside_unsafe)] +#[diag("unsafe attribute used without unsafe")] pub(crate) struct UnsafeAttrOutsideUnsafeLint { - #[label] + #[label("usage of unsafe attribute")] pub span: Span, #[subdiagnostic] pub suggestion: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion( - lint_unsafe_attr_outside_unsafe_suggestion, - applicability = "machine-applicable" -)] +#[multipart_suggestion("wrap the attribute in `unsafe(...)`", applicability = "machine-applicable")] pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { #[suggestion_part(code = "unsafe(")] pub left: Span, @@ -3222,121 +3719,161 @@ pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { } #[derive(LintDiagnostic)] -#[diag(lint_unused_visibilities)] -#[note] +#[diag("visibility qualifiers have no effect on `const _` declarations")] +#[note("`const _` does not declare a name, so there is nothing for the qualifier to apply to")] pub(crate) struct UnusedVisibility { - #[suggestion(style = "short", code = "", applicability = "machine-applicable")] + #[suggestion( + "remove the qualifier", + style = "short", + code = "", + applicability = "machine-applicable" + )] pub span: Span, } #[derive(LintDiagnostic)] -#[diag(lint_doc_alias_duplicated)] +#[diag("doc alias is duplicated")] pub(crate) struct DocAliasDuplicated { - #[label] + #[label("first defined here")] pub first_defn: Span, } #[derive(LintDiagnostic)] -#[diag(lint_doc_auto_cfg_expects_hide_or_show)] +#[diag("only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`")] pub(crate) struct DocAutoCfgExpectsHideOrShow; #[derive(LintDiagnostic)] -#[diag(lint_doc_auto_cfg_hide_show_unexpected_item)] +#[diag("there exists a built-in attribute with the same name")] +pub(crate) struct AmbiguousDeriveHelpers; + +#[derive(LintDiagnostic)] +#[diag("`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items")] pub(crate) struct DocAutoCfgHideShowUnexpectedItem { pub attr_name: Symbol, } #[derive(LintDiagnostic)] -#[diag(lint_doc_auto_cfg_hide_show_expects_list)] +#[diag("`#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items")] pub(crate) struct DocAutoCfgHideShowExpectsList { pub attr_name: Symbol, } #[derive(LintDiagnostic)] -#[diag(lint_doc_invalid)] +#[diag("invalid `doc` attribute")] pub(crate) struct DocInvalid; #[derive(LintDiagnostic)] -#[diag(lint_doc_unknown_include)] +#[diag("unknown `doc` attribute `include`")] pub(crate) struct DocUnknownInclude { pub inner: &'static str, pub value: Symbol, - #[suggestion(code = "#{inner}[doc = include_str!(\"{value}\")]")] + #[suggestion( + "use `doc = include_str!` instead", + code = "#{inner}[doc = include_str!(\"{value}\")]" + )] pub sugg: (Span, Applicability), } #[derive(LintDiagnostic)] -#[diag(lint_doc_unknown_spotlight)] -#[note] -#[note(lint_no_op_note)] +#[diag("unknown `doc` attribute `spotlight`")] +#[note("`doc(spotlight)` was renamed to `doc(notable_trait)`")] +#[note("`doc(spotlight)` is now a no-op")] pub(crate) struct DocUnknownSpotlight { - #[suggestion(style = "short", applicability = "machine-applicable", code = "notable_trait")] + #[suggestion( + "use `notable_trait` instead", + style = "short", + applicability = "machine-applicable", + code = "notable_trait" + )] pub sugg_span: Span, } #[derive(LintDiagnostic)] -#[diag(lint_doc_unknown_passes)] -#[note] -#[note(lint_no_op_note)] +#[diag("unknown `doc` attribute `{$name}`")] +#[note( + "`doc` attribute `{$name}` no longer functions; see issue #44136 " +)] +#[note("`doc({$name})` is now a no-op")] pub(crate) struct DocUnknownPasses { pub name: Symbol, - #[label] + #[label("no longer functions")] pub note_span: Span, } #[derive(LintDiagnostic)] -#[diag(lint_doc_unknown_plugins)] -#[note] -#[note(lint_no_op_note)] +#[diag("unknown `doc` attribute `plugins`")] +#[note( + "`doc` attribute `plugins` no longer functions; see issue #44136 and CVE-2018-1000622 " +)] +#[note("`doc(plugins)` is now a no-op")] pub(crate) struct DocUnknownPlugins { - #[label] + #[label("no longer functions")] pub label_span: Span, } #[derive(LintDiagnostic)] -#[diag(lint_doc_unknown_any)] +#[diag("unknown `doc` attribute `{$name}`")] pub(crate) struct DocUnknownAny { pub name: Symbol, } #[derive(LintDiagnostic)] -#[diag(lint_doc_auto_cfg_wrong_literal)] +#[diag("expected boolean for `#[doc(auto_cfg = ...)]`")] pub(crate) struct DocAutoCfgWrongLiteral; #[derive(LintDiagnostic)] -#[diag(lint_doc_test_takes_list)] +#[diag("`#[doc(test(...)]` takes a list of attributes")] pub(crate) struct DocTestTakesList; #[derive(LintDiagnostic)] -#[diag(lint_doc_test_unknown)] +#[diag("unknown `doc(test)` attribute `{$name}`")] pub(crate) struct DocTestUnknown { pub name: Symbol, } #[derive(LintDiagnostic)] -#[diag(lint_doc_test_literal)] +#[diag("`#![doc(test(...)]` does not take a literal")] pub(crate) struct DocTestLiteral; #[derive(LintDiagnostic)] -#[diag(lint_attr_crate_level)] -#[note] +#[diag("this attribute can only be applied at the crate level")] +#[note( + "read for more information" +)] pub(crate) struct AttrCrateLevelOnly; #[derive(LintDiagnostic)] -#[diag(lint_incorrect_do_not_recommend_args)] +#[diag("`#[diagnostic::do_not_recommend]` does not expect any arguments")] pub(crate) struct DoNotRecommendDoesNotExpectArgs; #[derive(LintDiagnostic)] -#[diag(lint_invalid_crate_type_value)] +#[diag("invalid `crate_type` value")] pub(crate) struct UnknownCrateTypes { #[subdiagnostic] pub sugg: Option, } #[derive(Subdiagnostic)] -#[suggestion(lint_suggestion, code = r#""{snippet}""#, applicability = "maybe-incorrect")] +#[suggestion("did you mean", code = r#""{snippet}""#, applicability = "maybe-incorrect")] pub(crate) struct UnknownCrateTypesSuggestion { #[primary_span] pub span: Span, pub snippet: Symbol, } + +#[derive(LintDiagnostic)] +#[diag("unreachable configuration predicate")] +pub(crate) struct UnreachableCfgSelectPredicate { + #[label("this configuration predicate is never reached")] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag("unreachable configuration predicate")] +pub(crate) struct UnreachableCfgSelectPredicateWildcard { + #[label("this configuration predicate is never reached")] + pub span: Span, + + #[label("always matches")] + pub wildcard_span: Span, +} diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index a07c3b372d34..daf62277693a 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -1,5 +1,5 @@ use rustc_ast as ast; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, inline_fluent}; use rustc_hir::{self as hir, LangItem}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::{bug, ty}; @@ -10,7 +10,7 @@ use rustc_span::{InnerSpan, Span, Symbol, hygiene, sym}; use rustc_trait_selection::infer::InferCtxtExt; use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused}; -use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; +use crate::{LateContext, LateLintPass, LintContext}; declare_lint! { /// The `non_fmt_panics` lint detects `panic!(..)` invocations where the first @@ -121,20 +121,20 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc } cx.span_lint(NON_FMT_PANICS, arg_span, |lint| { - lint.primary_message(fluent::lint_non_fmt_panic); + lint.primary_message(inline_fluent!("panic message is not a string literal")); lint.arg("name", symbol); - lint.note(fluent::lint_note); - lint.note(fluent::lint_more_info_note); + lint.note(inline_fluent!("this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021")); + lint.note(inline_fluent!("for more information, see ")); if !is_arg_inside_call(arg_span, span) { // No clue where this argument is coming from. return; } if arg_macro.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) { // A case of `panic!(format!(..))`. - lint.note(fluent::lint_supports_fmt_note); + lint.note(inline_fluent!("the `{$name}!()` macro supports formatting, so there's no need for the `format!()` macro here")); if let Some((open, close, _)) = find_delimiters(cx, arg_span) { lint.multipart_suggestion( - fluent::lint_supports_fmt_suggestion, + inline_fluent!("remove the `format!(..)` macro call"), vec![ (arg_span.until(open.shrink_to_hi()), "".into()), (close.until(arg_span.shrink_to_hi()), "".into()), @@ -178,7 +178,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc if suggest_display { lint.span_suggestion_verbose( arg_span.shrink_to_lo(), - fluent::lint_display_suggestion, + inline_fluent!(r#"add a "{"{"}{"}"}" format string to `Display` the message"#), "\"{}\", ", fmt_applicability, ); @@ -186,7 +186,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc lint.arg("ty", ty); lint.span_suggestion_verbose( arg_span.shrink_to_lo(), - fluent::lint_debug_suggestion, + inline_fluent!(r#"add a "{"{"}:?{"}"}" format string to use the `Debug` implementation of `{$ty}`"#), "\"{:?}\", ", fmt_applicability, ); @@ -196,7 +196,10 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc if let Some((open, close, del)) = find_delimiters(cx, span) { lint.arg("already_suggested", suggest_display || suggest_debug); lint.multipart_suggestion( - fluent::lint_panic_suggestion, + inline_fluent!("{$already_suggested -> + [true] or use + *[false] use + } std::panic::panic_any instead"), if del == '(' { vec![(span.until(open), "std::panic::panic_any".into())] } else { diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index bcf4d9ec3b29..fc7e5a821b02 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,4 +1,4 @@ -use rustc_errors::MultiSpan; +use rustc_errors::{MultiSpan, inline_fluent}; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; @@ -9,7 +9,7 @@ use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::{ExpnKind, Span, kw}; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; -use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; +use crate::{LateContext, LateLintPass, LintContext}; declare_lint! { /// The `non_local_definitions` lint checks for `impl` blocks and `#[macro_export]` @@ -210,7 +210,12 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { if !doctest { ms.push_span_label( cx.tcx.def_span(parent), - fluent::lint_non_local_definitions_impl_move_help, + inline_fluent!( + "move the `impl` block outside of this {$body_kind_descr} {$depth -> + [one] `{$body_name}` + *[other] `{$body_name}` and up {$depth} bodies + }" + ), ); } diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 3f2ca92a021a..b5b57eaa3389 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -202,11 +202,11 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { } #[derive(LintDiagnostic)] -#[diag(lint_opaque_hidden_inferred_bound)] +#[diag("opaque type `{$ty}` does not satisfy its associated type bounds")] struct OpaqueHiddenInferredBoundLint<'tcx> { ty: Ty<'tcx>, proj_ty: Ty<'tcx>, - #[label(lint_specifically)] + #[label("this associated type bound is unsatisfied for `{$proj_ty}`")] assoc_pred_span: Span, #[subdiagnostic] add_bound: Option>, @@ -214,7 +214,7 @@ struct OpaqueHiddenInferredBoundLint<'tcx> { #[derive(Subdiagnostic)] #[suggestion( - lint_opaque_hidden_inferred_bound_sugg, + "add this bound", style = "verbose", applicability = "machine-applicable", code = " + {trait_ref}" diff --git a/compiler/rustc_lint/src/transmute.rs b/compiler/rustc_lint/src/transmute.rs index 6bc4617eb2dc..e4716c869c5f 100644 --- a/compiler/rustc_lint/src/transmute.rs +++ b/compiler/rustc_lint/src/transmute.rs @@ -369,8 +369,10 @@ fn check_unnecessary_transmute<'tcx>( } #[derive(LintDiagnostic)] -#[diag(lint_undefined_transmute)] -#[note] -#[note(lint_note2)] -#[help] +#[diag("pointers cannot be transmuted to integers during const eval")] +#[note("at compile-time, pointers do not have an integer value")] +#[note( + "avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior" +)] +#[help("for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html")] pub(crate) struct UndefinedTransmuteLint; diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index f3e6db6f2d8e..47e1fef8b82e 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -746,24 +746,23 @@ fn pat_ty_is_known_nonnull<'tcx>( typing_env: ty::TypingEnv<'tcx>, pat: ty::Pattern<'tcx>, ) -> bool { - Option::unwrap_or_default( - try { - match *pat { - ty::PatternKind::Range { start, end } => { - let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?; - let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?; + try { + match *pat { + ty::PatternKind::Range { start, end } => { + let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?; + let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?; - // This also works for negative numbers, as we just need - // to ensure we aren't wrapping over zero. - start > 0 && end >= start - } - ty::PatternKind::NotNull => true, - ty::PatternKind::Or(patterns) => { - patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat)) - } + // This also works for negative numbers, as we just need + // to ensure we aren't wrapping over zero. + start > 0 && end >= start } - }, - ) + ty::PatternKind::NotNull => true, + ty::PatternKind::Or(patterns) => { + patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat)) + } + } + } + .unwrap_or_default() } /// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type. diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 38094c67c34a..fb9b55efa220 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -4,7 +4,7 @@ use std::ops::ControlFlow; use bitflags::bitflags; use rustc_abi::VariantIdx; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::DiagMessage; +use rustc_errors::{DiagMessage, inline_fluent}; use rustc_hir::def::CtorKind; use rustc_hir::intravisit::VisitorExt; use rustc_hir::{self as hir, AmbigArg}; @@ -21,7 +21,7 @@ use tracing::debug; use super::repr_nullable_ptr; use crate::lints::{ImproperCTypes, UsesPowerAlignment}; -use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; +use crate::{LateContext, LateLintPass, LintContext}; declare_lint! { /// The `improper_ctypes` lint detects incorrect use of types in foreign @@ -158,12 +158,12 @@ pub(crate) fn check_non_exhaustive_variant( // with an enum like `#[repr(u8)] enum Enum { A(DataA), B(DataB), }` // but exempt enums with unit ctors like C's (e.g. from rust-bindgen) if variant_has_complex_ctor(variant) { - return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive); + return ControlFlow::Break(inline_fluent!("this enum is non-exhaustive")); } } if variant.field_list_has_applicable_non_exhaustive() { - return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant); + return ControlFlow::Break(inline_fluent!("this enum has non-exhaustive variants")); } ControlFlow::Continue(()) @@ -424,7 +424,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if all_phantom { FfiPhantom(ty) } else if transparent_with_all_zst_fields { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } + FfiUnsafe { + ty, + reason: inline_fluent!("this struct contains only zero-sized fields"), + help: None, + } } else { FfiSafe } @@ -460,7 +464,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } else { return FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_box, + reason: inline_fluent!("box cannot be represented as a single pointer"), help: None, }; } @@ -476,8 +480,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { { return FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_cstr_reason, - help: Some(fluent::lint_improper_ctypes_cstr_help), + reason: inline_fluent!( + "`CStr`/`CString` do not have a guaranteed layout" + ), + help: Some(inline_fluent!( + "consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`" + )), }; } @@ -485,14 +493,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return FfiUnsafe { ty, reason: if def.is_struct() { - fluent::lint_improper_ctypes_struct_layout_reason + inline_fluent!("this struct has unspecified layout") } else { - fluent::lint_improper_ctypes_union_layout_reason + inline_fluent!("this union has unspecified layout") }, help: if def.is_struct() { - Some(fluent::lint_improper_ctypes_struct_layout_help) + Some(inline_fluent!( + "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct" + )) } else { - Some(fluent::lint_improper_ctypes_union_layout_help) + Some(inline_fluent!( + "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union" + )) }, }; } @@ -501,9 +513,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return FfiUnsafe { ty, reason: if def.is_struct() { - fluent::lint_improper_ctypes_struct_non_exhaustive + inline_fluent!("this struct is non-exhaustive") } else { - fluent::lint_improper_ctypes_union_non_exhaustive + inline_fluent!("this union is non-exhaustive") }, help: None, }; @@ -513,14 +525,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return FfiUnsafe { ty, reason: if def.is_struct() { - fluent::lint_improper_ctypes_struct_fieldless_reason + inline_fluent!("this struct has no fields") } else { - fluent::lint_improper_ctypes_union_fieldless_reason + inline_fluent!("this union has no fields") }, help: if def.is_struct() { - Some(fluent::lint_improper_ctypes_struct_fieldless_help) + Some(inline_fluent!("consider adding a member to this struct")) } else { - Some(fluent::lint_improper_ctypes_union_fieldless_help) + Some(inline_fluent!("consider adding a member to this union")) }, }; } @@ -545,8 +557,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_enum_repr_reason, - help: Some(fluent::lint_improper_ctypes_enum_repr_help), + reason: inline_fluent!("enum has no representation hint"), + help: Some(inline_fluent!( + "consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum" + )), }; } @@ -572,8 +586,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Char => FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_char_reason, - help: Some(fluent::lint_improper_ctypes_char_help), + reason: inline_fluent!("the `char` type has no C equivalent"), + help: Some(inline_fluent!("consider using `u32` or `libc::wchar_t` instead")), }, // It's just extra invariants on the type that you need to uphold, @@ -585,24 +599,26 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Slice(_) => FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_slice_reason, - help: Some(fluent::lint_improper_ctypes_slice_help), + reason: inline_fluent!("slices have no C equivalent"), + help: Some(inline_fluent!("consider using a raw pointer instead")), }, - ty::Dynamic(..) => { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None } - } + ty::Dynamic(..) => FfiUnsafe { + ty, + reason: inline_fluent!("trait objects have no C equivalent"), + help: None, + }, ty::Str => FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_str_reason, - help: Some(fluent::lint_improper_ctypes_str_help), + reason: inline_fluent!("string slices have no C equivalent"), + help: Some(inline_fluent!("consider using `*const u8` and a length instead")), }, ty::Tuple(..) => FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_tuple_reason, - help: Some(fluent::lint_improper_ctypes_tuple_help), + reason: inline_fluent!("tuples have unspecified layout"), + help: Some(inline_fluent!("consider using a struct instead")), }, ty::RawPtr(ty, _) | ty::Ref(_, ty, _) @@ -632,8 +648,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if sig.abi().is_rustic_abi() { return FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_fnptr_reason, - help: Some(fluent::lint_improper_ctypes_fnptr_help), + reason: inline_fluent!( + "this function pointer has Rust-specific calling convention" + ), + help: Some(inline_fluent!( + "consider using an `extern fn(...) -> ...` function pointer instead" + )), }; } @@ -657,9 +677,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // While opaque types are checked for earlier, if a projection in a struct field // normalizes to an opaque type, then it will reach this branch. - ty::Alias(ty::Opaque, ..) => { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None } - } + ty::Alias(ty::Opaque, ..) => FfiUnsafe { + ty, + reason: inline_fluent!("opaque types have no C equivalent"), + help: None, + }, // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, // so they are currently ignored for the purposes of this lint. @@ -669,9 +691,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe } - ty::UnsafeBinder(_) => { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_unsafe_binder, help: None } - } + ty::UnsafeBinder(_) => FfiUnsafe { + ty, + reason: inline_fluent!( + "unsafe binders are incompatible with foreign function interfaces" + ), + help: None, + }, ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) @@ -715,7 +741,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { { Some(FfiResult::FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_opaque, + reason: inline_fluent!("opaque types have no C equivalent"), help: None, }) } else { @@ -728,8 +754,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if let ty::Array(..) = ty.kind() { Some(FfiResult::FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_array_reason, - help: Some(fluent::lint_improper_ctypes_array_help), + reason: inline_fluent!("passing raw arrays by value is not FFI-safe"), + help: Some(inline_fluent!("consider passing a pointer to the array")), }) } else { None @@ -908,7 +934,7 @@ impl<'tcx> ImproperCTypesLint { cx, ty, sp, - fluent::lint_improper_ctypes_only_phantomdata, + inline_fluent!("composed only of `PhantomData`"), None, fn_mode, ); diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 51d9a4641cd5..8b2a6d1d2ab5 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1058,8 +1058,8 @@ trait UnusedDelimLint { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { use ast::ItemKind::*; - let expr = if let Const(box ast::ConstItem { rhs: Some(rhs), .. }) = &item.kind { - rhs.expr() + let expr = if let Const(box ast::ConstItem { rhs_kind, .. }) = &item.kind { + if let Some(e) = rhs_kind.expr() { e } else { return } } else if let Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind { expr } else { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index f4e6e93356c7..9e80ddbd551f 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -17,6 +17,7 @@ declare_lint_pass! { AARCH64_SOFTFLOAT_NEON, ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_ASSOCIATED_ITEMS, + AMBIGUOUS_DERIVE_HELPERS, AMBIGUOUS_GLOB_IMPORTED_TRAITS, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_GLOB_REEXPORTS, @@ -123,6 +124,7 @@ declare_lint_pass! { UNKNOWN_LINTS, UNNAMEABLE_TEST_ITEMS, UNNAMEABLE_TYPES, + UNREACHABLE_CFG_SELECT_PREDICATES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, UNSAFE_ATTR_OUTSIDE_UNSAFE, @@ -855,6 +857,34 @@ declare_lint! { "detects unreachable patterns" } +declare_lint! { + /// The `unreachable_cfg_select_predicates` lint detects unreachable configuration + /// predicates in the `cfg_select!` macro. + /// + /// ### Example + /// + /// ```rust + /// #![feature(cfg_select)] + /// cfg_select! { + /// _ => (), + /// windows => (), + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This usually indicates a mistake in how the predicates are specified or + /// ordered. In this example, the `_` predicate will always match, so the + /// `windows` is impossible to reach. Remember, arms match in order, you + /// probably wanted to put the `windows` case above the `_` case. + pub UNREACHABLE_CFG_SELECT_PREDICATES, + Warn, + "detects unreachable configuration predicates in the cfg_select macro", + @feature_gate = cfg_select; +} + declare_lint! { /// The `overlapping_range_endpoints` lint detects `match` arms that have [range patterns] that /// overlap on their endpoints. @@ -4238,6 +4268,75 @@ declare_lint! { }; } +declare_lint! { + /// The `ambiguous_derive_helpers` lint detects cases where a derive macro's helper attribute + /// is the same name as that of a built-in attribute. + /// + /// ### Example + /// + /// ```rust,ignore (proc-macro) + /// #![crate_type = "proc-macro"] + /// #![deny(ambiguous_derive_helpers)] + /// + /// use proc_macro::TokenStream; + /// + /// #[proc_macro_derive(Trait, attributes(ignore))] + /// pub fn example(input: TokenStream) -> TokenStream { + /// TokenStream::new() + /// } + /// ``` + /// + /// Produces: + /// + /// ```text + /// warning: there exists a built-in attribute with the same name + /// --> file.rs:5:39 + /// | + /// 5 | #[proc_macro_derive(Trait, attributes(ignore))] + /// | ^^^^^^ + /// | + /// = 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 #151152 + /// = note: `#[deny(ambiguous_derive_helpers)]` (part of `#[deny(future_incompatible)]`) on by default + /// ``` + /// + /// ### Explanation + /// + /// Attempting to use this helper attribute will throw an error: + /// + /// ```rust,ignore (needs-dependency) + /// #[derive(Trait)] + /// struct Example { + /// #[ignore] + /// fields: () + /// } + /// ``` + /// + /// Produces: + /// + /// ```text + /// error[E0659]: `ignore` is ambiguous + /// --> src/lib.rs:5:7 + /// | + /// 5 | #[ignore] + /// | ^^^^^^ ambiguous name + /// | + /// = note: ambiguous because of a name conflict with a builtin attribute + /// = note: `ignore` could refer to a built-in attribute + /// note: `ignore` could also refer to the derive helper attribute defined here + /// --> src/lib.rs:3:10 + /// | + /// 3 | #[derive(Trait)] + /// | ^^^^^ + /// ``` + pub AMBIGUOUS_DERIVE_HELPERS, + Warn, + "detects derive helper attributes that are ambiguous with built-in attributes", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #151276), + }; +} + declare_lint! { /// The `private_interfaces` lint detects types in a primary interface of an item, /// that are more private than the item itself. Primary interface of an item is all diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index b3e5b93cf2fc..a1b8b135819a 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -748,6 +748,10 @@ pub enum BuiltinLintDiag { }, UnusedVisibility(Span), AttributeLint(AttributeLintKind), + UnreachableCfg { + span: Span, + wildcard_span: Option, + }, } #[derive(Debug, HashStable_Generic)] @@ -796,6 +800,7 @@ pub enum AttributeLintKind { attr_name: Symbol, }, DocInvalid, + AmbiguousDeriveHelpers, DocUnknownInclude { span: Span, inner: &'static str, diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 97f95ac01e86..7aa4ddea78e1 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -812,12 +812,12 @@ extern "C" LLVMRustResult LLVMRustOptimize( auto ThinLTOBuffer = std::make_unique(); raw_string_ostream ThinLTODataOS(ThinLTOBuffer->data); raw_string_ostream ThinLinkDataOS(ThinLTOBuffer->thin_link_data); + bool IsLTO = OptStage == LLVMRustOptStage::ThinLTO || + OptStage == LLVMRustOptStage::FatLTO; if (!NoPrepopulatePasses) { // The pre-link pipelines don't support O0 and require using // buildO0DefaultPipeline() instead. At the same time, the LTO pipelines do // support O0 and using them is required. - bool IsLTO = OptStage == LLVMRustOptStage::ThinLTO || - OptStage == LLVMRustOptStage::FatLTO; if (OptLevel == OptimizationLevel::O0 && !IsLTO) { for (const auto &C : PipelineStartEPCallbacks) PB.registerPipelineStartEPCallback(C); @@ -908,7 +908,10 @@ extern "C" LLVMRustResult LLVMRustOptimize( // now load "-enzyme" pass: // With dlopen, ENZYME macro may not be defined, so check EnzymePtr directly - if (EnzymePtr) { + // In the case of debug builds with multiple codegen units, we might not + // have all function definitions available during the early compiler + // invocations. We therefore wait for the final lto step to run Enzyme. + if (EnzymePtr && IsLTO) { if (PrintBeforeEnzyme) { // Handle the Rust flag `-Zautodiff=PrintModBefore`. @@ -929,6 +932,7 @@ extern "C" LLVMRustResult LLVMRustOptimize( MPM.addPass(PrintModulePass(outs(), Banner, true, false)); } } + if (PrintPasses) { // Print all passes from the PM: std::string Pipeline; diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index e8356655dd9f..dc8231e5f0b0 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -1,7 +1,5 @@ #![deny(unused_must_use)] -use std::cell::RefCell; - use proc_macro2::TokenStream; use quote::quote; use synstructure::Structure; @@ -22,7 +20,6 @@ impl<'a> DiagnosticDerive<'a> { pub(crate) fn into_tokens(self) -> TokenStream { let DiagnosticDerive { mut structure } = self; let kind = DiagnosticDeriveKind::Diagnostic; - let messages = RefCell::new(Vec::new()); let implementation = kind.each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(variant); let body = builder.body(variant); @@ -30,7 +27,6 @@ impl<'a> DiagnosticDerive<'a> { let Some(message) = builder.primary_message() else { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; - messages.borrow_mut().push(message.clone()); let message = message.diag_message(Some(variant)); let init = quote! { @@ -52,9 +48,7 @@ impl<'a> DiagnosticDerive<'a> { }); // A lifetime of `'a` causes conflicts, but `_sess` is fine. - // FIXME(edition_2024): Fix the `keyword_idents_2024` lint to not trigger here? - #[allow(keyword_idents_2024)] - let mut imp = structure.gen_impl(quote! { + structure.gen_impl(quote! { gen impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for @Self where G: rustc_errors::EmissionGuarantee { @@ -67,11 +61,7 @@ impl<'a> DiagnosticDerive<'a> { #implementation } } - }); - for test in messages.borrow().iter().map(|s| s.generate_test(&structure)) { - imp.extend(test); - } - imp + }) } } @@ -88,7 +78,6 @@ impl<'a> LintDiagnosticDerive<'a> { pub(crate) fn into_tokens(self) -> TokenStream { let LintDiagnosticDerive { mut structure } = self; let kind = DiagnosticDeriveKind::LintDiagnostic; - let messages = RefCell::new(Vec::new()); let implementation = kind.each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(variant); let body = builder.body(variant); @@ -96,7 +85,6 @@ impl<'a> LintDiagnosticDerive<'a> { let Some(message) = builder.primary_message() else { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; - messages.borrow_mut().push(message.clone()); let message = message.diag_message(Some(variant)); let primary_message = quote! { diag.primary_message(#message); @@ -112,9 +100,7 @@ impl<'a> LintDiagnosticDerive<'a> { } }); - // FIXME(edition_2024): Fix the `keyword_idents_2024` lint to not trigger here? - #[allow(keyword_idents_2024)] - let mut imp = structure.gen_impl(quote! { + structure.gen_impl(quote! { gen impl<'__a> rustc_errors::LintDiagnostic<'__a, ()> for @Self { #[track_caller] fn decorate_lint<'__b>( @@ -124,11 +110,6 @@ impl<'a> LintDiagnosticDerive<'a> { #implementation; } } - }); - for test in messages.borrow().iter().map(|s| s.generate_test(&structure)) { - imp.extend(test); - } - - imp + }) } } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 6107b181eea2..de8ee42caf45 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -4,7 +4,7 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use syn::parse::ParseStream; use syn::spanned::Spanned; -use syn::{Attribute, LitStr, Meta, Path, Token, Type, parse_quote}; +use syn::{Attribute, LitStr, Meta, Path, Token, Type}; use synstructure::{BindingInfo, Structure, VariantInfo}; use super::utils::SubdiagnosticVariant; @@ -109,24 +109,14 @@ impl DiagnosticDeriveVariantBuilder { pub(crate) fn primary_message(&self) -> Option<&Message> { match self.message.as_ref() { None => { - span_err(self.span, "diagnostic slug not specified") + span_err(self.span, "diagnostic message not specified") .help( - "specify the slug as the first argument to the `#[diag(...)]` \ - attribute, such as `#[diag(hir_analysis_example_error)]`", + "specify the message as the first argument to the `#[diag(...)]` \ + attribute, such as `#[diag(\"Example error\")]`", ) .emit(); None } - Some(Message::Slug(slug)) - if let Some(Mismatch { slug_name, crate_name, slug_prefix }) = - Mismatch::check(slug) => - { - span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match") - .note(format!("slug is `{slug_name}` but the crate name is `{crate_name}`")) - .help(format!("expected a slug starting with `{slug_prefix}_...`")) - .emit(); - None - } Some(msg) => Some(msg), } } @@ -177,25 +167,15 @@ impl DiagnosticDeriveVariantBuilder { .help("consider creating a `Subdiagnostic` instead")); } - // For subdiagnostics without a message specified, insert a placeholder slug - let slug = subdiag.slug.unwrap_or_else(|| { - Message::Slug(match subdiag.kind { - SubdiagnosticKind::Label => parse_quote! { _subdiag::label }, - SubdiagnosticKind::Note => parse_quote! { _subdiag::note }, - SubdiagnosticKind::NoteOnce => parse_quote! { _subdiag::note_once }, - SubdiagnosticKind::Help => parse_quote! { _subdiag::help }, - SubdiagnosticKind::HelpOnce => parse_quote! { _subdiag::help_once }, - SubdiagnosticKind::Warn => parse_quote! { _subdiag::warn }, - SubdiagnosticKind::Suggestion { .. } => parse_quote! { _subdiag::suggestion }, - SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), - }) - }); + let Some(message) = subdiag.message else { + throw_invalid_attr!(attr, |diag| diag.help("subdiagnostic message is missing")) + }; - Ok(Some((subdiag.kind, slug, false))) + Ok(Some((subdiag.kind, message, false))) } /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct - /// attributes like `#[diag(..)]`, such as the slug and error code. Generates + /// attributes like `#[diag(..)]`, such as the message and error code. Generates /// diagnostic builder calls for setting error code and creating note/help messages. fn generate_structure_code_for_attr( &mut self, @@ -213,9 +193,6 @@ impl DiagnosticDeriveVariantBuilder { if name == "diag" { let mut tokens = TokenStream::new(); attr.parse_args_with(|input: ParseStream<'_>| { - let mut input = &*input; - let slug_recovery_point = input.fork(); - if input.peek(LitStr) { // Parse an inline message let message = input.parse::()?; @@ -226,15 +203,11 @@ impl DiagnosticDeriveVariantBuilder { ) .emit(); } - self.message = Some(Message::Inline(message.span(), message.value())); - } else { - // Parse a slug - let slug = input.parse::()?; - if input.is_empty() || input.peek(Token![,]) { - self.message = Some(Message::Slug(slug)); - } else { - input = &slug_recovery_point; - } + self.message = Some(Message { + attr_span: attr.span(), + message_span: message.span(), + value: message.value(), + }); } // Parse arguments @@ -248,7 +221,7 @@ impl DiagnosticDeriveVariantBuilder { if input.peek(Token![,]) { span_err( arg_name.span().unwrap(), - "diagnostic slug must be the first argument", + "diagnostic message must be the first argument", ) .emit(); continue; @@ -265,7 +238,7 @@ impl DiagnosticDeriveVariantBuilder { } _ => { span_err(arg_name.span().unwrap(), "unknown argument") - .note("only the `code` parameter is valid after the slug") + .note("only the `code` parameter is valid after the message") .emit(); } } @@ -276,7 +249,7 @@ impl DiagnosticDeriveVariantBuilder { return Ok(tokens); } - let Some((subdiag, slug, _no_span)) = self.parse_subdiag_attribute(attr)? else { + let Some((subdiag, message, _no_span)) = self.parse_subdiag_attribute(attr)? else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. return Ok(quote! {}); @@ -287,7 +260,7 @@ impl DiagnosticDeriveVariantBuilder { | SubdiagnosticKind::NoteOnce | SubdiagnosticKind::Help | SubdiagnosticKind::HelpOnce - | SubdiagnosticKind::Warn => Ok(self.add_subdiagnostic(&fn_ident, slug, variant)), + | SubdiagnosticKind::Warn => Ok(self.add_subdiagnostic(&fn_ident, message, variant)), SubdiagnosticKind::Label | SubdiagnosticKind::Suggestion { .. } => { throw_invalid_attr!(attr, |diag| diag .help("`#[label]` and `#[suggestion]` can only be applied to fields")); @@ -406,7 +379,7 @@ impl DiagnosticDeriveVariantBuilder { _ => (), } - let Some((subdiag, slug, _no_span)) = self.parse_subdiag_attribute(attr)? else { + let Some((subdiag, message, _no_span)) = self.parse_subdiag_attribute(attr)? else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. return Ok(quote! {}); @@ -415,7 +388,7 @@ impl DiagnosticDeriveVariantBuilder { match subdiag { SubdiagnosticKind::Label => { report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug, variant)) + Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, message, variant)) } SubdiagnosticKind::Note | SubdiagnosticKind::NoteOnce @@ -426,11 +399,11 @@ impl DiagnosticDeriveVariantBuilder { if type_matches_path(inner, &["rustc_span", "Span"]) || type_matches_path(inner, &["rustc_span", "MultiSpan"]) { - Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug, variant)) + Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, message, variant)) } else if type_is_unit(inner) || (matches!(info.ty, FieldInnerTy::Plain(_)) && type_is_bool(inner)) { - Ok(self.add_subdiagnostic(&fn_ident, slug, variant)) + Ok(self.add_subdiagnostic(&fn_ident, message, variant)) } else { report_type_error(attr, "`Span`, `MultiSpan`, `bool` or `()`")? } @@ -456,7 +429,7 @@ impl DiagnosticDeriveVariantBuilder { applicability.set_once(quote! { #static_applicability }, span); } - let message = slug.diag_message(Some(variant)); + let message = message.diag_message(Some(variant)); let applicability = applicability .value() .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); @@ -477,7 +450,7 @@ impl DiagnosticDeriveVariantBuilder { } } - /// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug + /// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current message /// and `fluent_attr_identifier`. fn add_spanned_subdiagnostic( &self, @@ -496,7 +469,7 @@ impl DiagnosticDeriveVariantBuilder { } } - /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug + /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current message /// and `fluent_attr_identifier`. fn add_subdiagnostic( &self, @@ -567,27 +540,3 @@ impl DiagnosticDeriveVariantBuilder { } } } - -struct Mismatch { - slug_name: String, - crate_name: String, - slug_prefix: String, -} - -impl Mismatch { - /// Checks whether the slug starts with the crate name it's in. - fn check(slug: &syn::Path) -> Option { - // If this is missing we're probably in a test, so bail. - let crate_name = std::env::var("CARGO_CRATE_NAME").ok()?; - - // If we're not in a "rustc_" crate, bail. - let Some(("rustc", slug_prefix)) = crate_name.split_once('_') else { return None }; - - let slug_name = slug.segments.first()?.ident.to_string(); - if slug_name.starts_with(slug_prefix) { - return None; - } - - Some(Mismatch { slug_name, slug_prefix: slug_prefix.to_string(), crate_name }) - } -} diff --git a/compiler/rustc_macros/src/diagnostics/message.rs b/compiler/rustc_macros/src/diagnostics/message.rs index 6c8aded89f16..3276abfce413 100644 --- a/compiler/rustc_macros/src/diagnostics/message.rs +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -2,15 +2,16 @@ use fluent_bundle::FluentResource; use fluent_syntax::ast::{Expression, InlineExpression, Pattern, PatternElement}; use proc_macro2::{Span, TokenStream}; use quote::quote; -use syn::Path; -use synstructure::{Structure, VariantInfo}; +use syn::ext::IdentExt; +use synstructure::VariantInfo; use crate::diagnostics::error::span_err; #[derive(Clone)] -pub(crate) enum Message { - Slug(Path), - Inline(Span, String), +pub(crate) struct Message { + pub attr_span: Span, + pub message_span: Span, + pub value: String, } impl Message { @@ -18,97 +19,46 @@ impl Message { /// The passed `variant` is used to check whether all variables in the message are used. /// For subdiagnostics, we cannot check this. pub(crate) fn diag_message(&self, variant: Option<&VariantInfo<'_>>) -> TokenStream { - match self { - Message::Slug(slug) => { - quote! { crate::fluent_generated::#slug } - } - Message::Inline(message_span, message) => { - if let Some(variant) = variant { - verify_fluent_message(*message_span, &message, variant); - } - quote! { rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed(#message)) } - } - } + let message = &self.value; + self.verify(variant); + quote! { rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed(#message)) } } - /// Generates a `#[test]` that verifies that all referenced variables - /// exist on this structure. - pub(crate) fn generate_test(&self, structure: &Structure<'_>) -> TokenStream { - match self { - Message::Slug(slug) => { - // FIXME: We can't identify variables in a subdiagnostic - for field in structure.variants().iter().flat_map(|v| v.ast().fields.iter()) { - for attr_name in field.attrs.iter().filter_map(|at| at.path().get_ident()) { - if attr_name == "subdiagnostic" { - return quote!(); - } - } - } - use std::sync::atomic::{AtomicUsize, Ordering}; - // We need to make sure that the same diagnostic slug can be used multiple times without - // causing an error, so just have a global counter here. - static COUNTER: AtomicUsize = AtomicUsize::new(0); - let slug = slug.get_ident().unwrap(); - let ident = quote::format_ident!( - "verify_{slug}_{}", - COUNTER.fetch_add(1, Ordering::Relaxed) - ); - let ref_slug = quote::format_ident!("{slug}_refs"); - let struct_name = &structure.ast().ident; - let variables: Vec<_> = structure - .variants() - .iter() - .flat_map(|v| { - v.ast() - .fields - .iter() - .filter_map(|f| f.ident.as_ref().map(|i| i.to_string())) - }) - .collect(); - // tidy errors on `#[test]` outside of test files, so we use `#[test ]` to work around this - quote! { - #[cfg(test)] - #[test ] - fn #ident() { - let variables = [#(#variables),*]; - for vref in crate::fluent_generated::#ref_slug { - assert!(variables.contains(vref), "{}: variable `{vref}` not found ({})", stringify!(#struct_name), stringify!(#slug)); - } - } - } - } - Message::Inline(..) => { - // We don't generate a test for inline diagnostics, we can verify these at compile-time! - // This verification is done in the `diag_message` function above - quote! {} - } - } + fn verify(&self, variant: Option<&VariantInfo<'_>>) { + verify_variables_used(self.message_span, &self.value, variant); + verify_message_style(self.message_span, &self.value); + verify_message_formatting(self.attr_span, self.message_span, &self.value); } } -fn verify_fluent_message(msg_span: Span, message: &str, variant: &VariantInfo<'_>) { +fn verify_variables_used(msg_span: Span, message_str: &str, variant: Option<&VariantInfo<'_>>) { // Parse the fluent message const GENERATED_MSG_ID: &str = "generated_msg"; - let resource = FluentResource::try_new(format!("{GENERATED_MSG_ID} = {message}\n")).unwrap(); + let resource = + FluentResource::try_new(format!("{GENERATED_MSG_ID} = {message_str}\n")).unwrap(); assert_eq!(resource.entries().count(), 1); let Some(fluent_syntax::ast::Entry::Message(message)) = resource.get_entry(0) else { panic!("Did not parse into a message") }; // Check if all variables are used - let fields: Vec = variant - .bindings() - .iter() - .flat_map(|b| b.ast().ident.as_ref()) - .map(|id| id.to_string()) - .collect(); - for variable in variable_references(&message) { - if !fields.iter().any(|f| f == variable) { - span_err(msg_span.unwrap(), format!("Variable `{variable}` not found in diagnostic ")) + if let Some(variant) = variant { + let fields: Vec = variant + .bindings() + .iter() + .flat_map(|b| b.ast().ident.as_ref()) + .map(|id| id.unraw().to_string()) + .collect(); + for variable in variable_references(&message) { + if !fields.iter().any(|f| f == variable) { + span_err( + msg_span.unwrap(), + format!("Variable `{variable}` not found in diagnostic "), + ) .help(format!("Available fields: {:?}", fields.join(", "))) .emit(); + } } - // assert!(, ); } } @@ -136,3 +86,68 @@ fn variable_references<'a>(msg: &fluent_syntax::ast::Message<&'a str>) -> Vec<&' } refs } + +const ALLOWED_CAPITALIZED_WORDS: &[&str] = &[ + // tidy-alphabetical-start + "ABI", + "ABIs", + "ADT", + "C-variadic", + "CGU-reuse", + "Cargo", + "Ferris", + "GCC", + "MIR", + "NaNs", + "OK", + "Rust", + "Unicode", + "VS", + // tidy-alphabetical-end +]; + +/// See: https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-output-style-guide +fn verify_message_style(msg_span: Span, message: &str) { + // Verify that message starts with lowercase char + let Some(first_word) = message.split_whitespace().next() else { + span_err(msg_span.unwrap(), "message must not be empty").emit(); + return; + }; + let first_char = first_word.chars().next().expect("Word is not empty"); + if first_char.is_uppercase() && !ALLOWED_CAPITALIZED_WORDS.contains(&first_word) { + span_err(msg_span.unwrap(), "message `{value}` starts with an uppercase letter. Fix it or add it to `ALLOWED_CAPITALIZED_WORDS`").emit(); + return; + } + + // Verify that message does not end in `.` + if message.ends_with(".") && !message.ends_with("...") { + span_err(msg_span.unwrap(), "message `{value}` ends with a period").emit(); + return; + } +} + +/// Verifies that the message is properly indented into the code +fn verify_message_formatting(attr_span: Span, msg_span: Span, message: &str) { + // Find the indent at the start of the message (`column()` is one-indexed) + let start = attr_span.unwrap().column() - 1; + + for line in message.lines().skip(1) { + if line.is_empty() { + continue; + } + let indent = line.chars().take_while(|c| *c == ' ').count(); + if indent < start { + span_err( + msg_span.unwrap(), + format!("message is not properly indented. {indent} < {start}"), + ) + .emit(); + return; + } + if indent % 4 != 0 { + span_err(msg_span.unwrap(), "message is not indented with a multiple of 4 spaces") + .emit(); + return; + } + } +} diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 09f05ce972f1..a036e61c3789 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -21,36 +21,25 @@ use synstructure::Structure; /// # extern crate rust_middle; /// # use rustc_middle::ty::Ty; /// #[derive(Diagnostic)] -/// #[diag(borrowck_move_out_of_borrow, code = E0505)] -/// pub struct MoveOutOfBorrowError<'tcx> { +/// #[diag("this is an example message", code = E0123)] +/// pub(crate) struct ExampleError<'tcx> { /// pub name: Ident, /// pub ty: Ty<'tcx>, /// #[primary_span] -/// #[label] +/// #[label("with a label")] /// pub span: Span, -/// #[label(first_borrow_label)] -/// pub first_borrow_span: Span, -/// #[suggestion(code = "{name}.clone()")] -/// pub clone_sugg: Option<(Span, Applicability)> +/// #[label("with a label")] +/// pub other_span: Span, +/// #[suggestion("with a suggestion", code = "{name}.clone()")] +/// pub opt_sugg: Option<(Span, Applicability)>, /// } /// ``` /// -/// ```fluent -/// move_out_of_borrow = cannot move out of {$name} because it is borrowed -/// .label = cannot move out of borrow -/// .first_borrow_label = `{$ty}` first borrowed here -/// .suggestion = consider cloning here -/// ``` -/// /// Then, later, to emit the error: /// /// ```ignore (rust) -/// sess.emit_err(MoveOutOfBorrowError { -/// expected, -/// actual, -/// span, -/// first_borrow_span, -/// clone_sugg: Some(suggestion, Applicability::MachineApplicable), +/// sess.emit_err(ExampleError { +/// name, ty, span, other_span, opt_sugg /// }); /// ``` /// @@ -65,38 +54,24 @@ pub(super) fn diagnostic_derive(s: Structure<'_>) -> TokenStream { /// /// ```ignore (rust) /// #[derive(LintDiagnostic)] -/// #[diag(lint_atomic_ordering_invalid_fail_success)] -/// pub struct AtomicOrderingInvalidLint { -/// method: Symbol, -/// success_ordering: Symbol, -/// fail_ordering: Symbol, -/// #[label(fail_label)] -/// fail_order_arg_span: Span, -/// #[label(success_label)] -/// #[suggestion( -/// code = "std::sync::atomic::Ordering::{success_suggestion}", -/// applicability = "maybe-incorrect" +/// #[diag("unused attribute")] +/// pub(crate) struct UnusedAttribute { +/// #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] +/// pub this: Span, +/// #[note("attribute also specified here")] +/// pub other: Span, +/// #[warning( +/// "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" /// )] -/// success_order_arg_span: Span, +/// pub warning: bool, /// } /// ``` /// -/// ```fluent -/// lint_atomic_ordering_invalid_fail_success = `{$method}`'s success ordering must be at least as strong as its failure ordering -/// .fail_label = `{$fail_ordering}` failure ordering -/// .success_label = `{$success_ordering}` success ordering -/// .suggestion = consider using `{$success_suggestion}` success ordering instead -/// ``` -/// /// Then, later, to emit the error: /// /// ```ignore (rust) -/// cx.emit_span_lint(INVALID_ATOMIC_ORDERING, fail_order_arg_span, AtomicOrderingInvalidLint { -/// method, -/// success_ordering, -/// fail_ordering, -/// fail_order_arg_span, -/// success_order_arg_span, +/// cx.emit_span_lint(UNUSED_ATTRIBUTES, span, UnusedAttribute { +/// ... /// }); /// ``` /// @@ -112,45 +87,25 @@ pub(super) fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { /// /// ```ignore (rust) /// #[derive(Subdiagnostic)] -/// pub enum ExpectedIdentifierLabel<'tcx> { -/// #[label(expected_identifier)] -/// WithoutFound { -/// #[primary_span] -/// span: Span, -/// } -/// #[label(expected_identifier_found)] -/// WithFound { -/// #[primary_span] -/// span: Span, -/// found: String, -/// } -/// } -/// -/// #[derive(Subdiagnostic)] -/// #[suggestion(style = "verbose",parser::raw_identifier)] -/// pub struct RawIdentifierSuggestion<'tcx> { -/// #[primary_span] -/// span: Span, -/// #[applicability] -/// applicability: Applicability, -/// ident: Ident, +/// pub(crate) enum BuiltinUnusedDocCommentSub { +/// #[help("use `//` for a plain comment")] +/// PlainHelp, +/// #[help("use `/* */` for a plain comment")] +/// BlockHelp, /// } /// ``` -/// -/// ```fluent -/// parser_expected_identifier = expected identifier -/// -/// parser_expected_identifier_found = expected identifier, found {$found} -/// -/// parser_raw_identifier = escape `{$ident}` to use it as an identifier -/// ``` -/// -/// Then, later, to add the subdiagnostic: +/// Then, later, use the subdiagnostic in a diagnostic: /// /// ```ignore (rust) -/// diag.subdiagnostic(ExpectedIdentifierLabel::WithoutFound { span }); -/// -/// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident }); +/// #[derive(LintDiagnostic)] +/// #[diag("unused doc comment")] +/// pub(crate) struct BuiltinUnusedDocComment<'a> { +/// pub kind: &'a str, +/// #[label("rustdoc does not generate documentation for {$kind}")] +/// pub label: Span, +/// #[subdiagnostic] +/// pub sub: BuiltinUnusedDocCommentSub, +/// } /// ``` pub(super) fn subdiagnostic_derive(s: Structure<'_>) -> TokenStream { SubdiagnosticDerive::new().into_tokens(s) diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index ac1fa984664c..7eb170ec236a 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -186,10 +186,10 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { fn identify_kind( &mut self, ) -> Result, DiagnosticDeriveError> { - let mut kind_slugs = vec![]; + let mut kind_messages = vec![]; for attr in self.variant.ast().attrs { - let Some(SubdiagnosticVariant { kind, slug }) = + let Some(SubdiagnosticVariant { kind, message }) = SubdiagnosticVariant::from_attr(attr, &self.fields)? else { // Some attributes aren't errors - like documentation comments - but also aren't @@ -197,22 +197,22 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { continue; }; - let Some(slug) = slug else { + let Some(message) = message else { let name = attr.path().segments.last().unwrap().ident.to_string(); let name = name.as_str(); throw_span_err!( attr.span().unwrap(), format!( - "diagnostic slug must be first argument of a `#[{name}(...)]` attribute" + "diagnostic message must be first argument of a `#[{name}(...)]` attribute" ) ); }; - kind_slugs.push((kind, slug)); + kind_messages.push((kind, message)); } - Ok(kind_slugs) + Ok(kind_messages) } /// Generates the code for a field with no attributes. @@ -498,9 +498,9 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } pub(crate) fn into_tokens(&mut self) -> Result { - let kind_slugs = self.identify_kind()?; + let kind_messages = self.identify_kind()?; - let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect(); + let kind_stats: KindsStatistics = kind_messages.iter().map(|(kind, _msg)| kind).collect(); let init = if kind_stats.has_multipart_suggestion { quote! { let mut suggestions = Vec::new(); } @@ -516,7 +516,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { .map(|binding| self.generate_field_attr_code(binding, kind_stats)) .collect(); - if kind_slugs.is_empty() && !self.has_subdiagnostic { + if kind_messages.is_empty() && !self.has_subdiagnostic { if self.is_enum { // It's okay for a variant to not be a subdiagnostic at all.. return Ok(quote! {}); @@ -533,9 +533,9 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let diag = &self.parent.diag; let mut calls = TokenStream::new(); - for (kind, slug) in kind_slugs { + for (kind, messages) in kind_messages { let message = format_ident!("__message"); - let message_stream = slug.diag_message(None); + let message_stream = messages.diag_message(None); calls.extend(quote! { let #message = #diag.eagerly_translate(#message_stream); }); let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index a5265a847a9c..55a8445744cb 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -267,7 +267,7 @@ pub(super) type FieldMap = HashMap; /// /// ```ignore (not-usage-example) /// /// Suggest `==` when users wrote `===`. -/// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")] +/// #[suggestion("example message", code = "{lhs} == {rhs}")] /// struct NotJavaScriptEq { /// #[primary_span] /// span: Span, @@ -588,13 +588,13 @@ pub(super) enum SubdiagnosticKind { pub(super) struct SubdiagnosticVariant { pub(super) kind: SubdiagnosticKind, - pub(super) slug: Option, + pub(super) message: Option, } impl SubdiagnosticVariant { /// Constructs a `SubdiagnosticVariant` from a field or type attribute such as `#[note]`, - /// `#[error(parser::add_paren)]` or `#[suggestion(code = "...")]`. Returns the - /// `SubdiagnosticKind` and the diagnostic slug, if specified. + /// `#[error("add parenthesis")]` or `#[suggestion(code = "...")]`. Returns the + /// `SubdiagnosticKind` and the diagnostic message, if specified. pub(super) fn from_attr( attr: &Attribute, fields: &FieldMap, @@ -660,11 +660,11 @@ impl SubdiagnosticVariant { let list = match &attr.meta { Meta::List(list) => { // An attribute with properties, such as `#[suggestion(code = "...")]` or - // `#[error(some::slug)]` + // `#[error("message")]` list } Meta::Path(_) => { - // An attribute without a slug or other properties, such as `#[note]` - return + // An attribute without a message or other properties, such as `#[note]` - return // without further processing. // // Only allow this if there are no mandatory properties, such as `code = "..."` in @@ -677,7 +677,7 @@ impl SubdiagnosticVariant { | SubdiagnosticKind::HelpOnce | SubdiagnosticKind::Warn | SubdiagnosticKind::MultipartSuggestion { .. } => { - return Ok(Some(SubdiagnosticVariant { kind, slug: None })); + return Ok(Some(SubdiagnosticVariant { kind, message: None })); } SubdiagnosticKind::Suggestion { .. } => { throw_span_err!(span, "suggestion without `code = \"...\"`") @@ -692,45 +692,34 @@ impl SubdiagnosticVariant { let mut code = None; let mut suggestion_kind = None; - let mut slug = None; + let mut message = None; list.parse_args_with(|input: ParseStream<'_>| { let mut is_first = true; while !input.is_empty() { // Try to parse an inline diagnostic message if input.peek(LitStr) { - let message = input.parse::()?; - if !message.suffix().is_empty() { + let inline_message = input.parse::()?; + if !inline_message.suffix().is_empty() { span_err( - message.span().unwrap(), + inline_message.span().unwrap(), "Inline message is not allowed to have a suffix", ).emit(); } if !input.is_empty() { input.parse::()?; } if is_first { - slug = Some(Message::Inline(message.span(), message.value())); + message = Some(Message { attr_span: attr.span(), message_span: inline_message.span(), value: inline_message.value() }); is_first = false; } else { - span_err(message.span().unwrap(), "a diagnostic message must be the first argument to the attribute").emit(); - } - continue - } - - // Try to parse a slug instead - let arg_name: Path = input.parse::()?; - let arg_name_span = arg_name.span().unwrap(); - if input.is_empty() || input.parse::().is_ok() { - if is_first { - slug = Some(Message::Slug(arg_name)); - is_first = false; - } else { - span_err(arg_name_span, "a diagnostic slug must be the first argument to the attribute").emit(); + span_err(inline_message.span().unwrap(), "a diagnostic message must be the first argument to the attribute").emit(); } continue } is_first = false; // Try to parse an argument + let arg_name: Path = input.parse::()?; + let arg_name_span = arg_name.span().unwrap(); match (arg_name.require_ident()?.to_string().as_str(), &mut kind) { ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { let code_init = build_suggestion_code( @@ -836,7 +825,7 @@ impl SubdiagnosticVariant { | SubdiagnosticKind::Warn => {} } - Ok(Some(SubdiagnosticVariant { kind, slug })) + Ok(Some(SubdiagnosticVariant { kind, message })) } } diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 6b5c427c8bc1..5b869dc3409a 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -1,4 +1,5 @@ use proc_macro::TokenStream; +use proc_macro2::Span; use quote::{quote, quote_spanned}; use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; @@ -62,7 +63,7 @@ impl Parse for Query { // If there are no doc-comments, give at least some idea of what // it does by showing the query description. if doc_comments.is_empty() { - doc_comments.push(doc_comment_from_desc(&modifiers.desc.1)?); + doc_comments.push(doc_comment_from_desc(&modifiers.desc.expr_list)?); } Ok(Query { doc_comments, modifiers, name, key, arg, result }) @@ -82,15 +83,27 @@ impl Parse for List { } } +struct Desc { + modifier: Ident, + tcx_binding: Option, + expr_list: Punctuated, +} + +struct CacheOnDiskIf { + modifier: Ident, + tcx_binding: Option, + block: Block, +} + struct QueryModifiers { /// The description of the query. - desc: (Option, Punctuated), + desc: Desc, /// Use this type for the in-memory cache. arena_cache: Option, /// Cache the query to disk if the `Block` returns true. - cache_on_disk_if: Option<(Option, Block)>, + cache_on_disk_if: Option, /// A cycle error for this query aborting the compilation with a fatal error. cycle_fatal: Option, @@ -164,7 +177,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { // `desc { |tcx| "foo {}", tcx.item_path(key) }` let attr_content; braced!(attr_content in input); - let tcx = if attr_content.peek(Token![|]) { + let tcx_binding = if attr_content.peek(Token![|]) { attr_content.parse::()?; let tcx = attr_content.parse()?; attr_content.parse::()?; @@ -172,15 +185,15 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { } else { None }; - let list = attr_content.parse_terminated(Expr::parse, Token![,])?; - try_insert!(desc = (tcx, list)); + let expr_list = attr_content.parse_terminated(Expr::parse, Token![,])?; + try_insert!(desc = Desc { modifier, tcx_binding, expr_list }); } else if modifier == "cache_on_disk_if" { // Parse a cache-on-disk modifier like: // // `cache_on_disk_if { true }` // `cache_on_disk_if { key.is_local() }` // `cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) }` - let args = if input.peek(token::Paren) { + let tcx_binding = if input.peek(token::Paren) { let args; parenthesized!(args in input); let tcx = Pat::parse_single(&args)?; @@ -189,7 +202,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { None }; let block = input.parse()?; - try_insert!(cache_on_disk_if = (args, block)); + try_insert!(cache_on_disk_if = CacheOnDiskIf { modifier, tcx_binding, block }); } else if modifier == "arena_cache" { try_insert!(arena_cache = modifier); } else if modifier == "cycle_fatal" { @@ -275,7 +288,92 @@ struct HelperTokenStreams { } fn make_helpers_for_query(query: &Query, streams: &mut HelperTokenStreams) { - let Query { name, key, modifiers, .. } = &query; + let Query { name, key, modifiers, arg, .. } = &query; + + // Replace span for `name` to make rust-analyzer ignore it. + let mut erased_name = name.clone(); + erased_name.set_span(Span::call_site()); + + // Generate a function to check whether we should cache the query to disk, for some key. + if let Some(CacheOnDiskIf { tcx_binding, block, .. }) = modifiers.cache_on_disk_if.as_ref() { + let tcx = tcx_binding.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ }); + // we're taking `key` by reference, but some rustc types usually prefer being passed by value + streams.cache_on_disk_if_fns_stream.extend(quote! { + #[allow(unused_variables, rustc::pass_by_value)] + #[inline] + pub fn #erased_name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::queries::#name::Key<'tcx>) -> bool + #block + }); + } + + let Desc { tcx_binding, expr_list, .. } = &modifiers.desc; + let tcx = tcx_binding.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t }); + + let desc = quote! { + #[allow(unused_variables)] + pub fn #erased_name<'tcx>(tcx: TyCtxt<'tcx>, key: #arg) -> String { + let (#tcx, #key) = (tcx, key); + format!(#expr_list) + } + }; + + streams.description_fns_stream.extend(quote! { + #desc + }); +} + +/// Add hints for rust-analyzer +fn add_to_analyzer_stream(query: &Query, analyzer_stream: &mut proc_macro2::TokenStream) { + // Add links to relevant modifiers + + let modifiers = &query.modifiers; + + let mut modifiers_stream = quote! {}; + + let name = &modifiers.desc.modifier; + modifiers_stream.extend(quote! { + crate::query::modifiers::#name; + }); + + if let Some(CacheOnDiskIf { modifier, .. }) = &modifiers.cache_on_disk_if { + modifiers_stream.extend(quote! { + crate::query::modifiers::#modifier; + }); + } + + macro_rules! doc_link { + ( $( $modifier:ident ),+ $(,)? ) => { + $( + if let Some(name) = &modifiers.$modifier { + modifiers_stream.extend(quote! { + crate::query::modifiers::#name; + }); + } + )+ + } + } + + doc_link!( + arena_cache, + cycle_fatal, + cycle_delay_bug, + cycle_stash, + no_hash, + anon, + eval_always, + depth_limit, + separate_provide_extern, + feedable, + return_result_from_ensure_ok, + ); + + let name = &query.name; + + // Replace span for `name` to make rust-analyzer ignore it. + let mut erased_name = name.clone(); + erased_name.set_span(Span::call_site()); + + let result = &query.result; // This dead code exists to instruct rust-analyzer about the link between the `rustc_queries` // query names and the corresponding produced provider. The issue is that by nature of this @@ -289,35 +387,13 @@ fn make_helpers_for_query(query: &Query, streams: &mut HelperTokenStreams) { let crate::query::Providers { #name: _, .. }; }; - // Generate a function to check whether we should cache the query to disk, for some key. - if let Some((args, expr)) = modifiers.cache_on_disk_if.as_ref() { - let tcx = args.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ }); - // expr is a `Block`, meaning that `{ #expr }` gets expanded - // to `{ { stmts... } }`, which triggers the `unused_braces` lint. - // we're taking `key` by reference, but some rustc types usually prefer being passed by value - streams.cache_on_disk_if_fns_stream.extend(quote! { - #[allow(unused_variables, unused_braces, rustc::pass_by_value)] - #[inline] - pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::queries::#name::Key<'tcx>) -> bool { - #ra_hint - #expr - } - }); - } - - let (tcx, desc) = &modifiers.desc; - let tcx = tcx.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t }); - - let desc = quote! { - #[allow(unused_variables)] - pub fn #name<'tcx>(tcx: TyCtxt<'tcx>, key: crate::queries::#name::Key<'tcx>) -> String { - let (#tcx, #key) = (tcx, key); - format!(#desc) + analyzer_stream.extend(quote! { + #[inline(always)] + fn #erased_name<'tcx>() #result { + #ra_hint + #modifiers_stream + loop {} } - }; - - streams.description_fns_stream.extend(quote! { - #desc }); } @@ -327,6 +403,7 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { let mut query_stream = quote! {}; let mut helpers = HelperTokenStreams::default(); let mut feedable_queries = quote! {}; + let mut analyzer_stream = quote! {}; let mut errors = quote! {}; macro_rules! assert { @@ -409,6 +486,7 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { }); } + add_to_analyzer_stream(&query, &mut analyzer_stream); make_helpers_for_query(&query, &mut helpers); } @@ -442,6 +520,12 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { } } + // Add hints for rust-analyzer + mod _analyzer_hints { + use super::*; + #analyzer_stream + } + /// Functions that format a human-readable description of each query /// and its key, as specified by the `desc` query modifier. /// diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index d30e8120993f..a74d387ad5a4 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -68,8 +68,7 @@ use crate::creader::CStore; use crate::errors::{ BadPanicStrategy, CrateDepMultiple, IncompatiblePanicInDropStrategy, IncompatibleWithImmediateAbort, IncompatibleWithImmediateAbortCore, LibRequired, - NonStaticCrateDep, RequiredPanicStrategy, RlibRequired, RustcDriverHelp, RustcLibRequired, - TwoPanicRuntimes, + NonStaticCrateDep, RequiredPanicStrategy, RlibRequired, RustcLibRequired, TwoPanicRuntimes, }; pub(crate) fn calculate(tcx: TyCtxt<'_>) -> Dependencies { @@ -318,7 +317,7 @@ fn add_library( .drain(..) .map(|cnum| NonStaticCrateDep { crate_name_: tcx.crate_name(cnum) }) .collect(), - rustc_driver_help: linking_to_rustc_driver.then_some(RustcDriverHelp), + rustc_driver_help: linking_to_rustc_driver, }); } } diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index da26f855b6c7..e8b6cb8d02f6 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -43,8 +43,8 @@ pub struct CrateDepMultiple { pub crate_name: Symbol, #[subdiagnostic] pub non_static_deps: Vec, - #[subdiagnostic] - pub rustc_driver_help: Option, + #[help("`feature(rustc_private)` is needed to link to the compiler's `rustc_driver` library")] + pub rustc_driver_help: bool, } #[derive(Subdiagnostic)] @@ -54,10 +54,6 @@ pub struct NonStaticCrateDep { pub crate_name_: Symbol, } -#[derive(Subdiagnostic)] -#[help("`feature(rustc_private)` is needed to link to the compiler's `rustc_driver` library")] -pub struct RustcDriverHelp; - #[derive(Diagnostic)] #[diag("cannot link together two panic runtimes: {$prev_name} and {$cur_name}")] pub struct TwoPanicRuntimes { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index c6c87534851c..6ea9a2852804 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -418,6 +418,7 @@ provide! { tcx, def_id, other, cdata, } anon_const_kind => { table } const_of_item => { table } + is_rhs_type_const => { table } } pub(in crate::rmeta) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 30f8a753b9b7..3a85b0a05052 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; -use rustc_data_structures::sync::{join, par_for_each_in}; +use rustc_data_structures::sync::{par_for_each_in, par_join}; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_data_structures::thousands::usize_with_underscores; use rustc_feature::Features; @@ -1439,7 +1439,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { | hir::ConstArgKind::TupleCall(..) | hir::ConstArgKind::Tup(..) | hir::ConstArgKind::Path(..) - | hir::ConstArgKind::Literal(..) + | hir::ConstArgKind::Literal { .. } | hir::ConstArgKind::Infer(..) => true, hir::ConstArgKind::Anon(..) => false, }, @@ -1630,6 +1630,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let table = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id); record!(self.tables.associated_types_for_impl_traits_in_trait_or_impl[def_id] <- table); } + if let DefKind::AssocConst | DefKind::Const = def_kind { + record!(self.tables.is_rhs_type_const[def_id] <- self.tcx.is_rhs_type_const(def_id)); + } } for (def_id, impls) in &tcx.crate_inherent_impls(()).0.inherent_impls { @@ -2458,7 +2461,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { // Prefetch some queries used by metadata encoding. // This is not necessary for correctness, but is only done for performance reasons. // It can be removed if it turns out to cause trouble or be detrimental to performance. - join( + par_join( || prefetch_mir(tcx), || { let _ = tcx.exported_non_generic_symbols(LOCAL_CRATE); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index af6df0cd6eb6..408b50ae48df 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -476,6 +476,7 @@ define_tables! { anon_const_kind: Table>, const_of_item: Table>>>, associated_types_for_impl_traits_in_trait_or_impl: Table>>>, + is_rhs_type_const: Table>, } #[derive(TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index a5d5978916eb..d487caf4d3ac 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -93,7 +93,7 @@ pub(crate) struct StrictCoherenceNeedsNegativeCoherence { #[primary_span] pub span: Span, #[label("due to this attribute")] - pub attr_span: Option, + pub attr_span: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 5b32d4551a16..2095327ea1b5 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -27,8 +27,8 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::direct_use_of_rustc_type_ir)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(allocator_api)] -#![feature(assert_matches)] #![feature(associated_type_defaults)] #![feature(box_as_ptr)] #![feature(box_patterns)] diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index ded02595563c..2e6e96b4d8a8 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -157,13 +157,13 @@ impl<'dis, 'de, 'tcx> MirDumper<'dis, 'de, 'tcx> { /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name /// or `typeck` and `bar` both appear in the name. pub fn dump_mir(&self, body: &Body<'tcx>) { - let _: io::Result<()> = try { + let _ = try { let mut file = self.create_dump_file("mir", body)?; self.dump_mir_to_writer(body, &mut file)?; }; if self.tcx().sess.opts.unstable_opts.dump_mir_graphviz { - let _: io::Result<()> = try { + let _ = try { let mut file = self.create_dump_file("dot", body)?; write_mir_fn_graphviz(self.tcx(), body, false, &mut file)?; }; diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index e212dd43fa7d..8032d6b9316c 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -710,28 +710,6 @@ impl<'tcx> TerminatorKind<'tcx> { _ => None, } } - - /// Returns true if the terminator can write to memory. - pub fn can_write_to_memory(&self) -> bool { - match self { - TerminatorKind::Goto { .. } - | TerminatorKind::SwitchInt { .. } - | TerminatorKind::UnwindResume - | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::Return - | TerminatorKind::Assert { .. } - | TerminatorKind::CoroutineDrop - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::Unreachable => false, - TerminatorKind::Call { .. } - | TerminatorKind::Drop { .. } - | TerminatorKind::TailCall { .. } - // Yield writes to the resume_arg place. - | TerminatorKind::Yield { .. } - | TerminatorKind::InlineAsm { .. } => true, - } - } } #[derive(Copy, Clone, Debug)] diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index de2298914438..32fc7198425b 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -63,7 +63,6 @@ #![allow(unused_parens)] use std::ffi::OsStr; -use std::mem; use std::path::PathBuf; use std::sync::Arc; @@ -293,13 +292,13 @@ rustc_queries! { separate_provide_extern } - /// Returns the const of the RHS of a (free or assoc) const item, if it is a `#[type_const]`. + /// Returns the const of the RHS of a (free or assoc) const item, if it is a `type const`. /// /// When a const item is used in a type-level expression, like in equality for an assoc const /// projection, this allows us to retrieve the typesystem-appropriate representation of the /// const value. /// - /// This query will ICE if given a const that is not marked with `#[type_const]`. + /// This query will ICE if given a const that is not marked with `type const`. query const_of_item(def_id: DefId) -> ty::EarlyBinder<'tcx, ty::Const<'tcx>> { desc { |tcx| "computing the type-level value for `{}`", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } @@ -2764,6 +2763,12 @@ rustc_queries! { cache_on_disk_if { *cnum == LOCAL_CRATE } separate_provide_extern } + + query is_rhs_type_const(def_id: DefId) -> bool { + desc { |tcx| "checking whether `{}` is a rhs type const", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + } } rustc_with_all_queries! { define_callbacks! } diff --git a/compiler/rustc_middle/src/query/arena_cached.rs b/compiler/rustc_middle/src/query/arena_cached.rs index ec6e466ff688..7c7ad1262260 100644 --- a/compiler/rustc_middle/src/query/arena_cached.rs +++ b/compiler/rustc_middle/src/query/arena_cached.rs @@ -1,3 +1,9 @@ +use std::mem; + +use rustc_arena::TypedArena; + +use crate::ty::TyCtxt; + /// Helper trait that allows `arena_cache` queries to return `Option<&T>` /// instead of `&Option`, and avoid allocating `None` in the arena. /// @@ -11,10 +17,11 @@ pub trait ArenaCached<'tcx>: Sized { /// Type that is stored in the arena. type Allocated: 'tcx; - /// Takes a provided value, and allocates it in the arena (if appropriate) - /// with the help of the given `arena_alloc` closure. + /// Takes a provided value, and allocates it in an appropriate arena, + /// unless the particular value doesn't need allocation (e.g. `None`). fn alloc_in_arena( - arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated, + tcx: TyCtxt<'tcx>, + typed_arena: &'tcx TypedArena, value: Self::Provided, ) -> Self; } @@ -23,12 +30,9 @@ impl<'tcx, T> ArenaCached<'tcx> for &'tcx T { type Provided = T; type Allocated = T; - fn alloc_in_arena( - arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated, - value: Self::Provided, - ) -> Self { + fn alloc_in_arena(tcx: TyCtxt<'tcx>, typed_arena: &'tcx TypedArena, value: T) -> Self { // Just allocate in the arena normally. - arena_alloc(value) + do_alloc(tcx, typed_arena, value) } } @@ -38,10 +42,17 @@ impl<'tcx, T> ArenaCached<'tcx> for Option<&'tcx T> { type Allocated = T; fn alloc_in_arena( - arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated, - value: Self::Provided, + tcx: TyCtxt<'tcx>, + typed_arena: &'tcx TypedArena, + value: Option, ) -> Self { // Don't store None in the arena, and wrap the allocated reference in Some. - value.map(arena_alloc) + try { do_alloc(tcx, typed_arena, value?) } } } + +/// Allocates a value in either its dedicated arena, or in the common dropless +/// arena, depending on whether it needs to be dropped. +fn do_alloc<'tcx, T>(tcx: TyCtxt<'tcx>, typed_arena: &'tcx TypedArena, value: T) -> &'tcx T { + if mem::needs_drop::() { typed_arena.alloc(value) } else { tcx.arena.dropless.alloc(value) } +} diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index 32fe0737ec12..594286bfa574 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -2,7 +2,7 @@ //! `tcx.$query(..)` and its variations. use rustc_query_system::dep_graph::{DepKind, DepNodeKey}; -use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached}; +use rustc_query_system::query::{QueryCache, QueryMode}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use crate::dep_graph; @@ -10,6 +10,25 @@ use crate::query::erase::{self, Erasable, Erased}; use crate::query::plumbing::QueryVTable; use crate::ty::TyCtxt; +/// Checks whether there is already a value for this key in the in-memory +/// query cache, returning that value if present. +/// +/// (Also performs some associated bookkeeping, if a value was found.) +#[inline(always)] +fn try_get_cached<'tcx, C>(tcx: TyCtxt<'tcx>, cache: &C, key: &C::Key) -> Option +where + C: QueryCache, +{ + match cache.lookup(key) { + Some((value, index)) => { + tcx.prof.query_cache_hit(index.into()); + tcx.dep_graph.read_index(index); + Some(value) + } + None => None, + } +} + /// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)` /// for all queries. #[inline(always)] diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a7707cf48c49..62d5c1f9dd20 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -13,7 +13,7 @@ mod keys; pub mod on_disk_cache; #[macro_use] pub mod plumbing; -pub mod values; +pub(crate) mod modifiers; pub fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> String { let def_id = def_id.into(); diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs new file mode 100644 index 000000000000..81b9f0da6446 --- /dev/null +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -0,0 +1,77 @@ +//! This contains documentation which is linked from query modifiers used in the `rustc_queries!` proc macro. +#![allow(unused, non_camel_case_types)] +// FIXME: Update and clarify documentation for these modifiers. + +/// # `desc` query modifier +/// +/// The description of the query. This modifier is required on every query. +pub struct desc; + +/// # `arena_cache` query modifier +/// +/// Use this type for the in-memory cache. +pub struct arena_cache; + +/// # `cache_on_disk_if` query modifier +/// +/// Cache the query to disk if the `Block` returns true. +pub struct cache_on_disk_if; + +/// # `cycle_fatal` query modifier +/// +/// A cycle error for this query aborting the compilation with a fatal error. +pub struct cycle_fatal; + +/// # `cycle_delay_bug` query modifier +/// +/// A cycle error results in a delay_bug call +pub struct cycle_delay_bug; + +/// # `cycle_stash` query modifier +/// +/// A cycle error results in a stashed cycle error that can be unstashed and canceled later +pub struct cycle_stash; + +/// # `no_hash` query modifier +/// +/// Don't hash the result, instead just mark a query red if it runs +pub struct no_hash; + +/// # `anon` query modifier +/// +/// Generate a dep node based on the dependencies of the query +pub struct anon; + +/// # `eval_always` query modifier +/// +/// Always evaluate the query, ignoring its dependencies +pub struct eval_always; + +/// # `depth_limit` query modifier +/// +/// Whether the query has a call depth limit +pub struct depth_limit; + +/// # `separate_provide_extern` query modifier +/// +/// Use a separate query provider for local and extern crates +pub struct separate_provide_extern; + +/// # `feedable` query modifier +/// +/// Generate a `feed` method to set the query's value from another query. +pub struct feedable; + +/// # `return_result_from_ensure_ok` query modifier +/// +/// When this query is called via `tcx.ensure_ok()`, it returns +/// `Result<(), ErrorGuaranteed>` instead of `()`. If the query needs to +/// be executed, and that execution returns an error, the error result is +/// returned to the caller. +/// +/// If execution is skipped, a synthetic `Ok(())` is returned, on the +/// assumption that a query with all-green inputs must have succeeded. +/// +/// Can only be applied to queries with a return value of +/// `Result<_, ErrorGuaranteed>`. +pub struct return_result_from_ensure_ok; diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index abb02caef25d..9a709d2c43c8 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -1,12 +1,14 @@ use std::ops::Deref; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sync::{AtomicU64, WorkerLocal}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::hir_id::OwnerId; use rustc_macros::HashStable; use rustc_query_system::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; +use rustc_query_system::ich::StableHashingContext; pub(crate) use rustc_query_system::query::QueryJobId; -use rustc_query_system::query::{CycleError, CycleErrorHandling, HashResult, QueryCache}; +use rustc_query_system::query::{CycleError, CycleErrorHandling, QueryCache}; use rustc_span::{ErrorGuaranteed, Span}; pub use sealed::IntoQueryParam; @@ -30,6 +32,8 @@ pub type TryLoadFromDiskFn<'tcx, Key, Value> = fn( pub type IsLoadableFromDiskFn<'tcx, Key> = fn(tcx: TyCtxt<'tcx>, key: &Key, index: SerializedDepNodeIndex) -> bool; +pub type HashResult = Option, &V) -> Fingerprint>; + /// Stores function pointers and other metadata for a particular query. /// /// Used indirectly by query plumbing in `rustc_query_system` via a trait, @@ -45,8 +49,21 @@ pub struct QueryVTable<'tcx, C: QueryCache> { // Offset of this query's cache field in the QueryCaches struct pub query_cache: usize, pub will_cache_on_disk_for_key_fn: Option>, - pub execute_query: fn(tcx: TyCtxt<'tcx>, k: C::Key) -> C::Value, - pub compute_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, + + /// Function pointer that calls `tcx.$query(key)` for this query and + /// discards the returned value. + /// + /// This is a weird thing to be doing, and probably not what you want. + /// It is used for loading query results from disk-cache in some cases. + pub call_query_method_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key), + + /// Function pointer that actually calls this query's provider. + /// Also performs some associated secondary tasks; see the macro-defined + /// implementation in `mod invoke_provider_fn` for more details. + /// + /// This should be the only code that calls the provider function. + pub invoke_provider_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, + pub try_load_from_disk_fn: Option>, pub is_loadable_from_disk_fn: Option>, pub hash_result: HashResult, @@ -291,36 +308,28 @@ macro_rules! define_callbacks { ($V) ); - /// This function takes `ProvidedValue` and converts it to an erased `Value` by - /// allocating it on an arena if the query has the `arena_cache` modifier. The - /// value is then erased and returned. This will happen when computing the query - /// using a provider or decoding a stored result. + /// This helper function takes a value returned by the query provider + /// (or loaded from disk, or supplied by query feeding), allocates + /// it in an arena if requested by the `arena_cache` modifier, and + /// then returns an erased copy of it. #[inline(always)] pub fn provided_to_erased<'tcx>( - _tcx: TyCtxt<'tcx>, + tcx: TyCtxt<'tcx>, provided_value: ProvidedValue<'tcx>, ) -> Erased> { - // Store the provided value in an arena and get a reference - // to it, for queries with `arena_cache`. - let value: Value<'tcx> = query_if_arena!([$($modifiers)*] - { - use $crate::query::arena_cached::ArenaCached; - - if mem::needs_drop::<<$V as ArenaCached<'tcx>>::Allocated>() { - <$V as ArenaCached>::alloc_in_arena( - |v| _tcx.query_system.arenas.$name.alloc(v), - provided_value, - ) - } else { - <$V as ArenaCached>::alloc_in_arena( - |v| _tcx.arena.dropless.alloc(v), - provided_value, - ) - } - } - // Otherwise, the provided value is the value. - (provided_value) - ); + // For queries with the `arena_cache` modifier, store the + // provided value in an arena and get a reference to it. + let value: Value<'tcx> = query_if_arena!([$($modifiers)*] { + <$V as $crate::query::arena_cached::ArenaCached>::alloc_in_arena( + tcx, + &tcx.query_system.arenas.$name, + provided_value, + ) + } { + // Otherwise, the provided value is the value (and `tcx` is unused). + let _ = tcx; + provided_value + }); erase::erase_val(value) } @@ -453,10 +462,14 @@ macro_rules! define_callbacks { } pub struct Providers { - $(pub $name: for<'tcx> fn( - TyCtxt<'tcx>, - $name::LocalKey<'tcx>, - ) -> $name::ProvidedValue<'tcx>,)* + $( + /// This is the provider for the query. Use `Find references` on this to + /// navigate between the provider assignment and the query definition. + pub $name: for<'tcx> fn( + TyCtxt<'tcx>, + $name::LocalKey<'tcx>, + ) -> $name::ProvidedValue<'tcx>, + )* } pub struct ExternProviders { diff --git a/compiler/rustc_query_system/src/cache.rs b/compiler/rustc_middle/src/traits/cache.rs similarity index 60% rename from compiler/rustc_query_system/src/cache.rs rename to compiler/rustc_middle/src/traits/cache.rs index 4217d0a49b91..9391764bf1ce 100644 --- a/compiler/rustc_query_system/src/cache.rs +++ b/compiler/rustc_middle/src/traits/cache.rs @@ -5,26 +5,27 @@ use std::hash::Hash; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lock; -use crate::dep_graph::{DepContext, DepNodeIndex}; +use crate::dep_graph::DepNodeIndex; +use crate::ty::TyCtxt; -pub struct Cache { +pub struct WithDepNodeCache { hashmap: Lock>>, } -impl Clone for Cache { +impl Clone for WithDepNodeCache { fn clone(&self) -> Self { Self { hashmap: Lock::new(self.hashmap.borrow().clone()) } } } -impl Default for Cache { +impl Default for WithDepNodeCache { fn default() -> Self { Self { hashmap: Default::default() } } } -impl Cache { - pub fn get(&self, key: &Key, tcx: Tcx) -> Option { +impl WithDepNodeCache { + pub fn get<'tcx>(&self, key: &Key, tcx: TyCtxt<'tcx>) -> Option { Some(self.hashmap.borrow().get(key)?.get(tcx)) } @@ -40,12 +41,12 @@ pub struct WithDepNode { } impl WithDepNode { - pub fn new(dep_node: DepNodeIndex, cached_value: T) -> Self { + pub(crate) fn new(dep_node: DepNodeIndex, cached_value: T) -> Self { WithDepNode { dep_node, cached_value } } - pub fn get(&self, tcx: Tcx) -> T { - tcx.dep_graph().read_index(self.dep_node); + pub(crate) fn get<'tcx>(&self, tcx: TyCtxt<'tcx>) -> T { + tcx.dep_graph.read_index(self.dep_node); self.cached_value.clone() } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 3a1682614cbf..2fca85c9f2f1 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -2,6 +2,7 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html +pub mod cache; pub mod query; pub mod select; pub mod solve; @@ -844,7 +845,7 @@ impl DynCompatibilityViolation { format!("it contains generic associated const `{name}`").into() } Self::AssocConst(name, AssocConstViolation::NonType, _) => { - format!("it contains associated const `{name}` that's not marked `#[type_const]`") + format!("it contains associated const `{name}` that's not defined as `type const`") .into() } Self::AssocConst(name, AssocConstViolation::TypeReferencesSelf, _) => format!( @@ -998,7 +999,7 @@ pub enum AssocConstViolation { /// Has own generic parameters (GAC). Generic, - /// Isn't marked `#[type_const]`. + /// Isn't defined as `type const`. NonType, /// Its type mentions the `Self` type parameter. diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 3861efd36428..3a32029c60bd 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -5,17 +5,20 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TypeVisitable}; -use rustc_query_system::cache::Cache; use rustc_type_ir::solve::AliasBoundKind; use self::EvaluationResult::*; use super::{SelectionError, SelectionResult}; +use crate::traits::cache::WithDepNodeCache; use crate::ty; -pub type SelectionCache<'tcx, ENV> = - Cache<(ENV, ty::TraitPredicate<'tcx>), SelectionResult<'tcx, SelectionCandidate<'tcx>>>; +pub type SelectionCache<'tcx, ENV> = WithDepNodeCache< + (ENV, ty::TraitPredicate<'tcx>), + SelectionResult<'tcx, SelectionCandidate<'tcx>>, +>; -pub type EvaluationCache<'tcx, ENV> = Cache<(ENV, ty::PolyTraitPredicate<'tcx>), EvaluationResult>; +pub type EvaluationCache<'tcx, ENV> = + WithDepNodeCache<(ENV, ty::PolyTraitPredicate<'tcx>), EvaluationResult>; /// The selection process begins by considering all impls, where /// clauses, and so forth that might resolve an obligation. Sometimes diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 05c19db4caa8..84415a592a2e 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -1,8 +1,9 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{DefId, DefIdMap}; +use rustc_hir::find_attr; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; -use rustc_span::sym; use crate::error::StrictCoherenceNeedsNegativeCoherence; use crate::ty::fast_reject::SimplifiedType; @@ -61,23 +62,15 @@ pub enum OverlapMode { impl OverlapMode { pub fn get(tcx: TyCtxt<'_>, trait_id: DefId) -> OverlapMode { let with_negative_coherence = tcx.features().with_negative_coherence(); - let strict_coherence = tcx.has_attr(trait_id, sym::rustc_strict_coherence); + let strict_coherence = find_attr!(tcx.get_all_attrs(trait_id), AttributeKind::RustcStrictCoherence(span) => *span); if with_negative_coherence { - if strict_coherence { OverlapMode::Strict } else { OverlapMode::WithNegative } + if strict_coherence.is_some() { OverlapMode::Strict } else { OverlapMode::WithNegative } } else { - if strict_coherence { - let attr_span = trait_id - .as_local() - .into_iter() - .flat_map(|local_def_id| { - tcx.hir_attrs(tcx.local_def_id_to_hir_id(local_def_id)) - }) - .find(|attr| attr.has_name(sym::rustc_strict_coherence)) - .map(|attr| attr.span()); + if let Some(span) = strict_coherence { tcx.dcx().emit_err(StrictCoherenceNeedsNegativeCoherence { span: tcx.def_span(trait_id), - attr_span, + attr_span: span, }); } OverlapMode::Stable diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 5581ad5669aa..3e17943a8857 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use rustc_data_structures::intern::Interned; use rustc_error_messages::MultiSpan; -use rustc_macros::{HashStable, TyDecodable, TyEncodable}; +use rustc_macros::HashStable; use rustc_type_ir::walk::TypeWalker; use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; @@ -335,16 +335,3 @@ impl<'tcx> Const<'tcx> { TypeWalker::new(self.into()) } } - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable, HashStable)] -pub enum AnonConstKind { - /// `feature(generic_const_exprs)` anon consts are allowed to use arbitrary generic parameters in scope - GCE, - /// stable `min_const_generics` anon consts are not allowed to use any generic parameters - MCG, - /// anon consts used as the length of a repeat expr are syntactically allowed to use generic parameters - /// but must not depend on the actual instantiation. See #76200 for more information - RepeatExprCount, - /// anon consts outside of the type system, e.g. enum discriminants - NonTypeSystem, -} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 41e1388e3146..f638dd80864c 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2,6 +2,7 @@ #![allow(rustc::usage_of_ty_tykind)] +mod impl_interner; pub mod tls; use std::borrow::{Borrow, Cow}; @@ -16,6 +17,7 @@ use std::{fmt, iter, mem}; use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, VariantIdx}; use rustc_ast as ast; +use rustc_data_structures::defer; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; @@ -27,12 +29,9 @@ use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{ self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal, }; -use rustc_data_structures::{debug_assert_matches, defer}; -use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan, -}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, LintDiagnostic, MultiSpan}; use rustc_hir::attrs::AttributeKind; -use rustc_hir::def::{CtorKind, CtorOf, DefKind}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::{DefPathData, Definitions, DisambiguatorState}; use rustc_hir::intravisit::VisitorExt; @@ -40,7 +39,6 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::limit::Limit; use rustc_hir::{self as hir, HirId, Node, TraitCandidate, find_attr}; use rustc_index::IndexVec; -use rustc_query_system::cache::WithDepNode; use rustc_query_system::dep_graph::DepNodeIndex; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; @@ -51,16 +49,13 @@ use rustc_session::lint::Lint; use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw}; use rustc_type_ir::TyKind::*; -use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; pub use rustc_type_ir::lift::Lift; -use rustc_type_ir::{ - CollectAndApply, Interner, TypeFlags, TypeFoldable, WithCachedTypeInfo, elaborate, search_graph, -}; +use rustc_type_ir::{CollectAndApply, TypeFlags, WithCachedTypeInfo, elaborate, search_graph}; use tracing::{debug, instrument}; use crate::arena::Arena; use crate::dep_graph::{DepGraph, DepKindVTable}; -use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind, CanonicalVarKinds}; +use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind}; use crate::lint::lint_level; use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature}; @@ -71,794 +66,16 @@ use crate::query::plumbing::QuerySystem; use crate::query::{IntoQueryParam, LocalCrate, Providers, TyCtxtAt}; use crate::thir::Thir; use crate::traits; -use crate::traits::solve::{ - self, CanonicalInput, ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, - QueryResult, inspect, -}; +use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData, PredefinedOpaques}; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs, - GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, ParamTy, - Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, - PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, - ValTree, ValTreeKind, Visibility, + GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, Pattern, + PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicatePolarity, + Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, ValTree, ValTreeKind, + Visibility, }; -#[allow(rustc::usage_of_ty_tykind)] -impl<'tcx> Interner for TyCtxt<'tcx> { - fn next_trait_solver_globally(self) -> bool { - self.next_trait_solver_globally() - } - - type DefId = DefId; - type LocalDefId = LocalDefId; - type TraitId = DefId; - type ForeignId = DefId; - type FunctionId = DefId; - type ClosureId = DefId; - type CoroutineClosureId = DefId; - type CoroutineId = DefId; - type AdtId = DefId; - type ImplId = DefId; - type UnevaluatedConstId = DefId; - type Span = Span; - - type GenericArgs = ty::GenericArgsRef<'tcx>; - - type GenericArgsSlice = &'tcx [ty::GenericArg<'tcx>]; - type GenericArg = ty::GenericArg<'tcx>; - type Term = ty::Term<'tcx>; - type BoundVarKinds = &'tcx List>; - - type PredefinedOpaques = solve::PredefinedOpaques<'tcx>; - - fn mk_predefined_opaques_in_body( - self, - data: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)], - ) -> Self::PredefinedOpaques { - self.mk_predefined_opaques_in_body(data) - } - type LocalDefIds = &'tcx ty::List; - type CanonicalVarKinds = CanonicalVarKinds<'tcx>; - fn mk_canonical_var_kinds( - self, - kinds: &[ty::CanonicalVarKind], - ) -> Self::CanonicalVarKinds { - self.mk_canonical_var_kinds(kinds) - } - - type ExternalConstraints = ExternalConstraints<'tcx>; - fn mk_external_constraints( - self, - data: ExternalConstraintsData, - ) -> ExternalConstraints<'tcx> { - self.mk_external_constraints(data) - } - type DepNodeIndex = DepNodeIndex; - fn with_cached_task(self, task: impl FnOnce() -> T) -> (T, DepNodeIndex) { - self.dep_graph.with_anon_task(self, crate::dep_graph::dep_kinds::TraitSelect, task) - } - type Ty = Ty<'tcx>; - type Tys = &'tcx List>; - - type FnInputTys = &'tcx [Ty<'tcx>]; - type ParamTy = ParamTy; - type Symbol = Symbol; - - type ErrorGuaranteed = ErrorGuaranteed; - type BoundExistentialPredicates = &'tcx List>; - - type AllocId = crate::mir::interpret::AllocId; - type Pat = Pattern<'tcx>; - type PatList = &'tcx List>; - type Safety = hir::Safety; - type Abi = ExternAbi; - type Const = ty::Const<'tcx>; - - type ParamConst = ty::ParamConst; - type ValueConst = ty::Value<'tcx>; - type ExprConst = ty::Expr<'tcx>; - type ValTree = ty::ValTree<'tcx>; - type ScalarInt = ty::ScalarInt; - - type Region = Region<'tcx>; - type EarlyParamRegion = ty::EarlyParamRegion; - type LateParamRegion = ty::LateParamRegion; - - type RegionAssumptions = &'tcx ty::List>; - - type ParamEnv = ty::ParamEnv<'tcx>; - type Predicate = Predicate<'tcx>; - - type Clause = Clause<'tcx>; - type Clauses = ty::Clauses<'tcx>; - - type Tracked = WithDepNode; - fn mk_tracked( - self, - data: T, - dep_node: DepNodeIndex, - ) -> Self::Tracked { - WithDepNode::new(dep_node, data) - } - fn get_tracked(self, tracked: &Self::Tracked) -> T { - tracked.get(self) - } - - fn with_global_cache(self, f: impl FnOnce(&mut search_graph::GlobalCache) -> R) -> R { - f(&mut *self.new_solver_evaluation_cache.lock()) - } - - fn canonical_param_env_cache_get_or_insert( - self, - param_env: ty::ParamEnv<'tcx>, - f: impl FnOnce() -> ty::CanonicalParamEnvCacheEntry, - from_entry: impl FnOnce(&ty::CanonicalParamEnvCacheEntry) -> R, - ) -> R { - let mut cache = self.new_solver_canonical_param_env_cache.lock(); - let entry = cache.entry(param_env).or_insert_with(f); - from_entry(entry) - } - - fn assert_evaluation_is_concurrent(&self) { - // Turns out, the assumption for this function isn't perfect. - // See trait-system-refactor-initiative#234. - } - - fn expand_abstract_consts>>(self, t: T) -> T { - self.expand_abstract_consts(t) - } - - type GenericsOf = &'tcx ty::Generics; - - fn generics_of(self, def_id: DefId) -> &'tcx ty::Generics { - self.generics_of(def_id) - } - - type VariancesOf = &'tcx [ty::Variance]; - - fn variances_of(self, def_id: DefId) -> Self::VariancesOf { - self.variances_of(def_id) - } - - fn opt_alias_variances( - self, - kind: impl Into, - def_id: DefId, - ) -> Option<&'tcx [ty::Variance]> { - self.opt_alias_variances(kind, def_id) - } - - fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { - self.type_of(def_id) - } - fn type_of_opaque_hir_typeck(self, def_id: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { - self.type_of_opaque_hir_typeck(def_id) - } - fn const_of_item(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Const<'tcx>> { - self.const_of_item(def_id) - } - - type AdtDef = ty::AdtDef<'tcx>; - fn adt_def(self, adt_def_id: DefId) -> Self::AdtDef { - self.adt_def(adt_def_id) - } - - fn alias_ty_kind(self, alias: ty::AliasTy<'tcx>) -> ty::AliasTyKind { - match self.def_kind(alias.def_id) { - DefKind::AssocTy => { - if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(alias.def_id)) - { - ty::Inherent - } else { - ty::Projection - } - } - DefKind::OpaqueTy => ty::Opaque, - DefKind::TyAlias => ty::Free, - kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), - } - } - - fn alias_term_kind(self, alias: ty::AliasTerm<'tcx>) -> ty::AliasTermKind { - match self.def_kind(alias.def_id) { - DefKind::AssocTy => { - if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(alias.def_id)) - { - ty::AliasTermKind::InherentTy - } else { - ty::AliasTermKind::ProjectionTy - } - } - DefKind::AssocConst => { - if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(alias.def_id)) - { - ty::AliasTermKind::InherentConst - } else { - ty::AliasTermKind::ProjectionConst - } - } - DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy, - DefKind::TyAlias => ty::AliasTermKind::FreeTy, - DefKind::Const => ty::AliasTermKind::FreeConst, - DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => { - ty::AliasTermKind::UnevaluatedConst - } - kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), - } - } - - fn trait_ref_and_own_args_for_alias( - self, - def_id: DefId, - args: ty::GenericArgsRef<'tcx>, - ) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) { - debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst); - let trait_def_id = self.parent(def_id); - debug_assert_matches!(self.def_kind(trait_def_id), DefKind::Trait); - let trait_ref = ty::TraitRef::from_assoc(self, trait_def_id, args); - (trait_ref, &args[trait_ref.args.len()..]) - } - - fn mk_args(self, args: &[Self::GenericArg]) -> ty::GenericArgsRef<'tcx> { - self.mk_args(args) - } - - fn mk_args_from_iter(self, args: I) -> T::Output - where - I: Iterator, - T: CollectAndApply>, - { - self.mk_args_from_iter(args) - } - - fn check_args_compatible(self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> bool { - self.check_args_compatible(def_id, args) - } - - fn debug_assert_args_compatible(self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) { - self.debug_assert_args_compatible(def_id, args); - } - - /// Assert that the args from an `ExistentialTraitRef` or `ExistentialProjection` - /// are compatible with the `DefId`. Since we're missing a `Self` type, stick on - /// a dummy self type and forward to `debug_assert_args_compatible`. - fn debug_assert_existential_args_compatible( - self, - def_id: Self::DefId, - args: Self::GenericArgs, - ) { - // FIXME: We could perhaps add a `skip: usize` to `debug_assert_args_compatible` - // to avoid needing to reintern the set of args... - if cfg!(debug_assertions) { - self.debug_assert_args_compatible( - def_id, - self.mk_args_from_iter( - [self.types.trait_object_dummy_self.into()].into_iter().chain(args.iter()), - ), - ); - } - } - - fn mk_type_list_from_iter(self, args: I) -> T::Output - where - I: Iterator, - T: CollectAndApply, &'tcx List>>, - { - self.mk_type_list_from_iter(args) - } - - fn parent(self, def_id: DefId) -> DefId { - self.parent(def_id) - } - - fn recursion_limit(self) -> usize { - self.recursion_limit().0 - } - - type Features = &'tcx rustc_feature::Features; - - fn features(self) -> Self::Features { - self.features() - } - - fn coroutine_hidden_types( - self, - def_id: DefId, - ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes>>> { - self.coroutine_hidden_types(def_id) - } - - fn fn_sig(self, def_id: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { - self.fn_sig(def_id) - } - - fn coroutine_movability(self, def_id: DefId) -> rustc_ast::Movability { - self.coroutine_movability(def_id) - } - - fn coroutine_for_closure(self, def_id: DefId) -> DefId { - self.coroutine_for_closure(def_id) - } - - fn generics_require_sized_self(self, def_id: DefId) -> bool { - self.generics_require_sized_self(def_id) - } - - fn item_bounds( - self, - def_id: DefId, - ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - self.item_bounds(def_id).map_bound(IntoIterator::into_iter) - } - - fn item_self_bounds( - self, - def_id: DefId, - ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - self.item_self_bounds(def_id).map_bound(IntoIterator::into_iter) - } - - fn item_non_self_bounds( - self, - def_id: DefId, - ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - self.item_non_self_bounds(def_id).map_bound(IntoIterator::into_iter) - } - - fn predicates_of( - self, - def_id: DefId, - ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - ty::EarlyBinder::bind( - self.predicates_of(def_id).instantiate_identity(self).predicates.into_iter(), - ) - } - - fn own_predicates_of( - self, - def_id: DefId, - ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - ty::EarlyBinder::bind( - self.predicates_of(def_id).instantiate_own_identity().map(|(clause, _)| clause), - ) - } - - fn explicit_super_predicates_of( - self, - def_id: DefId, - ) -> ty::EarlyBinder<'tcx, impl IntoIterator, Span)>> { - self.explicit_super_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied()) - } - - fn explicit_implied_predicates_of( - self, - def_id: DefId, - ) -> ty::EarlyBinder<'tcx, impl IntoIterator, Span)>> { - self.explicit_implied_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied()) - } - - fn impl_super_outlives( - self, - impl_def_id: DefId, - ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - self.impl_super_outlives(impl_def_id) - } - - fn impl_is_const(self, def_id: DefId) -> bool { - debug_assert_matches!(self.def_kind(def_id), DefKind::Impl { of_trait: true }); - self.is_conditionally_const(def_id) - } - - fn fn_is_const(self, def_id: DefId) -> bool { - debug_assert_matches!( - self.def_kind(def_id), - DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(CtorOf::Struct, CtorKind::Fn) - ); - self.is_conditionally_const(def_id) - } - - fn alias_has_const_conditions(self, def_id: DefId) -> bool { - debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::OpaqueTy); - self.is_conditionally_const(def_id) - } - - fn const_conditions( - self, - def_id: DefId, - ) -> ty::EarlyBinder<'tcx, impl IntoIterator>>> { - ty::EarlyBinder::bind( - self.const_conditions(def_id).instantiate_identity(self).into_iter().map(|(c, _)| c), - ) - } - - fn explicit_implied_const_bounds( - self, - def_id: DefId, - ) -> ty::EarlyBinder<'tcx, impl IntoIterator>>> { - ty::EarlyBinder::bind( - self.explicit_implied_const_bounds(def_id).iter_identity_copied().map(|(c, _)| c), - ) - } - - fn impl_self_is_guaranteed_unsized(self, impl_def_id: DefId) -> bool { - self.impl_self_is_guaranteed_unsized(impl_def_id) - } - - fn has_target_features(self, def_id: DefId) -> bool { - !self.codegen_fn_attrs(def_id).target_features.is_empty() - } - - fn require_lang_item(self, lang_item: SolverLangItem) -> DefId { - self.require_lang_item(solver_lang_item_to_lang_item(lang_item), DUMMY_SP) - } - - fn require_trait_lang_item(self, lang_item: SolverTraitLangItem) -> DefId { - self.require_lang_item(solver_trait_lang_item_to_lang_item(lang_item), DUMMY_SP) - } - - fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> DefId { - self.require_lang_item(solver_adt_lang_item_to_lang_item(lang_item), DUMMY_SP) - } - - fn is_lang_item(self, def_id: DefId, lang_item: SolverLangItem) -> bool { - self.is_lang_item(def_id, solver_lang_item_to_lang_item(lang_item)) - } - - fn is_trait_lang_item(self, def_id: DefId, lang_item: SolverTraitLangItem) -> bool { - self.is_lang_item(def_id, solver_trait_lang_item_to_lang_item(lang_item)) - } - - fn is_adt_lang_item(self, def_id: DefId, lang_item: SolverAdtLangItem) -> bool { - self.is_lang_item(def_id, solver_adt_lang_item_to_lang_item(lang_item)) - } - - fn is_default_trait(self, def_id: DefId) -> bool { - self.is_default_trait(def_id) - } - - fn is_sizedness_trait(self, def_id: DefId) -> bool { - self.is_sizedness_trait(def_id) - } - - fn as_lang_item(self, def_id: DefId) -> Option { - lang_item_to_solver_lang_item(self.lang_items().from_def_id(def_id)?) - } - - fn as_trait_lang_item(self, def_id: DefId) -> Option { - lang_item_to_solver_trait_lang_item(self.lang_items().from_def_id(def_id)?) - } - - fn as_adt_lang_item(self, def_id: DefId) -> Option { - lang_item_to_solver_adt_lang_item(self.lang_items().from_def_id(def_id)?) - } - - fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator { - self.associated_items(def_id) - .in_definition_order() - .filter(|assoc_item| assoc_item.is_type()) - .map(|assoc_item| assoc_item.def_id) - } - - // This implementation is a bit different from `TyCtxt::for_each_relevant_impl`, - // since we want to skip over blanket impls for non-rigid aliases, and also we - // only want to consider types that *actually* unify with float/int vars. - fn for_each_relevant_impl( - self, - trait_def_id: DefId, - self_ty: Ty<'tcx>, - mut f: impl FnMut(DefId), - ) { - let tcx = self; - let trait_impls = tcx.trait_impls_of(trait_def_id); - let mut consider_impls_for_simplified_type = |simp| { - if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { - for &impl_def_id in impls_for_type { - f(impl_def_id); - } - } - }; - - match self_ty.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Adt(_, _) - | ty::Foreign(_) - | ty::Str - | ty::Array(_, _) - | ty::Pat(_, _) - | ty::Slice(_) - | ty::RawPtr(_, _) - | ty::Ref(_, _, _) - | ty::FnDef(_, _) - | ty::FnPtr(..) - | ty::Dynamic(_, _) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(_, _) - | ty::Never - | ty::Tuple(_) - | ty::UnsafeBinder(_) => { - if let Some(simp) = ty::fast_reject::simplify_type( - tcx, - self_ty, - ty::fast_reject::TreatParams::AsRigid, - ) { - consider_impls_for_simplified_type(simp); - } - } - - // HACK: For integer and float variables we have to manually look at all impls - // which have some integer or float as a self type. - ty::Infer(ty::IntVar(_)) => { - use ty::IntTy::*; - use ty::UintTy::*; - // This causes a compiler error if any new integer kinds are added. - let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy; - let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy; - let possible_integers = [ - // signed integers - ty::SimplifiedType::Int(I8), - ty::SimplifiedType::Int(I16), - ty::SimplifiedType::Int(I32), - ty::SimplifiedType::Int(I64), - ty::SimplifiedType::Int(I128), - ty::SimplifiedType::Int(Isize), - // unsigned integers - ty::SimplifiedType::Uint(U8), - ty::SimplifiedType::Uint(U16), - ty::SimplifiedType::Uint(U32), - ty::SimplifiedType::Uint(U64), - ty::SimplifiedType::Uint(U128), - ty::SimplifiedType::Uint(Usize), - ]; - for simp in possible_integers { - consider_impls_for_simplified_type(simp); - } - } - - ty::Infer(ty::FloatVar(_)) => { - // This causes a compiler error if any new float kinds are added. - let (ty::FloatTy::F16 | ty::FloatTy::F32 | ty::FloatTy::F64 | ty::FloatTy::F128); - let possible_floats = [ - ty::SimplifiedType::Float(ty::FloatTy::F16), - ty::SimplifiedType::Float(ty::FloatTy::F32), - ty::SimplifiedType::Float(ty::FloatTy::F64), - ty::SimplifiedType::Float(ty::FloatTy::F128), - ]; - - for simp in possible_floats { - consider_impls_for_simplified_type(simp); - } - } - - // The only traits applying to aliases and placeholders are blanket impls. - // - // Impls which apply to an alias after normalization are handled by - // `assemble_candidates_after_normalizing_self_ty`. - ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (), - - // FIXME: These should ideally not exist as a self type. It would be nice for - // the builtin auto trait impls of coroutines to instead directly recurse - // into the witness. - ty::CoroutineWitness(..) => (), - - // These variants should not exist as a self type. - ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) - | ty::Param(_) - | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"), - } - - #[allow(rustc::usage_of_type_ir_traits)] - self.for_each_blanket_impl(trait_def_id, f) - } - fn for_each_blanket_impl(self, trait_def_id: DefId, mut f: impl FnMut(DefId)) { - let trait_impls = self.trait_impls_of(trait_def_id); - for &impl_def_id in trait_impls.blanket_impls() { - f(impl_def_id); - } - } - - fn has_item_definition(self, def_id: DefId) -> bool { - self.defaultness(def_id).has_value() - } - - fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool { - self.specializes((impl_def_id, victim_def_id)) - } - - fn impl_is_default(self, impl_def_id: DefId) -> bool { - self.defaultness(impl_def_id).is_default() - } - - fn impl_trait_ref(self, impl_def_id: DefId) -> ty::EarlyBinder<'tcx, ty::TraitRef<'tcx>> { - self.impl_trait_ref(impl_def_id) - } - - fn impl_polarity(self, impl_def_id: DefId) -> ty::ImplPolarity { - self.impl_polarity(impl_def_id) - } - - fn trait_is_auto(self, trait_def_id: DefId) -> bool { - self.trait_is_auto(trait_def_id) - } - - fn trait_is_coinductive(self, trait_def_id: DefId) -> bool { - self.trait_is_coinductive(trait_def_id) - } - - fn trait_is_alias(self, trait_def_id: DefId) -> bool { - self.trait_is_alias(trait_def_id) - } - - fn trait_is_dyn_compatible(self, trait_def_id: DefId) -> bool { - self.is_dyn_compatible(trait_def_id) - } - - fn trait_is_fundamental(self, def_id: DefId) -> bool { - self.trait_def(def_id).is_fundamental - } - - fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool { - self.trait_def(trait_def_id).safety.is_unsafe() - } - - fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { - self.is_impl_trait_in_trait(def_id) - } - - fn delay_bug(self, msg: impl ToString) -> ErrorGuaranteed { - self.dcx().span_delayed_bug(DUMMY_SP, msg.to_string()) - } - - fn is_general_coroutine(self, coroutine_def_id: DefId) -> bool { - self.is_general_coroutine(coroutine_def_id) - } - - fn coroutine_is_async(self, coroutine_def_id: DefId) -> bool { - self.coroutine_is_async(coroutine_def_id) - } - - fn coroutine_is_gen(self, coroutine_def_id: DefId) -> bool { - self.coroutine_is_gen(coroutine_def_id) - } - - fn coroutine_is_async_gen(self, coroutine_def_id: DefId) -> bool { - self.coroutine_is_async_gen(coroutine_def_id) - } - - type UnsizingParams = &'tcx rustc_index::bit_set::DenseBitSet; - fn unsizing_params_for_adt(self, adt_def_id: DefId) -> Self::UnsizingParams { - self.unsizing_params_for_adt(adt_def_id) - } - - fn anonymize_bound_vars>>( - self, - binder: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - self.anonymize_bound_vars(binder) - } - - fn opaque_types_defined_by(self, defining_anchor: LocalDefId) -> Self::LocalDefIds { - self.opaque_types_defined_by(defining_anchor) - } - - fn opaque_types_and_coroutines_defined_by( - self, - defining_anchor: Self::LocalDefId, - ) -> Self::LocalDefIds { - let coroutines_defined_by = self - .nested_bodies_within(defining_anchor) - .iter() - .filter(|def_id| self.is_coroutine(def_id.to_def_id())); - self.mk_local_def_ids_from_iter( - self.opaque_types_defined_by(defining_anchor).iter().chain(coroutines_defined_by), - ) - } - - type Probe = &'tcx inspect::Probe>; - fn mk_probe(self, probe: inspect::Probe) -> &'tcx inspect::Probe> { - self.arena.alloc(probe) - } - fn evaluate_root_goal_for_proof_tree_raw( - self, - canonical_goal: CanonicalInput<'tcx>, - ) -> (QueryResult<'tcx>, &'tcx inspect::Probe>) { - self.evaluate_root_goal_for_proof_tree_raw(canonical_goal) - } - - fn item_name(self, id: DefId) -> Symbol { - let id = id.into_query_param(); - self.opt_item_name(id).unwrap_or_else(|| { - bug!("item_name: no name for {:?}", self.def_path(id)); - }) - } -} - -macro_rules! bidirectional_lang_item_map { - ( - $solver_ty:ident, $to_solver:ident, $from_solver:ident; - $($name:ident),+ $(,)? - ) => { - fn $from_solver(lang_item: $solver_ty) -> LangItem { - match lang_item { - $($solver_ty::$name => LangItem::$name,)+ - } - } - - fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { - Some(match lang_item { - $(LangItem::$name => $solver_ty::$name,)+ - _ => return None, - }) - } - } -} - -bidirectional_lang_item_map! { - SolverLangItem, lang_item_to_solver_lang_item, solver_lang_item_to_lang_item; - -// tidy-alphabetical-start - AsyncFnKindUpvars, - AsyncFnOnceOutput, - CallOnceFuture, - CallRefFuture, - CoroutineReturn, - CoroutineYield, - DynMetadata, - FutureOutput, - Metadata, -// tidy-alphabetical-end -} - -bidirectional_lang_item_map! { - SolverAdtLangItem, lang_item_to_solver_adt_lang_item, solver_adt_lang_item_to_lang_item; - -// tidy-alphabetical-start - Option, - Poll, -// tidy-alphabetical-end -} - -bidirectional_lang_item_map! { - SolverTraitLangItem, lang_item_to_solver_trait_lang_item, solver_trait_lang_item_to_lang_item; - -// tidy-alphabetical-start - AsyncFn, - AsyncFnKindHelper, - AsyncFnMut, - AsyncFnOnce, - AsyncFnOnceOutput, - AsyncIterator, - BikeshedGuaranteedNoDrop, - Clone, - Copy, - Coroutine, - Destruct, - DiscriminantKind, - Drop, - Fn, - FnMut, - FnOnce, - FnPtrTrait, - FusedIterator, - Future, - Iterator, - MetaSized, - PointeeSized, - PointeeTrait, - Sized, - TransmuteTrait, - TrivialClone, - Tuple, - Unpin, - Unsize, -// tidy-alphabetical-end -} - impl<'tcx> rustc_type_ir::inherent::DefId> for DefId { fn is_local(self) -> bool { self.is_local() @@ -1886,10 +1103,23 @@ impl<'tcx> TyCtxt<'tcx> { self.is_lang_item(self.parent(def_id), LangItem::AsyncDropInPlace) } - /// Check if the given `def_id` is a const with the `#[type_const]` attribute. - pub fn is_type_const(self, def_id: DefId) -> bool { - matches!(self.def_kind(def_id), DefKind::Const | DefKind::AssocConst) - && find_attr!(self.get_all_attrs(def_id), AttributeKind::TypeConst(_)) + pub fn type_const_span(self, def_id: DefId) -> Option { + if !self.is_type_const(def_id) { + return None; + } + Some(self.def_span(def_id)) + } + + /// Check if the given `def_id` is a `type const` (mgca) + pub fn is_type_const>(self, def_id: I) -> bool { + // No need to call the query directly in this case always false. + if !(matches!( + self.def_kind(def_id.into_query_param()), + DefKind::Const | DefKind::AssocConst + )) { + return false; + } + self.is_rhs_type_const(def_id) } /// Returns the movability of the coroutine of `def_id`, or panics @@ -2915,12 +2145,15 @@ impl<'tcx> TyCtxt<'tcx> { ) -> bool { let generics = self.generics_of(def_id); - // IATs themselves have a weird arg setup (self + own args), but nested items *in* IATs - // (namely: opaques, i.e. ATPITs) do not. - let own_args = if !nested - && let DefKind::AssocTy = self.def_kind(def_id) - && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) - { + // IATs and IACs (inherent associated types/consts with `type const`) themselves have a + // weird arg setup (self + own args), but nested items *in* IATs (namely: opaques, i.e. + // ATPITs) do not. + let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy) + && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); + let is_inherent_assoc_type_const = matches!(self.def_kind(def_id), DefKind::AssocConst) + && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }) + && self.is_type_const(def_id); + let own_args = if !nested && (is_inherent_assoc_ty || is_inherent_assoc_type_const) { if generics.own_params.len() + 1 != args.len() { return false; } @@ -2962,9 +2195,12 @@ impl<'tcx> TyCtxt<'tcx> { /// and print out the args if not. pub fn debug_assert_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) { if cfg!(debug_assertions) && !self.check_args_compatible(def_id, args) { - if let DefKind::AssocTy = self.def_kind(def_id) - && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) - { + let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy) + && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); + let is_inherent_assoc_type_const = matches!(self.def_kind(def_id), DefKind::AssocConst) + && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }) + && self.is_type_const(def_id); + if is_inherent_assoc_ty || is_inherent_assoc_type_const { bug!( "args not compatible with generics for {}: args={:#?}, generics={:#?}", self.def_path_str(def_id), diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs new file mode 100644 index 000000000000..5a15d132048d --- /dev/null +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -0,0 +1,806 @@ +//! Implementation of [`rustc_type_ir::Interner`] for [`TyCtxt`]. + +use std::fmt; + +use rustc_abi::ExternAbi; +use rustc_data_structures::debug_assert_matches; +use rustc_errors::ErrorGuaranteed; +use rustc_hir::def::{CtorKind, CtorOf, DefKind}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{self as hir}; +use rustc_query_system::dep_graph::DepNodeIndex; +use rustc_span::{DUMMY_SP, Span, Symbol}; +use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::{CollectAndApply, Interner, TypeFoldable, search_graph}; + +use crate::infer::canonical::CanonicalVarKinds; +use crate::query::IntoQueryParam; +use crate::traits::cache::WithDepNode; +use crate::traits::solve::{ + self, CanonicalInput, ExternalConstraints, ExternalConstraintsData, QueryResult, inspect, +}; +use crate::ty::{ + self, Clause, Const, List, ParamTy, Pattern, PolyExistentialPredicate, Predicate, Region, Ty, + TyCtxt, +}; + +#[allow(rustc::usage_of_ty_tykind)] +impl<'tcx> Interner for TyCtxt<'tcx> { + fn next_trait_solver_globally(self) -> bool { + self.next_trait_solver_globally() + } + + type DefId = DefId; + type LocalDefId = LocalDefId; + type TraitId = DefId; + type ForeignId = DefId; + type FunctionId = DefId; + type ClosureId = DefId; + type CoroutineClosureId = DefId; + type CoroutineId = DefId; + type AdtId = DefId; + type ImplId = DefId; + type UnevaluatedConstId = DefId; + type Span = Span; + + type GenericArgs = ty::GenericArgsRef<'tcx>; + + type GenericArgsSlice = &'tcx [ty::GenericArg<'tcx>]; + type GenericArg = ty::GenericArg<'tcx>; + type Term = ty::Term<'tcx>; + type BoundVarKinds = &'tcx List>; + + type PredefinedOpaques = solve::PredefinedOpaques<'tcx>; + + fn mk_predefined_opaques_in_body( + self, + data: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)], + ) -> Self::PredefinedOpaques { + self.mk_predefined_opaques_in_body(data) + } + type LocalDefIds = &'tcx ty::List; + type CanonicalVarKinds = CanonicalVarKinds<'tcx>; + fn mk_canonical_var_kinds( + self, + kinds: &[ty::CanonicalVarKind], + ) -> Self::CanonicalVarKinds { + self.mk_canonical_var_kinds(kinds) + } + + type ExternalConstraints = ExternalConstraints<'tcx>; + fn mk_external_constraints( + self, + data: ExternalConstraintsData, + ) -> ExternalConstraints<'tcx> { + self.mk_external_constraints(data) + } + type DepNodeIndex = DepNodeIndex; + fn with_cached_task(self, task: impl FnOnce() -> T) -> (T, DepNodeIndex) { + self.dep_graph.with_anon_task(self, crate::dep_graph::dep_kinds::TraitSelect, task) + } + type Ty = Ty<'tcx>; + type Tys = &'tcx List>; + + type FnInputTys = &'tcx [Ty<'tcx>]; + type ParamTy = ParamTy; + type Symbol = Symbol; + + type ErrorGuaranteed = ErrorGuaranteed; + type BoundExistentialPredicates = &'tcx List>; + + type AllocId = crate::mir::interpret::AllocId; + type Pat = Pattern<'tcx>; + type PatList = &'tcx List>; + type Safety = hir::Safety; + type Abi = ExternAbi; + type Const = ty::Const<'tcx>; + + type ParamConst = ty::ParamConst; + type ValueConst = ty::Value<'tcx>; + type ExprConst = ty::Expr<'tcx>; + type ValTree = ty::ValTree<'tcx>; + type ScalarInt = ty::ScalarInt; + + type Region = Region<'tcx>; + type EarlyParamRegion = ty::EarlyParamRegion; + type LateParamRegion = ty::LateParamRegion; + + type RegionAssumptions = &'tcx ty::List>; + + type ParamEnv = ty::ParamEnv<'tcx>; + type Predicate = Predicate<'tcx>; + + type Clause = Clause<'tcx>; + type Clauses = ty::Clauses<'tcx>; + + type Tracked = WithDepNode; + fn mk_tracked( + self, + data: T, + dep_node: DepNodeIndex, + ) -> Self::Tracked { + WithDepNode::new(dep_node, data) + } + fn get_tracked(self, tracked: &Self::Tracked) -> T { + tracked.get(self) + } + + fn with_global_cache(self, f: impl FnOnce(&mut search_graph::GlobalCache) -> R) -> R { + f(&mut *self.new_solver_evaluation_cache.lock()) + } + + fn canonical_param_env_cache_get_or_insert( + self, + param_env: ty::ParamEnv<'tcx>, + f: impl FnOnce() -> ty::CanonicalParamEnvCacheEntry, + from_entry: impl FnOnce(&ty::CanonicalParamEnvCacheEntry) -> R, + ) -> R { + let mut cache = self.new_solver_canonical_param_env_cache.lock(); + let entry = cache.entry(param_env).or_insert_with(f); + from_entry(entry) + } + + fn assert_evaluation_is_concurrent(&self) { + // Turns out, the assumption for this function isn't perfect. + // See trait-system-refactor-initiative#234. + } + + fn expand_abstract_consts>>(self, t: T) -> T { + self.expand_abstract_consts(t) + } + + type GenericsOf = &'tcx ty::Generics; + + fn generics_of(self, def_id: DefId) -> &'tcx ty::Generics { + self.generics_of(def_id) + } + + type VariancesOf = &'tcx [ty::Variance]; + + fn variances_of(self, def_id: DefId) -> Self::VariancesOf { + self.variances_of(def_id) + } + + fn opt_alias_variances( + self, + kind: impl Into, + def_id: DefId, + ) -> Option<&'tcx [ty::Variance]> { + self.opt_alias_variances(kind, def_id) + } + + fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { + self.type_of(def_id) + } + fn type_of_opaque_hir_typeck(self, def_id: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { + self.type_of_opaque_hir_typeck(def_id) + } + fn const_of_item(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Const<'tcx>> { + self.const_of_item(def_id) + } + fn anon_const_kind(self, def_id: DefId) -> ty::AnonConstKind { + self.anon_const_kind(def_id) + } + + type AdtDef = ty::AdtDef<'tcx>; + fn adt_def(self, adt_def_id: DefId) -> Self::AdtDef { + self.adt_def(adt_def_id) + } + + fn alias_ty_kind(self, alias: ty::AliasTy<'tcx>) -> ty::AliasTyKind { + match self.def_kind(alias.def_id) { + DefKind::AssocTy => { + if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(alias.def_id)) + { + ty::Inherent + } else { + ty::Projection + } + } + DefKind::OpaqueTy => ty::Opaque, + DefKind::TyAlias => ty::Free, + kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), + } + } + + fn alias_term_kind(self, alias: ty::AliasTerm<'tcx>) -> ty::AliasTermKind { + match self.def_kind(alias.def_id) { + DefKind::AssocTy => { + if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(alias.def_id)) + { + ty::AliasTermKind::InherentTy + } else { + ty::AliasTermKind::ProjectionTy + } + } + DefKind::AssocConst => { + if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(alias.def_id)) + { + ty::AliasTermKind::InherentConst + } else { + ty::AliasTermKind::ProjectionConst + } + } + DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy, + DefKind::TyAlias => ty::AliasTermKind::FreeTy, + DefKind::Const => ty::AliasTermKind::FreeConst, + DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => { + ty::AliasTermKind::UnevaluatedConst + } + kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), + } + } + + fn trait_ref_and_own_args_for_alias( + self, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + ) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) { + debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst); + let trait_def_id = self.parent(def_id); + debug_assert_matches!(self.def_kind(trait_def_id), DefKind::Trait); + let trait_ref = ty::TraitRef::from_assoc(self, trait_def_id, args); + (trait_ref, &args[trait_ref.args.len()..]) + } + + fn mk_args(self, args: &[Self::GenericArg]) -> ty::GenericArgsRef<'tcx> { + self.mk_args(args) + } + + fn mk_args_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: CollectAndApply>, + { + self.mk_args_from_iter(args) + } + + fn check_args_compatible(self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> bool { + self.check_args_compatible(def_id, args) + } + + fn debug_assert_args_compatible(self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) { + self.debug_assert_args_compatible(def_id, args); + } + + /// Assert that the args from an `ExistentialTraitRef` or `ExistentialProjection` + /// are compatible with the `DefId`. Since we're missing a `Self` type, stick on + /// a dummy self type and forward to `debug_assert_args_compatible`. + fn debug_assert_existential_args_compatible( + self, + def_id: Self::DefId, + args: Self::GenericArgs, + ) { + // FIXME: We could perhaps add a `skip: usize` to `debug_assert_args_compatible` + // to avoid needing to reintern the set of args... + if cfg!(debug_assertions) { + self.debug_assert_args_compatible( + def_id, + self.mk_args_from_iter( + [self.types.trait_object_dummy_self.into()].into_iter().chain(args.iter()), + ), + ); + } + } + + fn mk_type_list_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: CollectAndApply, &'tcx List>>, + { + self.mk_type_list_from_iter(args) + } + + fn parent(self, def_id: DefId) -> DefId { + self.parent(def_id) + } + + fn recursion_limit(self) -> usize { + self.recursion_limit().0 + } + + type Features = &'tcx rustc_feature::Features; + + fn features(self) -> Self::Features { + self.features() + } + + fn coroutine_hidden_types( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes>>> { + self.coroutine_hidden_types(def_id) + } + + fn fn_sig(self, def_id: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { + self.fn_sig(def_id) + } + + fn coroutine_movability(self, def_id: DefId) -> rustc_ast::Movability { + self.coroutine_movability(def_id) + } + + fn coroutine_for_closure(self, def_id: DefId) -> DefId { + self.coroutine_for_closure(def_id) + } + + fn generics_require_sized_self(self, def_id: DefId) -> bool { + self.generics_require_sized_self(def_id) + } + + fn item_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.item_bounds(def_id).map_bound(IntoIterator::into_iter) + } + + fn item_self_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.item_self_bounds(def_id).map_bound(IntoIterator::into_iter) + } + + fn item_non_self_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.item_non_self_bounds(def_id).map_bound(IntoIterator::into_iter) + } + + fn predicates_of( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + ty::EarlyBinder::bind( + self.predicates_of(def_id).instantiate_identity(self).predicates.into_iter(), + ) + } + + fn own_predicates_of( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + ty::EarlyBinder::bind( + self.predicates_of(def_id).instantiate_own_identity().map(|(clause, _)| clause), + ) + } + + fn explicit_super_predicates_of( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator, Span)>> { + self.explicit_super_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied()) + } + + fn explicit_implied_predicates_of( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator, Span)>> { + self.explicit_implied_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied()) + } + + fn impl_super_outlives( + self, + impl_def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.impl_super_outlives(impl_def_id) + } + + fn impl_is_const(self, def_id: DefId) -> bool { + debug_assert_matches!(self.def_kind(def_id), DefKind::Impl { of_trait: true }); + self.is_conditionally_const(def_id) + } + + fn fn_is_const(self, def_id: DefId) -> bool { + debug_assert_matches!( + self.def_kind(def_id), + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(CtorOf::Struct, CtorKind::Fn) + ); + self.is_conditionally_const(def_id) + } + + fn alias_has_const_conditions(self, def_id: DefId) -> bool { + debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::OpaqueTy); + self.is_conditionally_const(def_id) + } + + fn const_conditions( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>>> { + ty::EarlyBinder::bind( + self.const_conditions(def_id).instantiate_identity(self).into_iter().map(|(c, _)| c), + ) + } + + fn explicit_implied_const_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>>> { + ty::EarlyBinder::bind( + self.explicit_implied_const_bounds(def_id).iter_identity_copied().map(|(c, _)| c), + ) + } + + fn impl_self_is_guaranteed_unsized(self, impl_def_id: DefId) -> bool { + self.impl_self_is_guaranteed_unsized(impl_def_id) + } + + fn has_target_features(self, def_id: DefId) -> bool { + !self.codegen_fn_attrs(def_id).target_features.is_empty() + } + + fn require_lang_item(self, lang_item: SolverLangItem) -> DefId { + self.require_lang_item(solver_lang_item_to_lang_item(lang_item), DUMMY_SP) + } + + fn require_trait_lang_item(self, lang_item: SolverTraitLangItem) -> DefId { + self.require_lang_item(solver_trait_lang_item_to_lang_item(lang_item), DUMMY_SP) + } + + fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> DefId { + self.require_lang_item(solver_adt_lang_item_to_lang_item(lang_item), DUMMY_SP) + } + + fn is_lang_item(self, def_id: DefId, lang_item: SolverLangItem) -> bool { + self.is_lang_item(def_id, solver_lang_item_to_lang_item(lang_item)) + } + + fn is_trait_lang_item(self, def_id: DefId, lang_item: SolverTraitLangItem) -> bool { + self.is_lang_item(def_id, solver_trait_lang_item_to_lang_item(lang_item)) + } + + fn is_adt_lang_item(self, def_id: DefId, lang_item: SolverAdtLangItem) -> bool { + self.is_lang_item(def_id, solver_adt_lang_item_to_lang_item(lang_item)) + } + + fn is_default_trait(self, def_id: DefId) -> bool { + self.is_default_trait(def_id) + } + + fn is_sizedness_trait(self, def_id: DefId) -> bool { + self.is_sizedness_trait(def_id) + } + + fn as_lang_item(self, def_id: DefId) -> Option { + lang_item_to_solver_lang_item(self.lang_items().from_def_id(def_id)?) + } + + fn as_trait_lang_item(self, def_id: DefId) -> Option { + lang_item_to_solver_trait_lang_item(self.lang_items().from_def_id(def_id)?) + } + + fn as_adt_lang_item(self, def_id: DefId) -> Option { + lang_item_to_solver_adt_lang_item(self.lang_items().from_def_id(def_id)?) + } + + fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator { + self.associated_items(def_id) + .in_definition_order() + .filter(|assoc_item| assoc_item.is_type()) + .map(|assoc_item| assoc_item.def_id) + } + + // This implementation is a bit different from `TyCtxt::for_each_relevant_impl`, + // since we want to skip over blanket impls for non-rigid aliases, and also we + // only want to consider types that *actually* unify with float/int vars. + fn for_each_relevant_impl( + self, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + mut f: impl FnMut(DefId), + ) { + let tcx = self; + let trait_impls = tcx.trait_impls_of(trait_def_id); + let mut consider_impls_for_simplified_type = |simp| { + if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { + for &impl_def_id in impls_for_type { + f(impl_def_id); + } + } + }; + + match self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Pat(_, _) + | ty::Slice(_) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(..) + | ty::Dynamic(_, _) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(_, _) + | ty::Never + | ty::Tuple(_) + | ty::UnsafeBinder(_) => { + if let Some(simp) = ty::fast_reject::simplify_type( + tcx, + self_ty, + ty::fast_reject::TreatParams::AsRigid, + ) { + consider_impls_for_simplified_type(simp); + } + } + + // HACK: For integer and float variables we have to manually look at all impls + // which have some integer or float as a self type. + ty::Infer(ty::IntVar(_)) => { + use ty::IntTy::*; + use ty::UintTy::*; + // This causes a compiler error if any new integer kinds are added. + let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy; + let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy; + let possible_integers = [ + // signed integers + ty::SimplifiedType::Int(I8), + ty::SimplifiedType::Int(I16), + ty::SimplifiedType::Int(I32), + ty::SimplifiedType::Int(I64), + ty::SimplifiedType::Int(I128), + ty::SimplifiedType::Int(Isize), + // unsigned integers + ty::SimplifiedType::Uint(U8), + ty::SimplifiedType::Uint(U16), + ty::SimplifiedType::Uint(U32), + ty::SimplifiedType::Uint(U64), + ty::SimplifiedType::Uint(U128), + ty::SimplifiedType::Uint(Usize), + ]; + for simp in possible_integers { + consider_impls_for_simplified_type(simp); + } + } + + ty::Infer(ty::FloatVar(_)) => { + // This causes a compiler error if any new float kinds are added. + let (ty::FloatTy::F16 | ty::FloatTy::F32 | ty::FloatTy::F64 | ty::FloatTy::F128); + let possible_floats = [ + ty::SimplifiedType::Float(ty::FloatTy::F16), + ty::SimplifiedType::Float(ty::FloatTy::F32), + ty::SimplifiedType::Float(ty::FloatTy::F64), + ty::SimplifiedType::Float(ty::FloatTy::F128), + ]; + + for simp in possible_floats { + consider_impls_for_simplified_type(simp); + } + } + + // The only traits applying to aliases and placeholders are blanket impls. + // + // Impls which apply to an alias after normalization are handled by + // `assemble_candidates_after_normalizing_self_ty`. + ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (), + + // FIXME: These should ideally not exist as a self type. It would be nice for + // the builtin auto trait impls of coroutines to instead directly recurse + // into the witness. + ty::CoroutineWitness(..) => (), + + // These variants should not exist as a self type. + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Param(_) + | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"), + } + + #[allow(rustc::usage_of_type_ir_traits)] + self.for_each_blanket_impl(trait_def_id, f) + } + fn for_each_blanket_impl(self, trait_def_id: DefId, mut f: impl FnMut(DefId)) { + let trait_impls = self.trait_impls_of(trait_def_id); + for &impl_def_id in trait_impls.blanket_impls() { + f(impl_def_id); + } + } + + fn has_item_definition(self, def_id: DefId) -> bool { + self.defaultness(def_id).has_value() + } + + fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool { + self.specializes((impl_def_id, victim_def_id)) + } + + fn impl_is_default(self, impl_def_id: DefId) -> bool { + self.defaultness(impl_def_id).is_default() + } + + fn impl_trait_ref(self, impl_def_id: DefId) -> ty::EarlyBinder<'tcx, ty::TraitRef<'tcx>> { + self.impl_trait_ref(impl_def_id) + } + + fn impl_polarity(self, impl_def_id: DefId) -> ty::ImplPolarity { + self.impl_polarity(impl_def_id) + } + + fn trait_is_auto(self, trait_def_id: DefId) -> bool { + self.trait_is_auto(trait_def_id) + } + + fn trait_is_coinductive(self, trait_def_id: DefId) -> bool { + self.trait_is_coinductive(trait_def_id) + } + + fn trait_is_alias(self, trait_def_id: DefId) -> bool { + self.trait_is_alias(trait_def_id) + } + + fn trait_is_dyn_compatible(self, trait_def_id: DefId) -> bool { + self.is_dyn_compatible(trait_def_id) + } + + fn trait_is_fundamental(self, def_id: DefId) -> bool { + self.trait_def(def_id).is_fundamental + } + + fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool { + self.trait_def(trait_def_id).safety.is_unsafe() + } + + fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { + self.is_impl_trait_in_trait(def_id) + } + + fn delay_bug(self, msg: impl ToString) -> ErrorGuaranteed { + self.dcx().span_delayed_bug(DUMMY_SP, msg.to_string()) + } + + fn is_general_coroutine(self, coroutine_def_id: DefId) -> bool { + self.is_general_coroutine(coroutine_def_id) + } + + fn coroutine_is_async(self, coroutine_def_id: DefId) -> bool { + self.coroutine_is_async(coroutine_def_id) + } + + fn coroutine_is_gen(self, coroutine_def_id: DefId) -> bool { + self.coroutine_is_gen(coroutine_def_id) + } + + fn coroutine_is_async_gen(self, coroutine_def_id: DefId) -> bool { + self.coroutine_is_async_gen(coroutine_def_id) + } + + type UnsizingParams = &'tcx rustc_index::bit_set::DenseBitSet; + fn unsizing_params_for_adt(self, adt_def_id: DefId) -> Self::UnsizingParams { + self.unsizing_params_for_adt(adt_def_id) + } + + fn anonymize_bound_vars>>( + self, + binder: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.anonymize_bound_vars(binder) + } + + fn opaque_types_defined_by(self, defining_anchor: LocalDefId) -> Self::LocalDefIds { + self.opaque_types_defined_by(defining_anchor) + } + + fn opaque_types_and_coroutines_defined_by( + self, + defining_anchor: Self::LocalDefId, + ) -> Self::LocalDefIds { + let coroutines_defined_by = self + .nested_bodies_within(defining_anchor) + .iter() + .filter(|def_id| self.is_coroutine(def_id.to_def_id())); + self.mk_local_def_ids_from_iter( + self.opaque_types_defined_by(defining_anchor).iter().chain(coroutines_defined_by), + ) + } + + type Probe = &'tcx inspect::Probe>; + fn mk_probe(self, probe: inspect::Probe) -> &'tcx inspect::Probe> { + self.arena.alloc(probe) + } + fn evaluate_root_goal_for_proof_tree_raw( + self, + canonical_goal: CanonicalInput<'tcx>, + ) -> (QueryResult<'tcx>, &'tcx inspect::Probe>) { + self.evaluate_root_goal_for_proof_tree_raw(canonical_goal) + } + + fn item_name(self, id: DefId) -> Symbol { + let id = id.into_query_param(); + self.opt_item_name(id).unwrap_or_else(|| { + bug!("item_name: no name for {:?}", self.def_path(id)); + }) + } +} + +/// Defines trivial conversion functions between the main [`LangItem`] enum, +/// and some other lang-item enum that is a subset of it. +macro_rules! bidirectional_lang_item_map { + ( + $solver_ty:ident, fn $to_solver:ident, fn $from_solver:ident; + $($name:ident),+ $(,)? + ) => { + fn $from_solver(lang_item: $solver_ty) -> LangItem { + match lang_item { + $($solver_ty::$name => LangItem::$name,)+ + } + } + + fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { + Some(match lang_item { + $(LangItem::$name => $solver_ty::$name,)+ + _ => return None, + }) + } + } +} + +bidirectional_lang_item_map! { + SolverLangItem, fn lang_item_to_solver_lang_item, fn solver_lang_item_to_lang_item; + +// tidy-alphabetical-start + AsyncFnKindUpvars, + AsyncFnOnceOutput, + CallOnceFuture, + CallRefFuture, + CoroutineReturn, + CoroutineYield, + DynMetadata, + FutureOutput, + Metadata, +// tidy-alphabetical-end +} + +bidirectional_lang_item_map! { + SolverAdtLangItem, fn lang_item_to_solver_adt_lang_item, fn solver_adt_lang_item_to_lang_item; + +// tidy-alphabetical-start + Option, + Poll, +// tidy-alphabetical-end +} + +bidirectional_lang_item_map! { + SolverTraitLangItem, fn lang_item_to_solver_trait_lang_item, fn solver_trait_lang_item_to_lang_item; + +// tidy-alphabetical-start + AsyncFn, + AsyncFnKindHelper, + AsyncFnMut, + AsyncFnOnce, + AsyncFnOnceOutput, + AsyncIterator, + BikeshedGuaranteedNoDrop, + Clone, + Copy, + Coroutine, + Destruct, + DiscriminantKind, + Drop, + Fn, + FnMut, + FnOnce, + FnPtrTrait, + FusedIterator, + Future, + Iterator, + MetaSized, + PointeeSized, + PointeeTrait, + Sized, + TransmuteTrait, + TrivialClone, + Tuple, + Unpin, + Unsize, +// tidy-alphabetical-end +} diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index fa9995898ac2..a06f92792820 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -16,8 +16,7 @@ pub struct ImplicitCtxt<'a, 'tcx> { /// The current `TyCtxt`. pub tcx: TyCtxt<'tcx>, - /// The current query job, if any. This is updated by `JobOwner::start` in - /// `ty::query::plumbing` when executing a query. + /// The current query job, if any. pub query: Option, /// Used to prevent queries from calling too deeply. diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index b6b10e245857..ed587cbc3c28 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -284,7 +284,7 @@ impl<'tcx> Generics { args: &'a [ty::GenericArg<'tcx>], ) -> &'a [ty::GenericArg<'tcx>] { let mut own_params = self.parent_count..self.count(); - if self.has_self && self.parent.is_none() { + if self.has_own_self() { own_params.start = 1; } @@ -316,7 +316,7 @@ impl<'tcx> Generics { args: &'tcx [ty::GenericArg<'tcx>], ) -> &'tcx [ty::GenericArg<'tcx>] { let own = &args[self.parent_count..][..self.own_params.len()]; - if self.has_self && self.parent.is_none() { &own[1..] } else { own } + if self.has_own_self() { &own[1..] } else { own } } /// Returns true if a concrete type is specified after a default type. @@ -350,6 +350,10 @@ impl<'tcx> Generics { pub fn is_own_empty(&'tcx self) -> bool { self.own_params.is_empty() } + + pub fn has_own_self(&'tcx self) -> bool { + self.has_self && self.parent.is_none() + } } /// Bounds on generics. diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index ac45ce887c9a..95a1a1bf5bce 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -20,6 +20,9 @@ where T: HashStable>, { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + // Note: this cache makes an *enormous* performance difference on certain benchmarks. E.g. + // without it, compiling `diesel-2.2.10` can be 74% slower, and compiling + // `deeply-nested-multi` can be ~4,000x slower(!) thread_local! { static CACHE: RefCell> = RefCell::new(Default::default()); diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs index d03e593e37b9..c935869b504c 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs @@ -2,8 +2,7 @@ use rustc_macros::HashStable; use smallvec::SmallVec; use tracing::instrument; -use crate::ty::context::TyCtxt; -use crate::ty::{self, DefId, OpaqueTypeKey, Ty, TypingEnv}; +use crate::ty::{self, DefId, OpaqueTypeKey, Ty, TyCtxt, TypingEnv}; /// Represents whether some type is inhabited in a given context. /// Examples of uninhabited types are `!`, `enum Void {}`, or a struct diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 7eb74b52b44c..5c4c1733be29 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -47,8 +47,7 @@ use rustc_type_ir::TyKind::*; use tracing::instrument; use crate::query::Providers; -use crate::ty::context::TyCtxt; -use crate::ty::{self, DefId, Ty, TypeVisitableExt, VariantDef, Visibility}; +use crate::ty::{self, DefId, Ty, TyCtxt, TypeVisitableExt, VariantDef, Visibility}; pub mod inhabited_predicate; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0220531b09fa..37871aad4a77 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -76,8 +76,8 @@ pub use self::closure::{ place_to_string_for_capture, }; pub use self::consts::{ - AnonConstKind, AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, Expr, - ExprKind, ScalarInt, SimdAlign, UnevaluatedConst, ValTree, ValTreeKindExt, Value, + AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, Expr, ExprKind, ScalarInt, + SimdAlign, UnevaluatedConst, ValTree, ValTreeKindExt, Value, }; pub use self::context::{ CtxtInterners, CurrentGcx, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, tls, diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 69dcca54f469..3baeb7141de5 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -49,10 +49,6 @@ impl<'tcx> rustc_type_ir::inherent::Predicate> for Predicate<'tcx> fn as_clause(self) -> Option> { self.as_clause() } - - fn allow_normalization(self) -> bool { - self.allow_normalization() - } } impl<'tcx> rustc_type_ir::inherent::IntoKind for Predicate<'tcx> { @@ -121,25 +117,7 @@ impl<'tcx> Predicate<'tcx> { /// unsoundly accept some programs. See #91068. #[inline] pub fn allow_normalization(self) -> bool { - match self.kind().skip_binder() { - PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) => { - false - } - PredicateKind::Clause(ClauseKind::Trait(_)) - | PredicateKind::Clause(ClauseKind::HostEffect(..)) - | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) - | PredicateKind::Clause(ClauseKind::TypeOutlives(_)) - | PredicateKind::Clause(ClauseKind::Projection(_)) - | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) - | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) - | PredicateKind::DynCompatible(_) - | PredicateKind::Subtype(_) - | PredicateKind::Coerce(_) - | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) - | PredicateKind::ConstEquate(_, _) - | PredicateKind::NormalizesTo(..) - | PredicateKind::Ambiguous => true, - } + rustc_type_ir::inherent::Predicate::allow_normalization(self) } } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index d6e6ffae7300..e01a5721a5e0 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -10,11 +10,11 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hashes::Hash128; -use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_hir::limit::Limit; +use rustc_hir::{self as hir, find_attr}; use rustc_index::bit_set::GrowableBitSet; use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension}; use rustc_span::sym; @@ -1679,7 +1679,9 @@ pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { /// the compiler to make some assumptions about its shape; if the user doesn't use a feature gate, they may /// cause an ICE that we otherwise may want to prevent. pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - if tcx.features().intrinsics() && tcx.has_attr(def_id, sym::rustc_intrinsic) { + if tcx.features().intrinsics() + && find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcIntrinsic) + { let must_be_overridden = match tcx.hir_node_by_def_id(def_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { has_body, .. }, .. }) => { !has_body @@ -1689,7 +1691,10 @@ pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option - [1] feature - *[count] features - }: {$missing_target_features} - .note = the {$build_target_features} target {$build_target_features_count -> - [1] feature - *[count] features - } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count -> - [1] it - *[count] them - } in `#[target_feature]` - .label = call to function with `#[target_feature]` - -mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe function or block - .help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> - [1] feature - *[count] features - }: {$missing_target_features} - .note = the {$build_target_features} target {$build_target_features_count -> - [1] feature - *[count] features - } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count -> - [1] it - *[count] them - } in `#[target_feature]` - .label = call to function with `#[target_feature]` - -mir_build_call_to_unsafe_fn_requires_unsafe = - call to unsafe function `{$function}` is unsafe and requires unsafe block - .note = consult the function's documentation for information on how to avoid undefined behavior - .label = call to unsafe function - -mir_build_call_to_unsafe_fn_requires_unsafe_nameless = - call to unsafe function is unsafe and requires unsafe block - .note = consult the function's documentation for information on how to avoid undefined behavior - .label = call to unsafe function - -mir_build_call_to_unsafe_fn_requires_unsafe_nameless_unsafe_op_in_unsafe_fn_allowed = - call to unsafe function is unsafe and requires unsafe function or block - .note = consult the function's documentation for information on how to avoid undefined behavior - .label = call to unsafe function - -mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - call to unsafe function `{$function}` is unsafe and requires unsafe function or block - .note = consult the function's documentation for information on how to avoid undefined behavior - .label = call to unsafe function - -mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable - -mir_build_const_continue_bad_const = could not determine the target branch for this `#[const_continue]` - .label = this value is too generic - -mir_build_const_continue_missing_label_or_value = a `#[const_continue]` must break to a label with a value - -mir_build_const_continue_not_const = could not determine the target branch for this `#[const_continue]` - .help = try extracting the expression into a `const` item - -mir_build_const_continue_not_const_const_block = `const` blocks may use generics, and are not evaluated early enough -mir_build_const_continue_not_const_const_other = this value must be a literal or a monomorphic const -mir_build_const_continue_not_const_constant_parameter = constant parameters may use generics, and are not evaluated early enough - -mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known - .label = this value must be a literal or a monomorphic const - -mir_build_const_defined_here = constant defined here - -mir_build_const_param_in_pattern = constant parameters cannot be referenced in patterns - .label = can't be used in patterns -mir_build_const_param_in_pattern_def = constant defined here - -mir_build_const_pattern_depends_on_generic_parameter = constant pattern cannot depend on generic parameters - .label = `const` depends on a generic parameter - -mir_build_could_not_eval_const_pattern = could not evaluate constant pattern - .label = could not evaluate constant - -mir_build_deref_raw_pointer_requires_unsafe = - dereference of raw pointer is unsafe and requires unsafe block - .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior - .label = dereference of raw pointer - -mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - dereference of raw pointer is unsafe and requires unsafe function or block - .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior - .label = dereference of raw pointer - -mir_build_extern_static_requires_unsafe = - use of extern static is unsafe and requires unsafe block - .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior - .label = use of extern static - -mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - use of extern static is unsafe and requires unsafe function or block - .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior - .label = use of extern static - -mir_build_inform_irrefutable = `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant - -mir_build_initializing_type_with_requires_unsafe = - initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe block - .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior - .label = initializing type with `rustc_layout_scalar_valid_range` attr - -mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block - .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior - .label = initializing type with `rustc_layout_scalar_valid_range` attr - -mir_build_initializing_type_with_unsafe_field_requires_unsafe = - initializing type with an unsafe field is unsafe and requires unsafe block - .note = unsafe fields may carry library invariants - .label = initialization of struct with unsafe field - -mir_build_initializing_type_with_unsafe_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - initializing type with an unsafe field is unsafe and requires unsafe block - .note = unsafe fields may carry library invariants - .label = initialization of struct with unsafe field - -mir_build_inline_assembly_requires_unsafe = - use of inline assembly is unsafe and requires unsafe block - .note = inline assembly is entirely unchecked and can cause undefined behavior - .label = use of inline assembly - -mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - use of inline assembly is unsafe and requires unsafe function or block - .note = inline assembly is entirely unchecked and can cause undefined behavior - .label = use of inline assembly - -mir_build_interpreted_as_const = introduce a variable instead - -mir_build_invalid_pattern = {$prefix} `{$non_sm_ty}` cannot be used in patterns - .label = {$prefix} can't be used in patterns - -mir_build_irrefutable_let_patterns_if_let = irrefutable `if let` {$count -> - [one] pattern - *[other] patterns - } - .note = {$count -> - [one] this pattern - *[other] these patterns - } will always match, so the `if let` is useless - .help = consider replacing the `if let` with a `let` - -mir_build_irrefutable_let_patterns_if_let_guard = irrefutable `if let` guard {$count -> - [one] pattern - *[other] patterns - } - .note = {$count -> - [one] this pattern - *[other] these patterns - } will always match, so the guard is useless - .help = consider removing the guard and adding a `let` inside the match arm - -mir_build_irrefutable_let_patterns_let_else = irrefutable `let...else` {$count -> - [one] pattern - *[other] patterns - } - .note = {$count -> - [one] this pattern - *[other] these patterns - } will always match, so the `else` clause is useless - .help = consider removing the `else` clause - -mir_build_irrefutable_let_patterns_while_let = irrefutable `while let` {$count -> - [one] pattern - *[other] patterns - } - .note = {$count -> - [one] this pattern - *[other] these patterns - } will always match, so the loop will never exit - .help = consider instead using a `loop {"{"} ... {"}"}` with a `let` inside it - -mir_build_leading_irrefutable_let_patterns = leading irrefutable {$count -> - [one] pattern - *[other] patterns - } in let chain - .note = {$count -> - [one] this pattern - *[other] these patterns - } will always match - .help = consider moving {$count -> - [one] it - *[other] them - } outside of the construct - -mir_build_literal_in_range_out_of_bounds = - literal out of range for `{$ty}` - .label = this value does not fit into the type `{$ty}` whose range is `{$min}..={$max}` - -mir_build_loop_match_arm_with_guard = - match arms that are part of a `#[loop_match]` cannot have guards - -mir_build_loop_match_bad_rhs = - this expression must be a single `match` wrapped in a labeled block - -mir_build_loop_match_bad_statements = - statements are not allowed in this position within a `#[loop_match]` - -mir_build_loop_match_invalid_match = - invalid match on `#[loop_match]` state - .note = a local variable must be the scrutinee within a `#[loop_match]` - -mir_build_loop_match_invalid_update = - invalid update of the `#[loop_match]` state - .label = the assignment must update this variable - -mir_build_loop_match_missing_assignment = - expected a single assignment expression - -mir_build_loop_match_unsupported_type = - this `#[loop_match]` state value has type `{$ty}`, which is not supported - .note = only integers, floats, bool, char, and enums without fields are supported - -mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper = - lower bound for range pattern must be less than or equal to upper bound - .label = lower bound larger than upper bound - .teach_note = When matching against a range, the compiler verifies that the range is non-empty. Range patterns include both end-points, so this is equivalent to requiring the start of the range to be less than or equal to the end of the range. - -mir_build_lower_range_bound_must_be_less_than_upper = lower bound for range pattern must be less than upper bound - -mir_build_more_information = for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html - -mir_build_moved = value is moved into `{$name}` here - -mir_build_moved_while_borrowed = cannot move out of value because it is borrowed - -mir_build_multiple_mut_borrows = cannot borrow value as mutable more than once at a time - -mir_build_mutable_borrow = value is mutably borrowed by `{$name}` here - -mir_build_mutable_static_requires_unsafe = - use of mutable static is unsafe and requires unsafe block - .note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior - .label = use of mutable static - -mir_build_mutable_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - use of mutable static is unsafe and requires unsafe function or block - .note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior - .label = use of mutable static - -mir_build_mutation_of_layout_constrained_field_requires_unsafe = - mutation of layout constrained field is unsafe and requires unsafe block - .note = mutating layout constrained fields cannot statically be checked for valid values - .label = mutation of layout constrained field - -mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - mutation of layout constrained field is unsafe and requires unsafe function or block - .note = mutating layout constrained fields cannot statically be checked for valid values - .label = mutation of layout constrained field - -mir_build_nan_pattern = cannot use NaN in patterns - .label = evaluates to `NaN`, which is not allowed in patterns - .note = NaNs compare inequal to everything, even themselves, so this pattern would never match - .help = try using the `is_nan` method instead - -mir_build_non_const_path = runtime values cannot be referenced in patterns - .label = references a runtime value - -mir_build_non_empty_never_pattern = - mismatched types - .label = a never pattern must be used on an uninhabited type - .note = the matched value is of type `{$ty}` - -mir_build_non_exhaustive_match_all_arms_guarded = - match arms with guards don't count towards exhaustivity - -mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type `{$ty}` is non-empty - .def_note = `{$peeled_ty}` defined here - .type_note = the matched value is of type `{$ty}` - .non_exhaustive_type_note = the matched value is of type `{$ty}`, which is marked as non-exhaustive - .reference_note = references are always considered inhabited - .suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - .help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern - -mir_build_non_partial_eq_match = constant of non-structural type `{$ty}` in a pattern - .label = constant of non-structural type - -mir_build_pattern_not_covered = refutable pattern in {$origin} - .pattern_ty = the matched value is of type `{$pattern_ty}` - -mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon - .label = can't be used in patterns - .note = see https://github.com/rust-lang/rust/issues/70861 for details - -mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future - -mir_build_static_in_pattern = statics cannot be referenced in patterns - .label = can't be used in patterns -mir_build_static_in_pattern_def = `static` defined here - -mir_build_suggest_attempted_int_lit = alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits - - -mir_build_suggest_if_let = you might want to use `if let` to ignore the {$count -> - [one] variant that isn't - *[other] variants that aren't - } matched - -mir_build_suggest_let_else = you might want to use `let...else` to handle the {$count -> - [one] variant that isn't - *[other] variants that aren't - } matched - -mir_build_trailing_irrefutable_let_patterns = trailing irrefutable {$count -> - [one] pattern - *[other] patterns - } in let chain - .note = {$count -> - [one] this pattern - *[other] these patterns - } will always match - .help = consider moving {$count -> - [one] it - *[other] them - } into the body - -mir_build_type_not_structural = constant of non-structural type `{$ty}` in a pattern - .label = constant of non-structural type -mir_build_type_not_structural_def = `{$ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns -mir_build_type_not_structural_more_info = see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -mir_build_type_not_structural_tip = - the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details - -mir_build_union_field_requires_unsafe = - access to union field is unsafe and requires unsafe block - .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior - .label = access to union field - -mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - access to union field is unsafe and requires unsafe function or block - .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior - .label = access to union field - -mir_build_union_pattern = cannot use unions in constant patterns - .label = can't use a `union` here - -mir_build_unreachable_due_to_uninhabited = unreachable {$descr} - .label = unreachable {$descr} - .label_orig = any code following this expression is unreachable - .note = this expression has type `{$ty}`, which is uninhabited - -mir_build_unreachable_making_this_unreachable = collectively making this unreachable - -mir_build_unreachable_making_this_unreachable_n_more = ...and {$covered_by_many_n_more_count} other patterns collectively make this unreachable - -mir_build_unreachable_matches_same_values = matches some of the same values - -mir_build_unreachable_pattern = unreachable pattern - .label = no value can reach this - .unreachable_matches_no_values = matches no values because `{$matches_no_values_ty}` is uninhabited - .unreachable_uninhabited_note = to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types - .unreachable_covered_by_catchall = matches any value - .unreachable_covered_by_one = matches all the relevant values - .unreachable_covered_by_many = multiple earlier patterns match some of the same values - .unreachable_pattern_const_reexport_accessible = there is a constant of the same name imported in another scope, which could have been used to pattern match against its value instead of introducing a new catch-all binding, but it needs to be imported in the pattern's scope - .unreachable_pattern_wanted_const = you might have meant to pattern match against the value of {$is_typo -> - [true] similarly named constant - *[false] constant - } `{$const_name}` instead of introducing a new catch-all binding - .unreachable_pattern_const_inaccessible = there is a constant of the same name, which could have been used to pattern match against its value instead of introducing a new catch-all binding, but it is not accessible from this scope - .unreachable_pattern_let_binding = there is a binding of the same name; if you meant to pattern match against the value of that binding, that is a feature of constants that is not available for `let` bindings - .suggestion = remove the match arm - -mir_build_unsafe_binder_cast_requires_unsafe = - unsafe binder cast is unsafe and requires unsafe block - .label = unsafe binder cast - .note = casting to or from an `unsafe<...>` binder type is unsafe since it erases lifetime - information that may be required to uphold safety guarantees of a type - -mir_build_unsafe_binder_cast_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - unsafe binder cast is unsafe and requires unsafe block or unsafe fn - .label = unsafe binder cast - .note = casting to or from an `unsafe<...>` binder type is unsafe since it erases lifetime - information that may be required to uphold safety guarantees of a type - -mir_build_unsafe_field_requires_unsafe = - use of unsafe field is unsafe and requires unsafe block - .note = unsafe fields may carry library invariants - .label = use of unsafe field - -mir_build_unsafe_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = - use of unsafe field is unsafe and requires unsafe block - .note = unsafe fields may carry library invariants - .label = use of unsafe field - -mir_build_unsafe_fn_safe_body = an unsafe function restricts its caller, but its body is safe by default -mir_build_unsafe_not_inherited = items do not inherit unsafety from separate enclosing items - -mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe = - borrow of layout constrained field with interior mutability is unsafe and requires unsafe block - .note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values - .label = borrow of layout constrained field with interior mutability - -mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe = - call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block - .help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> - [1] feature - *[count] features - }: {$missing_target_features} - .note = the {$build_target_features} target {$build_target_features_count -> - [1] feature - *[count] features - } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count -> - [1] it - *[count] them - } in `#[target_feature]` - .label = call to function with `#[target_feature]` - -mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe = - call to unsafe function `{$function}` is unsafe and requires unsafe block - .note = consult the function's documentation for information on how to avoid undefined behavior - .label = call to unsafe function - -mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless = - call to unsafe function is unsafe and requires unsafe block - .note = consult the function's documentation for information on how to avoid undefined behavior - .label = call to unsafe function - -mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe = - dereference of raw pointer is unsafe and requires unsafe block - .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior - .label = dereference of raw pointer - -mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe = - use of extern static is unsafe and requires unsafe block - .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior - .label = use of extern static - -mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe = - initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe block - .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior - .label = initializing type with `rustc_layout_scalar_valid_range` attr - -mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_unsafe_field_requires_unsafe = - initializing type with an unsafe field is unsafe and requires unsafe block - .note = unsafe fields may carry library invariants - .label = initialization of struct with unsafe field - -mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe = - use of inline assembly is unsafe and requires unsafe block - .note = inline assembly is entirely unchecked and can cause undefined behavior - .label = use of inline assembly - -mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe = - use of mutable static is unsafe and requires unsafe block - .note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior - .label = use of mutable static - -mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe = - mutation of layout constrained field is unsafe and requires unsafe block - .note = mutating layout constrained fields cannot statically be checked for valid values - .label = mutation of layout constrained field - -mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe = - access to union field is unsafe and requires unsafe block - .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior - .label = access to union field - -mir_build_unsafe_op_in_unsafe_fn_unsafe_field_requires_unsafe = - use of unsafe field is unsafe and requires unsafe block - .note = unsafe fields may carry library invariants - .label = use of unsafe field - -mir_build_unsized_pattern = cannot use unsized non-slice type `{$non_sm_ty}` in constant patterns - -mir_build_unused_unsafe = unnecessary `unsafe` block - .label = unnecessary `unsafe` block - -mir_build_unused_unsafe_enclosing_block_label = because it's nested under this `unsafe` block - -mir_build_upper_range_bound_cannot_be_min = exclusive upper bound for a range bound cannot be the minimum - -mir_build_variant_defined_here = not covered - -mir_build_wrap_suggestion = consider wrapping the function body in an unsafe block diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index 792ad6d782cf..1005dd30d73f 100644 --- a/compiler/rustc_mir_build/src/builder/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -85,7 +85,7 @@ pub(super) fn build_custom_mir<'tcx>( block_map: FxHashMap::default(), }; - let res: PResult<_> = try { + let res = try { pctxt.parse_args(params)?; pctxt.parse_body(expr)?; }; diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 317df4e64fdb..fbd7aa90f49c 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -578,7 +578,7 @@ fn construct_const<'a, 'tcx>( }) | Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(ty, _), span, .. }) | Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Const(ty, Some(_)), + kind: hir::TraitItemKind::Const(ty, Some(_), _), span, .. }) => (*span, ty.span), diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 4a20b05d1fc2..7bced8168bd1 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint::Level; use rustc_session::lint::builtin::{DEPRECATED_SAFE_2024, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; use crate::builder::ExprCategory; use crate::errors::*; @@ -98,29 +98,14 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { // from an edition before 2024. &UnsafeOpKind::CallToUnsafeFunction(Some(id)) if !span.at_least_rust_2024() - && let Some(attr) = self.tcx.get_attr(id, sym::rustc_deprecated_safe_2024) => + && let Some(suggestion) = find_attr!(self.tcx.get_all_attrs(id), AttributeKind::RustcDeprecatedSafe2024{suggestion} => suggestion) => { - let suggestion = attr - .meta_item_list() - .unwrap_or_default() - .into_iter() - .find(|item| item.has_name(sym::audit_that)) - .map(|item| { - item.value_str().expect( - "`#[rustc_deprecated_safe_2024(audit_that)]` must have a string value", - ) - }); - let sm = self.tcx.sess.source_map(); - let guarantee = suggestion - .as_ref() - .map(|suggestion| format!("that {}", suggestion)) - .unwrap_or_else(|| String::from("its unsafe preconditions")); - let suggestion = suggestion - .and_then(|suggestion| { - sm.indentation_before(span).map(|indent| { - format!("{}// TODO: Audit that {}.\n", indent, suggestion) // ignore-tidy-todo - }) + let guarantee = format!("that {}", suggestion); + let suggestion = sm + .indentation_before(span) + .map(|indent| { + format!("{}// TODO: Audit that {}.\n", indent, suggestion) // ignore-tidy-todo }) .unwrap_or_default(); diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 64e2bb3207c8..fed0435f59a0 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1,7 +1,7 @@ use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, - MultiSpan, Subdiagnostic, + MultiSpan, Subdiagnostic, inline_fluent, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; @@ -9,12 +9,10 @@ use rustc_pattern_analysis::errors::Uncovered; use rustc_pattern_analysis::rustc::RustcPatCtxt; use rustc_span::{Ident, Span, Symbol}; -use crate::fluent_generated as fluent; - #[derive(LintDiagnostic)] -#[diag(mir_build_call_to_deprecated_safe_fn_requires_unsafe)] +#[diag("call to deprecated safe function `{$function}` is unsafe and requires unsafe block")] pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe { - #[label] + #[label("call to unsafe function")] pub(crate) span: Span, pub(crate) function: String, pub(crate) guarantee: String, @@ -23,7 +21,10 @@ pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe { } #[derive(Subdiagnostic)] -#[multipart_suggestion(mir_build_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + "you can wrap the call in an `unsafe` block if you can guarantee {$guarantee}", + applicability = "machine-applicable" +)] pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafeSub { pub(crate) start_of_line_suggestion: String, #[suggestion_part(code = "{start_of_line_suggestion}")] @@ -35,10 +36,10 @@ pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafeSub { } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe, code = E0133)] -#[note] +#[diag("call to unsafe function `{$function}` is unsafe and requires unsafe block", code = E0133)] +#[note("consult the function's documentation for information on how to avoid undefined behavior")] pub(crate) struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe { - #[label] + #[label("call to unsafe function")] pub(crate) span: Span, pub(crate) function: String, #[subdiagnostic] @@ -46,90 +47,100 @@ pub(crate) struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe { } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless, code = E0133)] -#[note] +#[diag("call to unsafe function is unsafe and requires unsafe block", code = E0133)] +#[note("consult the function's documentation for information on how to avoid undefined behavior")] pub(crate) struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { - #[label] + #[label("call to unsafe function")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe, code = E0133)] -#[note] +#[diag("use of inline assembly is unsafe and requires unsafe block", code = E0133)] +#[note("inline assembly is entirely unchecked and can cause undefined behavior")] pub(crate) struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { - #[label] + #[label("use of inline assembly")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe, code = E0133)] -#[note] +#[diag("initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe block", code = E0133)] +#[note( + "initializing a layout restricted type's field with a value outside the valid range is undefined behavior" +)] pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { - #[label] + #[label("initializing type with `rustc_layout_scalar_valid_range` attr")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_unsafe_field_requires_unsafe, code = E0133)] -#[note] +#[diag("initializing type with an unsafe field is unsafe and requires unsafe block", code = E0133)] +#[note("unsafe fields may carry library invariants")] pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithUnsafeFieldRequiresUnsafe { - #[label] + #[label("initialization of struct with unsafe field")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe, code = E0133)] -#[note] +#[diag("use of mutable static is unsafe and requires unsafe block", code = E0133)] +#[note( + "mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior" +)] pub(crate) struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { - #[label] + #[label("use of mutable static")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe, code = E0133)] -#[note] +#[diag("use of extern static is unsafe and requires unsafe block", code = E0133)] +#[note( + "extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior" +)] pub(crate) struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { - #[label] + #[label("use of extern static")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_unsafe_field_requires_unsafe, code = E0133)] -#[note] +#[diag("use of unsafe field is unsafe and requires unsafe block", code = E0133)] +#[note("unsafe fields may carry library invariants")] pub(crate) struct UnsafeOpInUnsafeFnUseOfUnsafeFieldRequiresUnsafe { - #[label] + #[label("use of unsafe field")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe, code = E0133)] -#[note] +#[diag("dereference of raw pointer is unsafe and requires unsafe block", code = E0133)] +#[note( + "raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior" +)] pub(crate) struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { - #[label] + #[label("dereference of raw pointer")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe, code = E0133)] -#[note] +#[diag("access to union field is unsafe and requires unsafe block", code = E0133)] +#[note( + "the field may not be properly initialized: using uninitialized data will cause undefined behavior" +)] pub(crate) struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { - #[label] + #[label("access to union field")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, @@ -137,12 +148,12 @@ pub(crate) struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { #[derive(LintDiagnostic)] #[diag( - mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe, + "mutation of layout constrained field is unsafe and requires unsafe block", code = E0133 )] -#[note] +#[note("mutating layout constrained fields cannot statically be checked for valid values")] pub(crate) struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { - #[label] + #[label("mutation of layout constrained field")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, @@ -150,11 +161,11 @@ pub(crate) struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsa #[derive(LintDiagnostic)] #[diag( - mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe, + "borrow of layout constrained field with interior mutability is unsafe and requires unsafe block", code = E0133, )] pub(crate) struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { - #[label] + #[label("borrow of layout constrained field with interior mutability")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, @@ -162,26 +173,37 @@ pub(crate) struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe #[derive(LintDiagnostic)] #[diag( - mir_build_unsafe_binder_cast_requires_unsafe, + "unsafe binder cast is unsafe and requires unsafe block information that may be required to uphold safety guarantees of a type", code = E0133, )] pub(crate) struct UnsafeOpInUnsafeFnUnsafeBinderCastRequiresUnsafe { - #[label] + #[label("unsafe binder cast")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe, code = E0133)] -#[help] +#[diag("call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block", code = E0133)] +#[help( + "in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> + [1] feature + *[count] features + }: {$missing_target_features}" +)] pub(crate) struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe { - #[label] + #[label("call to function with `#[target_feature]`")] pub(crate) span: Span, pub(crate) function: String, pub(crate) missing_target_features: DiagArgValue, pub(crate) missing_target_features_count: usize, - #[note] + #[note("the {$build_target_features} target {$build_target_features_count -> + [1] feature + *[count] features + } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count -> + [1] it + *[count] them + } in `#[target_feature]`")] pub(crate) note: bool, pub(crate) build_target_features: DiagArgValue, pub(crate) build_target_features_count: usize, @@ -190,11 +212,11 @@ pub(crate) struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe { } #[derive(Diagnostic)] -#[diag(mir_build_call_to_unsafe_fn_requires_unsafe, code = E0133)] -#[note] +#[diag("call to unsafe function `{$function}` is unsafe and requires unsafe block", code = E0133)] +#[note("consult the function's documentation for information on how to avoid undefined behavior")] pub(crate) struct CallToUnsafeFunctionRequiresUnsafe { #[primary_span] - #[label] + #[label("call to unsafe function")] pub(crate) span: Span, pub(crate) function: String, #[subdiagnostic] @@ -202,22 +224,22 @@ pub(crate) struct CallToUnsafeFunctionRequiresUnsafe { } #[derive(Diagnostic)] -#[diag(mir_build_call_to_unsafe_fn_requires_unsafe_nameless, code = E0133)] -#[note] +#[diag("call to unsafe function is unsafe and requires unsafe block", code = E0133)] +#[note("consult the function's documentation for information on how to avoid undefined behavior")] pub(crate) struct CallToUnsafeFunctionRequiresUnsafeNameless { #[primary_span] - #[label] + #[label("call to unsafe function")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] -#[note] +#[diag("call to unsafe function `{$function}` is unsafe and requires unsafe function or block", code = E0133)] +#[note("consult the function's documentation for information on how to avoid undefined behavior")] pub(crate) struct CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("call to unsafe function")] pub(crate) span: Span, pub(crate) function: String, #[subdiagnostic] @@ -226,57 +248,59 @@ pub(crate) struct CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[derive(Diagnostic)] #[diag( - mir_build_call_to_unsafe_fn_requires_unsafe_nameless_unsafe_op_in_unsafe_fn_allowed, + "call to unsafe function is unsafe and requires unsafe function or block", code = E0133 )] -#[note] +#[note("consult the function's documentation for information on how to avoid undefined behavior")] pub(crate) struct CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("call to unsafe function")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_inline_assembly_requires_unsafe, code = E0133)] -#[note] +#[diag("use of inline assembly is unsafe and requires unsafe block", code = E0133)] +#[note("inline assembly is entirely unchecked and can cause undefined behavior")] pub(crate) struct UseOfInlineAssemblyRequiresUnsafe { #[primary_span] - #[label] + #[label("use of inline assembly")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] -#[note] +#[diag("use of inline assembly is unsafe and requires unsafe function or block", code = E0133)] +#[note("inline assembly is entirely unchecked and can cause undefined behavior")] pub(crate) struct UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("use of inline assembly")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_initializing_type_with_requires_unsafe, code = E0133)] -#[note] +#[diag("initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe block", code = E0133)] +#[note( + "initializing a layout restricted type's field with a value outside the valid range is undefined behavior" +)] pub(crate) struct InitializingTypeWithRequiresUnsafe { #[primary_span] - #[label] + #[label("initializing type with `rustc_layout_scalar_valid_range` attr")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_initializing_type_with_unsafe_field_requires_unsafe, code = E0133)] -#[note] +#[diag("initializing type with an unsafe field is unsafe and requires unsafe block", code = E0133)] +#[note("unsafe fields may carry library invariants")] pub(crate) struct InitializingTypeWithUnsafeFieldRequiresUnsafe { #[primary_span] - #[label] + #[label("initialization of struct with unsafe field")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, @@ -284,13 +308,15 @@ pub(crate) struct InitializingTypeWithUnsafeFieldRequiresUnsafe { #[derive(Diagnostic)] #[diag( - mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + "initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block", code = E0133 )] -#[note] +#[note( + "initializing a layout restricted type's field with a value outside the valid range is undefined behavior" +)] pub(crate) struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("initializing type with `rustc_layout_scalar_valid_range` attr")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, @@ -298,134 +324,150 @@ pub(crate) struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[derive(Diagnostic)] #[diag( - mir_build_initializing_type_with_unsafe_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + "initializing type with an unsafe field is unsafe and requires unsafe block", code = E0133 )] -#[note] +#[note("unsafe fields may carry library invariants")] pub(crate) struct InitializingTypeWithUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("initialization of struct with unsafe field")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_mutable_static_requires_unsafe, code = E0133)] -#[note] +#[diag("use of mutable static is unsafe and requires unsafe block", code = E0133)] +#[note( + "mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior" +)] pub(crate) struct UseOfMutableStaticRequiresUnsafe { #[primary_span] - #[label] + #[label("use of mutable static")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_mutable_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] -#[note] +#[diag("use of mutable static is unsafe and requires unsafe function or block", code = E0133)] +#[note( + "mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior" +)] pub(crate) struct UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("use of mutable static")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_extern_static_requires_unsafe, code = E0133)] -#[note] +#[diag("use of extern static is unsafe and requires unsafe block", code = E0133)] +#[note( + "extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior" +)] pub(crate) struct UseOfExternStaticRequiresUnsafe { #[primary_span] - #[label] + #[label("use of extern static")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] -#[note] +#[diag("use of extern static is unsafe and requires unsafe function or block", code = E0133)] +#[note( + "extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior" +)] pub(crate) struct UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("use of extern static")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_unsafe_field_requires_unsafe, code = E0133)] -#[note] +#[diag("use of unsafe field is unsafe and requires unsafe block", code = E0133)] +#[note("unsafe fields may carry library invariants")] pub(crate) struct UseOfUnsafeFieldRequiresUnsafe { #[primary_span] - #[label] + #[label("use of unsafe field")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_unsafe_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] -#[note] +#[diag("use of unsafe field is unsafe and requires unsafe block", code = E0133)] +#[note("unsafe fields may carry library invariants")] pub(crate) struct UseOfUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("use of unsafe field")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_deref_raw_pointer_requires_unsafe, code = E0133)] -#[note] +#[diag("dereference of raw pointer is unsafe and requires unsafe block", code = E0133)] +#[note( + "raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior" +)] pub(crate) struct DerefOfRawPointerRequiresUnsafe { #[primary_span] - #[label] + #[label("dereference of raw pointer")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] -#[note] +#[diag("dereference of raw pointer is unsafe and requires unsafe function or block", code = E0133)] +#[note( + "raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior" +)] pub(crate) struct DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("dereference of raw pointer")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_union_field_requires_unsafe, code = E0133)] -#[note] +#[diag("access to union field is unsafe and requires unsafe block", code = E0133)] +#[note( + "the field may not be properly initialized: using uninitialized data will cause undefined behavior" +)] pub(crate) struct AccessToUnionFieldRequiresUnsafe { #[primary_span] - #[label] + #[label("access to union field")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] -#[note] +#[diag("access to union field is unsafe and requires unsafe function or block", code = E0133)] +#[note( + "the field may not be properly initialized: using uninitialized data will cause undefined behavior" +)] pub(crate) struct AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("access to union field")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_mutation_of_layout_constrained_field_requires_unsafe, code = E0133)] -#[note] +#[diag("mutation of layout constrained field is unsafe and requires unsafe block", code = E0133)] +#[note("mutating layout constrained fields cannot statically be checked for valid values")] pub(crate) struct MutationOfLayoutConstrainedFieldRequiresUnsafe { #[primary_span] - #[label] + #[label("mutation of layout constrained field")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, @@ -433,24 +475,26 @@ pub(crate) struct MutationOfLayoutConstrainedFieldRequiresUnsafe { #[derive(Diagnostic)] #[diag( - mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + "mutation of layout constrained field is unsafe and requires unsafe function or block", code = E0133 )] -#[note] +#[note("mutating layout constrained fields cannot statically be checked for valid values")] pub(crate) struct MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("mutation of layout constrained field")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_borrow_of_layout_constrained_field_requires_unsafe, code = E0133)] -#[note] +#[diag("borrow of layout constrained field with interior mutability is unsafe and requires unsafe block", code = E0133)] +#[note( + "references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values" +)] pub(crate) struct BorrowOfLayoutConstrainedFieldRequiresUnsafe { #[primary_span] - #[label] + #[label("borrow of layout constrained field with interior mutability")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, @@ -458,47 +502,42 @@ pub(crate) struct BorrowOfLayoutConstrainedFieldRequiresUnsafe { #[derive(Diagnostic)] #[diag( - mir_build_borrow_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + "borrow of layout constrained field with interior mutability is unsafe and requires unsafe function or block", code = E0133 )] -#[note] +#[note( + "references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values" +)] pub(crate) struct BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("borrow of layout constrained field with interior mutability")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] -#[diag(mir_build_call_to_fn_with_requires_unsafe, code = E0133)] -#[help] +#[diag("call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block", code = E0133)] +#[help( + "in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> + [1] feature + *[count] features +}: {$missing_target_features}" +)] pub(crate) struct CallToFunctionWithRequiresUnsafe { #[primary_span] - #[label] + #[label("call to function with `#[target_feature]`")] pub(crate) span: Span, pub(crate) function: String, pub(crate) missing_target_features: DiagArgValue, pub(crate) missing_target_features_count: usize, - #[note] - pub(crate) note: bool, - pub(crate) build_target_features: DiagArgValue, - pub(crate) build_target_features_count: usize, - #[subdiagnostic] - pub(crate) unsafe_not_inherited_note: Option, -} - -#[derive(Diagnostic)] -#[diag(mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] -#[help] -pub(crate) struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { - #[primary_span] - #[label] - pub(crate) span: Span, - pub(crate) function: String, - pub(crate) missing_target_features: DiagArgValue, - pub(crate) missing_target_features_count: usize, - #[note] + #[note("the {$build_target_features} target {$build_target_features_count -> + [1] feature + *[count] features + } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count -> + [1] it + *[count] them + } in `#[target_feature]`")] pub(crate) note: bool, pub(crate) build_target_features: DiagArgValue, pub(crate) build_target_features_count: usize, @@ -508,12 +547,44 @@ pub(crate) struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[derive(Diagnostic)] #[diag( - mir_build_unsafe_binder_cast_requires_unsafe, + "call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe function or block", + code = E0133, +)] +#[help( + "in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> + [1] feature + *[count] features +}: {$missing_target_features}" +)] +pub(crate) struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label("call to function with `#[target_feature]`")] + pub(crate) span: Span, + pub(crate) function: String, + pub(crate) missing_target_features: DiagArgValue, + pub(crate) missing_target_features_count: usize, + #[note("the {$build_target_features} target {$build_target_features_count -> + [1] feature + *[count] features + } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count -> + [1] it + *[count] them + } in `#[target_feature]`")] + pub(crate) note: bool, + pub(crate) build_target_features: DiagArgValue, + pub(crate) build_target_features_count: usize, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + +#[derive(Diagnostic)] +#[diag( + "unsafe binder cast is unsafe and requires unsafe block information that may be required to uphold safety guarantees of a type", code = E0133, )] pub(crate) struct UnsafeBinderCastRequiresUnsafe { #[primary_span] - #[label] + #[label("unsafe binder cast")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, @@ -521,19 +592,19 @@ pub(crate) struct UnsafeBinderCastRequiresUnsafe { #[derive(Diagnostic)] #[diag( - mir_build_unsafe_binder_cast_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + "unsafe binder cast is unsafe and requires unsafe block or unsafe fn information that may be required to uphold safety guarantees of a type", code = E0133, )] pub(crate) struct UnsafeBinderCastRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] - #[label] + #[label("unsafe binder cast")] pub(crate) span: Span, #[subdiagnostic] pub(crate) unsafe_not_inherited_note: Option, } #[derive(Subdiagnostic)] -#[label(mir_build_unsafe_not_inherited)] +#[label("items do not inherit unsafety from separate enclosing items")] pub(crate) struct UnsafeNotInheritedNote { #[primary_span] pub(crate) span: Span, @@ -546,11 +617,16 @@ pub(crate) struct UnsafeNotInheritedLintNote { impl Subdiagnostic for UnsafeNotInheritedLintNote { fn add_to_diag(self, diag: &mut Diag<'_, G>) { - diag.span_note(self.signature_span, fluent::mir_build_unsafe_fn_safe_body); + diag.span_note( + self.signature_span, + inline_fluent!( + "an unsafe function restricts its caller, but its body is safe by default" + ), + ); let body_start = self.body_span.shrink_to_lo(); let body_end = self.body_span.shrink_to_hi(); diag.tool_only_multipart_suggestion( - fluent::mir_build_wrap_suggestion, + inline_fluent!("consider wrapping the function body in an unsafe block"), vec![(body_start, "{ unsafe ".into()), (body_end, "}".into())], Applicability::MachineApplicable, ); @@ -558,9 +634,9 @@ impl Subdiagnostic for UnsafeNotInheritedLintNote { } #[derive(LintDiagnostic)] -#[diag(mir_build_unused_unsafe)] +#[diag("unnecessary `unsafe` block")] pub(crate) struct UnusedUnsafe { - #[label] + #[label("unnecessary `unsafe` block")] pub(crate) span: Span, #[subdiagnostic] pub(crate) enclosing: Option, @@ -568,7 +644,7 @@ pub(crate) struct UnusedUnsafe { #[derive(Subdiagnostic)] pub(crate) enum UnusedUnsafeEnclosing { - #[label(mir_build_unused_unsafe_enclosing_block_label)] + #[label("because it's nested under this `unsafe` block")] Block { #[primary_span] span: Span, @@ -584,8 +660,11 @@ pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - let mut diag = - Diag::new(dcx, level, fluent::mir_build_non_exhaustive_patterns_type_not_empty); + let mut diag = Diag::new( + dcx, + level, + inline_fluent!("non-exhaustive patterns: type `{$ty}` is non-empty"), + ); diag.span(self.scrut_span); diag.code(E0004); let peeled_ty = self.ty.peel_refs(); @@ -605,20 +684,22 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo let mut span: MultiSpan = def_span.into(); span.push_span_label(def_span, ""); - diag.span_note(span, fluent::mir_build_def_note); + diag.span_note(span, inline_fluent!("`{$peeled_ty}` defined here")); } let is_non_exhaustive = matches!(self.ty.kind(), ty::Adt(def, _) if def.variant_list_has_applicable_non_exhaustive()); if is_non_exhaustive { - diag.note(fluent::mir_build_non_exhaustive_type_note); + diag.note(inline_fluent!( + "the matched value is of type `{$ty}`, which is marked as non-exhaustive" + )); } else { - diag.note(fluent::mir_build_type_note); + diag.note(inline_fluent!("the matched value is of type `{$ty}`")); } if let ty::Ref(_, sub_ty, _) = self.ty.kind() { if !sub_ty.is_inhabited_from(self.cx.tcx, self.cx.module, self.cx.typing_env) { - diag.note(fluent::mir_build_reference_note); + diag.note(inline_fluent!("references are always considered inhabited")); } } @@ -633,12 +714,14 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo }; diag.span_suggestion_verbose( braces_span, - fluent::mir_build_suggestion, + inline_fluent!("ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown"), format!(" {{{indentation}{more}_ => todo!(),{indentation}}}"), Applicability::HasPlaceholders, ); } else { - diag.help(fluent::mir_build_help); + diag.help(inline_fluent!( + "ensure that all possible cases are being handled by adding a match arm with a wildcard pattern" + )); } diag @@ -646,69 +729,80 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo } #[derive(Subdiagnostic)] -#[note(mir_build_non_exhaustive_match_all_arms_guarded)] +#[note("match arms with guards don't count towards exhaustivity")] pub(crate) struct NonExhaustiveMatchAllArmsGuarded; #[derive(Diagnostic)] -#[diag(mir_build_static_in_pattern, code = E0158)] +#[diag("statics cannot be referenced in patterns", code = E0158)] pub(crate) struct StaticInPattern { #[primary_span] - #[label] + #[label("can't be used in patterns")] pub(crate) span: Span, - #[label(mir_build_static_in_pattern_def)] + #[label("`static` defined here")] pub(crate) static_span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_const_param_in_pattern, code = E0158)] +#[diag("constant parameters cannot be referenced in patterns", code = E0158)] pub(crate) struct ConstParamInPattern { #[primary_span] - #[label] + #[label("can't be used in patterns")] pub(crate) span: Span, - #[label(mir_build_const_param_in_pattern_def)] + #[label("constant defined here")] pub(crate) const_span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_non_const_path, code = E0080)] +#[diag("runtime values cannot be referenced in patterns", code = E0080)] pub(crate) struct NonConstPath { #[primary_span] - #[label] + #[label("references a runtime value")] pub(crate) span: Span, } #[derive(LintDiagnostic)] -#[diag(mir_build_unreachable_pattern)] +#[diag("unreachable pattern")] pub(crate) struct UnreachablePattern<'tcx> { - #[label] + #[label("no value can reach this")] pub(crate) span: Option, - #[label(mir_build_unreachable_matches_no_values)] + #[label("matches no values because `{$matches_no_values_ty}` is uninhabited")] pub(crate) matches_no_values: Option, pub(crate) matches_no_values_ty: Ty<'tcx>, - #[note(mir_build_unreachable_uninhabited_note)] + #[note( + "to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types" + )] pub(crate) uninhabited_note: Option<()>, - #[label(mir_build_unreachable_covered_by_catchall)] + #[label("matches any value")] pub(crate) covered_by_catchall: Option, #[subdiagnostic] pub(crate) wanted_constant: Option, - #[note(mir_build_unreachable_pattern_const_reexport_accessible)] + #[note( + "there is a constant of the same name imported in another scope, which could have been used to pattern match against its value instead of introducing a new catch-all binding, but it needs to be imported in the pattern's scope" + )] pub(crate) accessible_constant: Option, - #[note(mir_build_unreachable_pattern_const_inaccessible)] + #[note( + "there is a constant of the same name, which could have been used to pattern match against its value instead of introducing a new catch-all binding, but it is not accessible from this scope" + )] pub(crate) inaccessible_constant: Option, - #[note(mir_build_unreachable_pattern_let_binding)] + #[note( + "there is a binding of the same name; if you meant to pattern match against the value of that binding, that is a feature of constants that is not available for `let` bindings" + )] pub(crate) pattern_let_binding: Option, - #[label(mir_build_unreachable_covered_by_one)] + #[label("matches all the relevant values")] pub(crate) covered_by_one: Option, - #[note(mir_build_unreachable_covered_by_many)] + #[note("multiple earlier patterns match some of the same values")] pub(crate) covered_by_many: Option, pub(crate) covered_by_many_n_more_count: usize, - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion("remove the match arm", code = "", applicability = "machine-applicable")] pub(crate) suggest_remove: Option, } #[derive(Subdiagnostic)] #[suggestion( - mir_build_unreachable_pattern_wanted_const, + "you might have meant to pattern match against the value of {$is_typo -> + [true] similarly named constant + *[false] constant + } `{$const_name}` instead of introducing a new catch-all binding", code = "{const_path}", applicability = "machine-applicable" )] @@ -721,48 +815,50 @@ pub(crate) struct WantedConstant { } #[derive(LintDiagnostic)] -#[diag(mir_build_unreachable_due_to_uninhabited)] +#[diag("unreachable {$descr}")] pub(crate) struct UnreachableDueToUninhabited<'desc, 'tcx> { pub descr: &'desc str, - #[label] + #[label("unreachable {$descr}")] pub expr: Span, - #[label(mir_build_label_orig)] - #[note] + #[label("any code following this expression is unreachable")] + #[note("this expression has type `{$ty}`, which is uninhabited")] pub orig: Span, pub ty: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(mir_build_const_pattern_depends_on_generic_parameter, code = E0158)] +#[diag("constant pattern cannot depend on generic parameters", code = E0158)] pub(crate) struct ConstPatternDependsOnGenericParameter { #[primary_span] - #[label] + #[label("`const` depends on a generic parameter")] pub(crate) span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_could_not_eval_const_pattern)] +#[diag("could not evaluate constant pattern")] pub(crate) struct CouldNotEvalConstPattern { #[primary_span] - #[label] + #[label("could not evaluate constant")] pub(crate) span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper, code = E0030)] +#[diag("lower bound for range pattern must be less than or equal to upper bound", code = E0030)] pub(crate) struct LowerRangeBoundMustBeLessThanOrEqualToUpper { #[primary_span] - #[label] + #[label("lower bound larger than upper bound")] pub(crate) span: Span, - #[note(mir_build_teach_note)] + #[note( + "when matching against a range, the compiler verifies that the range is non-empty. Range patterns include both end-points, so this is equivalent to requiring the start of the range to be less than or equal to the end of the range" + )] pub(crate) teach: bool, } #[derive(Diagnostic)] -#[diag(mir_build_literal_in_range_out_of_bounds)] +#[diag("literal out of range for `{$ty}`")] pub(crate) struct LiteralOutOfRange<'tcx> { #[primary_span] - #[label] + #[label("this value does not fit into the type `{$ty}` whose range is `{$min}..={$max}`")] pub(crate) span: Span, pub(crate) ty: Ty<'tcx>, pub(crate) min: i128, @@ -770,93 +866,173 @@ pub(crate) struct LiteralOutOfRange<'tcx> { } #[derive(Diagnostic)] -#[diag(mir_build_lower_range_bound_must_be_less_than_upper, code = E0579)] +#[diag("lower bound for range pattern must be less than upper bound", code = E0579)] pub(crate) struct LowerRangeBoundMustBeLessThanUpper { #[primary_span] pub(crate) span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_upper_range_bound_cannot_be_min, code = E0579)] +#[diag("exclusive upper bound for a range bound cannot be the minimum", code = E0579)] pub(crate) struct UpperRangeBoundCannotBeMin { #[primary_span] pub(crate) span: Span, } #[derive(LintDiagnostic)] -#[diag(mir_build_leading_irrefutable_let_patterns)] -#[note] -#[help] +#[diag( + "leading irrefutable {$count -> + [one] pattern + *[other] patterns +} in let chain" +)] +#[note( + "{$count -> + [one] this pattern + *[other] these patterns +} will always match" +)] +#[help( + "consider moving {$count -> + [one] it + *[other] them +} outside of the construct" +)] pub(crate) struct LeadingIrrefutableLetPatterns { pub(crate) count: usize, } #[derive(LintDiagnostic)] -#[diag(mir_build_trailing_irrefutable_let_patterns)] -#[note] -#[help] +#[diag( + "trailing irrefutable {$count -> + [one] pattern + *[other] patterns +} in let chain" +)] +#[note( + "{$count -> + [one] this pattern + *[other] these patterns +} will always match" +)] +#[help( + "consider moving {$count -> + [one] it + *[other] them +} into the body" +)] pub(crate) struct TrailingIrrefutableLetPatterns { pub(crate) count: usize, } #[derive(LintDiagnostic)] -#[diag(mir_build_bindings_with_variant_name, code = E0170)] +#[diag("pattern binding `{$name}` is named the same as one of the variants of the type `{$ty_path}`", code = E0170)] pub(crate) struct BindingsWithVariantName { - #[suggestion(code = "{ty_path}::{name}", applicability = "machine-applicable")] + #[suggestion( + "to match on the variant, qualify the path", + code = "{ty_path}::{name}", + applicability = "machine-applicable" + )] pub(crate) suggestion: Option, pub(crate) ty_path: String, pub(crate) name: Ident, } #[derive(LintDiagnostic)] -#[diag(mir_build_irrefutable_let_patterns_if_let)] -#[note] -#[help] +#[diag( + "irrefutable `if let` {$count -> + [one] pattern + *[other] patterns +}" +)] +#[note( + "{$count -> + [one] this pattern + *[other] these patterns +} will always match, so the `if let` is useless" +)] +#[help("consider replacing the `if let` with a `let`")] pub(crate) struct IrrefutableLetPatternsIfLet { pub(crate) count: usize, } #[derive(LintDiagnostic)] -#[diag(mir_build_irrefutable_let_patterns_if_let_guard)] -#[note] -#[help] +#[diag( + "irrefutable `if let` guard {$count -> + [one] pattern + *[other] patterns +}" +)] +#[note( + "{$count -> + [one] this pattern + *[other] these patterns +} will always match, so the guard is useless" +)] +#[help("consider removing the guard and adding a `let` inside the match arm")] pub(crate) struct IrrefutableLetPatternsIfLetGuard { pub(crate) count: usize, } #[derive(LintDiagnostic)] -#[diag(mir_build_irrefutable_let_patterns_let_else)] -#[note] -#[help] +#[diag( + "irrefutable `let...else` {$count -> + [one] pattern + *[other] patterns +}" +)] +#[note( + "{$count -> + [one] this pattern + *[other] these patterns +} will always match, so the `else` clause is useless" +)] +#[help("consider removing the `else` clause")] pub(crate) struct IrrefutableLetPatternsLetElse { pub(crate) count: usize, } #[derive(LintDiagnostic)] -#[diag(mir_build_irrefutable_let_patterns_while_let)] -#[note] -#[help] +#[diag( + "irrefutable `while let` {$count -> + [one] pattern + *[other] patterns +}" +)] +#[note( + "{$count -> + [one] this pattern + *[other] these patterns +} will always match, so the loop will never exit" +)] +#[help("consider instead using a `loop {\"{\"} ... {\"}\"}` with a `let` inside it")] pub(crate) struct IrrefutableLetPatternsWhileLet { pub(crate) count: usize, } #[derive(Diagnostic)] -#[diag(mir_build_borrow_of_moved_value)] +#[diag("borrow of moved value")] pub(crate) struct BorrowOfMovedValue<'tcx> { #[primary_span] - #[label] - #[label(mir_build_occurs_because_label)] + #[label("value moved into `{$name}` here")] + #[label( + "move occurs because `{$name}` has type `{$ty}`, which does not implement the `Copy` trait" + )] pub(crate) binding_span: Span, - #[label(mir_build_value_borrowed_label)] + #[label("value borrowed here after move")] pub(crate) conflicts_ref: Vec, pub(crate) name: Ident, pub(crate) ty: Ty<'tcx>, - #[suggestion(code = "ref ", applicability = "machine-applicable")] + #[suggestion( + "borrow this binding in the pattern to avoid moving the value", + code = "ref ", + applicability = "machine-applicable" + )] pub(crate) suggest_borrowing: Option, } #[derive(Diagnostic)] -#[diag(mir_build_multiple_mut_borrows)] +#[diag("cannot borrow value as mutable more than once at a time")] pub(crate) struct MultipleMutBorrows { #[primary_span] pub(crate) span: Span, @@ -865,7 +1041,7 @@ pub(crate) struct MultipleMutBorrows { } #[derive(Diagnostic)] -#[diag(mir_build_already_borrowed)] +#[diag("cannot borrow value as mutable because it is also borrowed as immutable")] pub(crate) struct AlreadyBorrowed { #[primary_span] pub(crate) span: Span, @@ -874,7 +1050,7 @@ pub(crate) struct AlreadyBorrowed { } #[derive(Diagnostic)] -#[diag(mir_build_already_mut_borrowed)] +#[diag("cannot borrow value as immutable because it is also borrowed as mutable")] pub(crate) struct AlreadyMutBorrowed { #[primary_span] pub(crate) span: Span, @@ -883,7 +1059,7 @@ pub(crate) struct AlreadyMutBorrowed { } #[derive(Diagnostic)] -#[diag(mir_build_moved_while_borrowed)] +#[diag("cannot move out of value because it is borrowed")] pub(crate) struct MovedWhileBorrowed { #[primary_span] pub(crate) span: Span, @@ -893,19 +1069,19 @@ pub(crate) struct MovedWhileBorrowed { #[derive(Subdiagnostic)] pub(crate) enum Conflict { - #[label(mir_build_mutable_borrow)] + #[label("value is mutably borrowed by `{$name}` here")] Mut { #[primary_span] span: Span, name: Symbol, }, - #[label(mir_build_borrow)] + #[label("value is borrowed by `{$name}` here")] Ref { #[primary_span] span: Span, name: Symbol, }, - #[label(mir_build_moved)] + #[label("value is moved into `{$name}` here")] Moved { #[primary_span] span: Span, @@ -914,50 +1090,56 @@ pub(crate) enum Conflict { } #[derive(Diagnostic)] -#[diag(mir_build_union_pattern)] +#[diag("cannot use unions in constant patterns")] pub(crate) struct UnionPattern { #[primary_span] - #[label] + #[label("can't use a `union` here")] pub(crate) span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_type_not_structural)] +#[diag("constant of non-structural type `{$ty}` in a pattern")] pub(crate) struct TypeNotStructural<'tcx> { #[primary_span] - #[label] + #[label("constant of non-structural type")] pub(crate) span: Span, - #[label(mir_build_type_not_structural_def)] + #[label("`{$ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns")] pub(crate) ty_def_span: Span, pub(crate) ty: Ty<'tcx>, - #[note(mir_build_type_not_structural_tip)] + #[note( + "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details" + )] pub(crate) manual_partialeq_impl_span: Option, - #[note(mir_build_type_not_structural_more_info)] + #[note( + "see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details" + )] pub(crate) manual_partialeq_impl_note: bool, } #[derive(Diagnostic)] -#[diag(mir_build_non_partial_eq_match)] -#[note(mir_build_type_not_structural_more_info)] +#[diag("constant of non-structural type `{$ty}` in a pattern")] +#[note( + "see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details" +)] pub(crate) struct TypeNotPartialEq<'tcx> { #[primary_span] - #[label] + #[label("constant of non-structural type")] pub(crate) span: Span, pub(crate) ty: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(mir_build_invalid_pattern)] +#[diag("{$prefix} `{$non_sm_ty}` cannot be used in patterns")] pub(crate) struct InvalidPattern<'tcx> { #[primary_span] - #[label] + #[label("{$prefix} can't be used in patterns")] pub(crate) span: Span, pub(crate) non_sm_ty: Ty<'tcx>, pub(crate) prefix: String, } #[derive(Diagnostic)] -#[diag(mir_build_unsized_pattern)] +#[diag("cannot use unsized non-slice type `{$non_sm_ty}` in constant patterns")] pub(crate) struct UnsizedPattern<'tcx> { #[primary_span] pub(crate) span: Span, @@ -965,36 +1147,38 @@ pub(crate) struct UnsizedPattern<'tcx> { } #[derive(Diagnostic)] -#[diag(mir_build_nan_pattern)] -#[note] -#[help] +#[diag("cannot use NaN in patterns")] +#[note("NaNs compare inequal to everything, even themselves, so this pattern would never match")] +#[help("try using the `is_nan` method instead")] pub(crate) struct NaNPattern { #[primary_span] - #[label] + #[label("evaluates to `NaN`, which is not allowed in patterns")] pub(crate) span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_pointer_pattern)] -#[note] +#[diag( + "function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon" +)] +#[note("see https://github.com/rust-lang/rust/issues/70861 for details")] pub(crate) struct PointerPattern { #[primary_span] - #[label] + #[label("can't be used in patterns")] pub(crate) span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_non_empty_never_pattern)] -#[note] +#[diag("mismatched types")] +#[note("the matched value is of type `{$ty}`")] pub(crate) struct NonEmptyNeverPattern<'tcx> { #[primary_span] - #[label] + #[label("a never pattern must be used on an uninhabited type")] pub(crate) span: Span, pub(crate) ty: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(mir_build_pattern_not_covered, code = E0005)] +#[diag("refutable pattern in {$origin}", code = E0005)] pub(crate) struct PatternNotCovered<'s, 'tcx> { #[primary_span] pub(crate) span: Span, @@ -1009,10 +1193,12 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> { pub(crate) interpreted_as_const_sugg: Option, #[subdiagnostic] pub(crate) adt_defined_here: Option>, - #[note(mir_build_privately_uninhabited)] + #[note( + "pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future" + )] pub(crate) witness_1_is_privately_uninhabited: bool, pub(crate) witness_1: String, - #[note(mir_build_pattern_ty)] + #[note("the matched value is of type `{$pattern_ty}`")] pub(crate) _p: (), pub(crate) pattern_ty: Ty<'tcx>, #[subdiagnostic] @@ -1022,12 +1208,16 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> { } #[derive(Subdiagnostic)] -#[note(mir_build_inform_irrefutable)] -#[note(mir_build_more_information)] +#[note( + "`let` bindings require an \"irrefutable pattern\", like a `struct` or an `enum` with only one variant" +)] +#[note("for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html")] pub(crate) struct Inform; #[derive(Subdiagnostic)] -#[label(mir_build_confused)] +#[label( + "missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable" +)] pub(crate) struct InterpretedAsConst { #[primary_span] pub(crate) span: Span, @@ -1050,16 +1240,16 @@ impl<'tcx> Subdiagnostic for AdtDefinedHere<'tcx> { let mut spans = MultiSpan::from(self.adt_def_span); for Variant { span } in self.variants { - spans.push_span_label(span, fluent::mir_build_variant_defined_here); + spans.push_span_label(span, inline_fluent!("not covered")); } - diag.span_note(spans, fluent::mir_build_adt_defined_here); + diag.span_note(spans, inline_fluent!("`{$ty}` defined here")); } } #[derive(Subdiagnostic)] #[suggestion( - mir_build_interpreted_as_const, + "introduce a variable instead", code = "{variable}_var", applicability = "maybe-incorrect", style = "verbose" @@ -1072,7 +1262,13 @@ pub(crate) struct InterpretedAsConstSugg { #[derive(Subdiagnostic)] pub(crate) enum SuggestLet { - #[multipart_suggestion(mir_build_suggest_if_let, applicability = "has-placeholders")] + #[multipart_suggestion( + "you might want to use `if let` to ignore the {$count -> + [one] variant that isn't + *[other] variants that aren't + } matched", + applicability = "has-placeholders" + )] If { #[suggestion_part(code = "if ")] start_span: Span, @@ -1081,7 +1277,10 @@ pub(crate) enum SuggestLet { count: usize, }, #[suggestion( - mir_build_suggest_let_else, + "you might want to use `let...else` to handle the {$count -> + [one] variant that isn't + *[other] variants that aren't + } matched", code = " else {{ todo!() }}", applicability = "has-placeholders" )] @@ -1095,7 +1294,7 @@ pub(crate) enum SuggestLet { #[derive(Subdiagnostic)] pub(crate) enum MiscPatternSuggestion { #[suggestion( - mir_build_suggest_attempted_int_lit, + "alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits", code = "_", applicability = "maybe-incorrect" )] @@ -1106,25 +1305,25 @@ pub(crate) enum MiscPatternSuggestion { } #[derive(Diagnostic)] -#[diag(mir_build_loop_match_invalid_update)] +#[diag("invalid update of the `#[loop_match]` state")] pub(crate) struct LoopMatchInvalidUpdate { #[primary_span] pub lhs: Span, - #[label] + #[label("the assignment must update this variable")] pub scrutinee: Span, } #[derive(Diagnostic)] -#[diag(mir_build_loop_match_invalid_match)] -#[note] +#[diag("invalid match on `#[loop_match]` state")] +#[note("a local variable must be the scrutinee within a `#[loop_match]`")] pub(crate) struct LoopMatchInvalidMatch { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_loop_match_unsupported_type)] -#[note] +#[diag("this `#[loop_match]` state value has type `{$ty}`, which is not supported")] +#[note("only integers, floats, bool, char, and enums without fields are supported")] pub(crate) struct LoopMatchUnsupportedType<'tcx> { #[primary_span] pub span: Span, @@ -1132,36 +1331,36 @@ pub(crate) struct LoopMatchUnsupportedType<'tcx> { } #[derive(Diagnostic)] -#[diag(mir_build_loop_match_bad_statements)] +#[diag("statements are not allowed in this position within a `#[loop_match]`")] pub(crate) struct LoopMatchBadStatements { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_loop_match_bad_rhs)] +#[diag("this expression must be a single `match` wrapped in a labeled block")] pub(crate) struct LoopMatchBadRhs { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_loop_match_missing_assignment)] +#[diag("expected a single assignment expression")] pub(crate) struct LoopMatchMissingAssignment { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_loop_match_arm_with_guard)] +#[diag("match arms that are part of a `#[loop_match]` cannot have guards")] pub(crate) struct LoopMatchArmWithGuard { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_const_continue_not_const)] -#[help] +#[diag("could not determine the target branch for this `#[const_continue]`")] +#[help("try extracting the expression into a `const` item")] pub(crate) struct ConstContinueNotMonomorphicConst { #[primary_span] pub span: Span, @@ -1172,19 +1371,19 @@ pub(crate) struct ConstContinueNotMonomorphicConst { #[derive(Subdiagnostic)] pub(crate) enum ConstContinueNotMonomorphicConstReason { - #[label(mir_build_const_continue_not_const_constant_parameter)] + #[label("constant parameters may use generics, and are not evaluated early enough")] ConstantParameter { #[primary_span] span: Span, }, - #[label(mir_build_const_continue_not_const_const_block)] + #[label("`const` blocks may use generics, and are not evaluated early enough")] ConstBlock { #[primary_span] span: Span, }, - #[label(mir_build_const_continue_not_const_const_other)] + #[label("this value must be a literal or a monomorphic const")] Other { #[primary_span] span: Span, @@ -1192,22 +1391,22 @@ pub(crate) enum ConstContinueNotMonomorphicConstReason { } #[derive(Diagnostic)] -#[diag(mir_build_const_continue_bad_const)] +#[diag("could not determine the target branch for this `#[const_continue]`")] pub(crate) struct ConstContinueBadConst { #[primary_span] - #[label] + #[label("this value is too generic")] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_const_continue_missing_label_or_value)] +#[diag("a `#[const_continue]` must break to a label with a value")] pub(crate) struct ConstContinueMissingLabelOrValue { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_build_const_continue_unknown_jump_target)] +#[diag("the target of this `#[const_continue]` is not statically known")] pub(crate) struct ConstContinueUnknownJumpTarget { #[primary_span] pub span: Span, diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 5c5d979306bf..21817dea6cb4 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -1,7 +1,7 @@ //! Construction of MIR from HIR. // tidy-alphabetical-start -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(try_blocks)] @@ -18,8 +18,6 @@ pub mod thir; use rustc_middle::util::Providers; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub fn provide(providers: &mut Providers) { providers.queries.check_match = thir::pattern::check_match; providers.queries.lit_to_const = thir::constant::lit_to_const; diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index fad73a7115b0..b108dff5555f 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -75,8 +75,13 @@ impl<'tcx> ThirBuildCx<'tcx> { debug!(?pattern); let span = match local.init { - Some(init) => local.span.with_hi(init.span.hi()), - None => local.span, + Some(init) + if let Some(init_span) = + init.span.find_ancestor_inside_same_ctxt(local.span) => + { + local.span.with_hi(init_span.hi()) + } + Some(_) | None => local.span, }; let stmt = Stmt { kind: StmtKind::Let { diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 290d4ab2bfbb..4eb7b3671e9c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -3,7 +3,9 @@ use rustc_ast::Mutability; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::codes::*; -use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, struct_span_code_err}; +use rustc_errors::{ + Applicability, ErrorGuaranteed, MultiSpan, inline_fluent, struct_span_code_err, +}; use rustc_hir::def::*; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, MatchSource}; @@ -29,7 +31,6 @@ use rustc_trait_selection::infer::InferCtxtExt; use tracing::instrument; use crate::errors::*; -use crate::fluent_generated as fluent; pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> { let typeck_results = tcx.typeck(def_id); @@ -988,20 +989,20 @@ fn report_unreachable_pattern<'p, 'tcx>( for p in iter.by_ref().take(CAP_COVERED_BY_MANY) { multispan.push_span_label( p.data().span, - fluent::mir_build_unreachable_matches_same_values, + inline_fluent!("matches some of the same values"), ); } let remain = iter.count(); if remain == 0 { multispan.push_span_label( pat_span, - fluent::mir_build_unreachable_making_this_unreachable, + inline_fluent!("collectively making this unreachable"), ); } else { lint.covered_by_many_n_more_count = remain; multispan.push_span_label( pat_span, - fluent::mir_build_unreachable_making_this_unreachable_n_more, + inline_fluent!("...and {$covered_by_many_n_more_count} other patterns collectively make this unreachable"), ); } lint.covered_by_many = Some(multispan); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 0a0e0d06061e..6f0f6478f2d7 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -3,7 +3,7 @@ use core::ops::ControlFlow; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_apfloat::Float; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::Diag; +use rustc_errors::{Diag, inline_fluent}; use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::find_attr; @@ -82,10 +82,7 @@ impl<'tcx> ConstToPat<'tcx> { err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), ""); } if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind { - err.span_label( - self.tcx.def_span(uv.def), - crate::fluent_generated::mir_build_const_defined_here, - ); + err.span_label(self.tcx.def_span(uv.def), inline_fluent!("constant defined here")); } } Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()), extra: None }) diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 5f1cf1250152..692591a41a15 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -1,5 +1,5 @@ // tidy-alphabetical-start -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(exact_size_is_empty)] diff --git a/compiler/rustc_mir_transform/src/check_inline.rs b/compiler/rustc_mir_transform/src/check_inline.rs index 8d28cb3ca003..1f65bd1ba69b 100644 --- a/compiler/rustc_mir_transform/src/check_inline.rs +++ b/compiler/rustc_mir_transform/src/check_inline.rs @@ -1,13 +1,13 @@ //! Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its //! definition alone (irrespective of any specific caller). -use rustc_hir::attrs::InlineAttr; +use rustc_hir::attrs::{AttributeKind, InlineAttr}; use rustc_hir::def_id::DefId; +use rustc_hir::find_attr; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::{Body, TerminatorKind}; use rustc_middle::ty; use rustc_middle::ty::TyCtxt; -use rustc_span::sym; use crate::pass_manager::MirLint; @@ -41,7 +41,8 @@ pub(super) fn is_inline_valid_on_fn<'tcx>( def_id: DefId, ) -> Result<(), &'static str> { let codegen_attrs = tcx.codegen_fn_attrs(def_id); - if tcx.has_attr(def_id, sym::rustc_no_mir_inline) { + + if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcNoMirInline) { return Err("#[rustc_no_mir_inline]"); } @@ -62,7 +63,7 @@ pub(super) fn is_inline_valid_on_fn<'tcx>( // but at this stage we don't know whether codegen knows the intrinsic, // so just conservatively don't inline it. This also ensures that we do not // accidentally inline the body of an intrinsic that *must* be overridden. - if tcx.has_attr(def_id, sym::rustc_intrinsic) { + if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcIntrinsic) { return Err("callee is an intrinsic"); } diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 71bdafa958ca..da133861617f 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -1,6 +1,7 @@ -use rustc_hir::attrs::InlineAttr; +use rustc_hir::attrs::{AttributeKind, InlineAttr}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_hir::find_attr; use rustc_middle::bug; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; @@ -43,7 +44,7 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { return true; } - if tcx.has_attr(def_id, sym::rustc_intrinsic) { + if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcIntrinsic) { // Intrinsic fallback bodies are always cross-crate inlineable. // To ensure that the MIR inliner doesn't cluelessly try to inline fallback // bodies even when the backend would implement something better, we stop @@ -157,7 +158,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { // But intrinsics don't have a body that gets assigned to a CGU, so they are // ignored. if let Some((fn_def_id, _)) = func.const_fn_def() - && self.tcx.has_attr(fn_def_id, sym::rustc_intrinsic) + && find_attr!(tcx.get_all_attrs(fn_def_id), AttributeKind::RustcIntrinsic) { return; } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index db38c271aaf9..6507a5194add 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -129,24 +129,18 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { let ssa = SsaLocals::new(tcx, body, typing_env); // Clone dominators because we need them while mutating the body. let dominators = body.basic_blocks.dominators().clone(); - let maybe_loop_headers = loops::maybe_loop_headers(body); let arena = DroplessArena::default(); let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls, &arena); for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) { - let opaque = state.new_opaque(body.local_decls[local].ty); + let opaque = state.new_argument(body.local_decls[local].ty); state.assign(local, opaque); } let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec(); for bb in reverse_postorder { - // N.B. With loops, reverse postorder cannot produce a valid topological order. - // A statement or terminator from inside the loop, that is not processed yet, may have performed an indirect write. - if maybe_loop_headers.contains(bb) { - state.invalidate_derefs(); - } let data = &mut body.basic_blocks.as_mut_preserves_cfg()[bb]; state.visit_basic_block_data(bb, data); } @@ -204,8 +198,9 @@ enum AddressBase { enum Value<'a, 'tcx> { // Root values. /// Used to represent values we know nothing about. - /// The `usize` is a counter incremented by `new_opaque`. Opaque(VnOpaque), + /// The value is a argument. + Argument(VnOpaque), /// Evaluated or unevaluated constant value. Constant { value: Const<'tcx>, @@ -290,7 +285,7 @@ impl<'a, 'tcx> ValueSet<'a, 'tcx> { let value = value(VnOpaque); debug_assert!(match value { - Value::Opaque(_) | Value::Address { .. } => true, + Value::Opaque(_) | Value::Argument(_) | Value::Address { .. } => true, Value::Constant { disambiguator, .. } => disambiguator.is_some(), _ => false, }); @@ -350,12 +345,6 @@ impl<'a, 'tcx> ValueSet<'a, 'tcx> { fn ty(&self, index: VnIndex) -> Ty<'tcx> { self.types[index] } - - /// Replace the value associated with `index` with an opaque value. - #[inline] - fn forget(&mut self, index: VnIndex) { - self.values[index] = Value::Opaque(VnOpaque); - } } struct VnState<'body, 'a, 'tcx> { @@ -374,8 +363,6 @@ struct VnState<'body, 'a, 'tcx> { /// - `Some(None)` are values for which computation has failed; /// - `Some(Some(op))` are successful computations. evaluated: IndexVec>>>, - /// Cache the deref values. - derefs: Vec, ssa: &'body SsaLocals, dominators: Dominators, reused_locals: DenseBitSet, @@ -408,7 +395,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { rev_locals: IndexVec::with_capacity(num_values), values: ValueSet::new(num_values), evaluated: IndexVec::with_capacity(num_values), - derefs: Vec::new(), ssa, dominators, reused_locals: DenseBitSet::new_empty(local_decls.len()), @@ -455,6 +441,13 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { index } + #[instrument(level = "trace", skip(self), ret)] + fn new_argument(&mut self, ty: Ty<'tcx>) -> VnIndex { + let index = self.insert_unique(ty, Value::Argument); + self.evaluated[index] = Some(None); + index + } + /// Create a new `Value::Address` distinct from all the others. #[instrument(level = "trace", skip(self), ret)] fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> Option { @@ -472,8 +465,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { // Skip the initial `Deref`. projection.next(); AddressBase::Deref(base) - } else { + } else if self.ssa.is_ssa(place.local) { + // Only propagate the pointer of the SSA local. AddressBase::Local(place.local) + } else { + return None; }; // Do not try evaluating inside `Index`, this has been done by `simplify_place_projection`. let projection = @@ -541,18 +537,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { self.insert(ty, Value::Aggregate(VariantIdx::ZERO, self.arena.alloc_slice(values))) } - fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex { - let value = self.insert(ty, Value::Projection(value, ProjectionElem::Deref)); - self.derefs.push(value); - value - } - - fn invalidate_derefs(&mut self) { - for deref in std::mem::take(&mut self.derefs) { - self.values.forget(deref); - } - } - #[instrument(level = "trace", skip(self), ret)] fn eval_to_const_inner(&mut self, value: VnIndex) -> Option> { use Value::*; @@ -566,7 +550,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { let op = match self.get(value) { _ if ty.is_zst() => ImmTy::uninit(ty).into(), - Opaque(_) => return None, + Opaque(_) | Argument(_) => return None, // Keep runtime check constants as symbolic. RuntimeChecks(..) => return None, @@ -815,10 +799,24 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { { return Some((projection_ty, value)); } + // DO NOT reason the pointer value. + // We cannot unify two pointers that dereference same local, because they may + // have different lifetimes. + // ``` + // let b: &T = *a; + // ... `a` is allowed to be modified. `c` and `b` have different borrowing lifetime. + // Unifying them will extend the lifetime of `b`. + // let c: &T = *a; + // ``` + if projection_ty.ty.is_ref() { + return None; + } // An immutable borrow `_x` always points to the same value for the // lifetime of the borrow, so we can merge all instances of `*_x`. - return Some((projection_ty, self.insert_deref(projection_ty.ty, value))); + let deref = self + .insert(projection_ty.ty, Value::Projection(value, ProjectionElem::Deref)); + return Some((projection_ty, deref)); } else { return None; } @@ -1037,7 +1035,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { let op = self.simplify_operand(op, location)?; Value::Repeat(op, amount) } - Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location), + Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location), Rvalue::Ref(_, borrow_kind, ref mut place) => { self.simplify_place_projection(place, location); return self.new_pointer(*place, AddressKind::Ref(borrow_kind)); @@ -1148,7 +1146,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { fn simplify_aggregate( &mut self, - lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option { @@ -1231,12 +1228,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } if let Some(value) = self.simplify_aggregate_to_copy(ty, variant_index, &fields) { - // Allow introducing places with non-constant offsets, as those are still better than - // reconstructing an aggregate. But avoid creating `*a = copy (*b)`, as they might be - // aliases resulting in overlapping assignments. - let allow_complex_projection = - lhs.projection[..].iter().all(PlaceElem::is_stable_offset); - if let Some(place) = self.try_as_place(value, location, allow_complex_projection) { + if let Some(place) = self.try_as_place(value, location, true) { self.reused_locals.insert(place.local); *rvalue = Rvalue::Use(Operand::Copy(place)); } @@ -1890,6 +1882,17 @@ impl<'tcx> VnState<'_, '_, 'tcx> { && (allow_complex_projection || proj.is_stable_offset()) && let Some(proj) = self.try_as_place_elem(self.ty(index), proj, loc) { + if proj == PlaceElem::Deref { + // We can introduce a new dereference if the source value cannot be changed in the body. + // Dereferencing an immutable argument always gives the same value in the body. + match self.get(pointer) { + Value::Argument(_) + if let Some(Mutability::Not) = self.ty(pointer).ref_mutability() => {} + _ => { + return None; + } + } + } projection.push(proj); index = pointer; } else { @@ -1916,10 +1919,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> { fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { self.simplify_place_projection(place, location); - if context.is_mutating_use() && place.is_indirect() { - // Non-local mutation maybe invalidate deref. - self.invalidate_derefs(); - } self.super_place(place, context, location); } @@ -1949,11 +1948,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> { } } - if lhs.is_indirect() { - // Non-local mutation maybe invalidate deref. - self.invalidate_derefs(); - } - if let Some(local) = lhs.as_local() && self.ssa.is_ssa(local) && let rvalue_ty = rvalue.ty(self.local_decls, self.tcx) @@ -1976,10 +1970,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> { self.assign(local, opaque); } } - // Terminators that can write to memory may invalidate (nested) derefs. - if terminator.kind.can_write_to_memory() { - self.invalidate_derefs(); - } self.super_terminator(terminator, location); } } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 402e22621426..bc2c6bd81aca 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,5 +1,5 @@ // tidy-alphabetical-start -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] #![feature(const_type_name)] #![feature(cow_is_borrowed)] diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index d045ae0b92cb..62a8743873bd 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -162,9 +162,9 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { #[primary_span] #[label( "function {$is_call -> -[true] called -*[false] defined -} here" + [true] called + *[false] defined + } here" )] pub span: Span, pub required_feature: &'a str, diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index a86230e9ab22..d8f4e0194507 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -99,7 +99,7 @@ use std::io::Write; use std::path::{Path, PathBuf}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_data_structures::sync; +use rustc_data_structures::sync::par_join; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir::LangItem; use rustc_hir::attrs::{InlineAttr, Linkage}; @@ -1145,7 +1145,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio tcx.dcx().abort_if_errors(); let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || { - sync::join( + par_join( || { let mut codegen_units = partition(tcx, items.iter().copied(), &usage_map); codegen_units[0].make_primary(); diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs index 46312be5ea9a..eb6a1b51421c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs @@ -1,4 +1,4 @@ -use rustc_type_ir::{self as ty, Interner}; +use rustc_type_ir::{self as ty, Interner, TypingMode}; use tracing::instrument; use crate::delegate::SolverDelegate; @@ -14,7 +14,18 @@ where &mut self, goal: Goal>, ) -> QueryResult { - if let Some(normalized_const) = self.evaluate_const( + if self.typing_mode() == TypingMode::Coherence + && self.cx().anon_const_kind(goal.predicate.alias.def_id) == ty::AnonConstKind::OGCA + { + // During coherence, OGCA consts should be normalized ambiguously + // because they are opaque but eventually resolved to a real value. + // We don't want two OGCAs that have the same value to be treated + // as distinct for coherence purposes. (Just like opaque types.) + // + // We can't rely on evaluate_const below because that particular wrapper + // treats too-generic consts as a successful evaluation. + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } else if let Some(normalized_const) = self.evaluate_const( goal.param_env, ty::UnevaluatedConst::new( goal.predicate.alias.def_id.try_into().unwrap(), diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 003841f3af3f..dd012a5146e6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -687,6 +687,9 @@ where /// /// because these impls overlap, and I'd rather not build a coherence hack for /// this harmless overlap. + /// + /// This trait is indirectly exposed on stable, so do *not* extend the set of types that + /// implement the trait without FCP! fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index f0c84e07a56f..28a67ae12126 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -12,7 +12,6 @@ rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_index = { path = "../rustc_index" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl deleted file mode 100644 index 3f3300117460..000000000000 --- a/compiler/rustc_parse/messages.ftl +++ /dev/null @@ -1,1043 +0,0 @@ -parse_add_paren = try adding parentheses - -parse_ambiguous_range_pattern = the range pattern here has ambiguous interpretation -parse_ambiguous_range_pattern_suggestion = add parentheses to clarify the precedence - -parse_array_brackets_instead_of_braces = this is a block expression, not an array - .suggestion = to make an array, use square brackets instead of curly braces - -parse_array_index_offset_of = array indexing not supported in offset_of - -parse_asm_expected_comma = expected token: `,` - .label = expected `,` - -parse_asm_expected_other = expected operand, {$is_inline_asm -> - [false] options - *[true] clobber_abi, options - }, or additional template string - -parse_asm_expected_register_class_or_explicit_register = expected register class or explicit register - -parse_asm_expected_string_literal = expected string literal - .label = not a string literal - -parse_asm_non_abi = at least one abi must be provided as an argument to `clobber_abi` - -parse_asm_requires_template = requires at least a template string argument - -parse_asm_sym_no_path = expected a path for argument to `sym` - -parse_asm_underscore_input = _ cannot be used for input operands - -parse_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `{$macro_name}!` - .label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it - -parse_assignment_else_not_allowed = ... else {"{"} ... {"}"} is not allowed - -parse_associated_static_item_not_allowed = associated `static` items are not allowed - -parse_async_block_in_2015 = `async` blocks are only allowed in Rust 2018 or later - -parse_async_bound_modifier_in_2015 = `async` trait bounds are only allowed in Rust 2018 or later - -parse_async_fn_in_2015 = `async fn` is not permitted in Rust 2015 - .label = to use `async fn`, switch to Rust 2018 or later - -parse_async_impl = `async` trait implementations are unsupported - -parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 2018 or later - -parse_async_move_order_incorrect = the order of `move` and `async` is incorrect - .suggestion = try switching the order - -parse_async_use_block_in_2015 = `async use` blocks are only allowed in Rust 2018 or later - -parse_async_use_order_incorrect = the order of `use` and `async` is incorrect - .suggestion = try switching the order - -parse_at_dot_dot_in_struct_pattern = `@ ..` is not supported in struct patterns - .suggestion = bind to each field separately or, if you don't need them, just remove `{$ident} @` - -parse_at_in_struct_pattern = unexpected `@` in struct pattern - .note = struct patterns use `field: pattern` syntax to bind to fields - .help = consider replacing `new_name @ field_name` with `field_name: new_name` if that is what you intended - -parse_attr_after_generic = trailing attribute after generic parameter - .label = attributes must go before parameters - -parse_attr_without_generics = attribute without generic parameters - .label = attributes are only permitted when preceding parameters - -parse_attribute_on_empty_type = attributes cannot be applied here - .label = attributes are not allowed here - -parse_attribute_on_generic_arg = attributes cannot be applied to generic arguments - .label = attributes are not allowed here - .suggestion = remove attribute from here - -parse_attribute_on_param_type = attributes cannot be applied to a function parameter's type - .label = attributes are not allowed here - -parse_attribute_on_type = attributes cannot be applied to types - .label = attributes are not allowed here - .suggestion = remove attribute from here - -parse_bad_assoc_type_bounds = bounds on associated types do not belong here - .label = belongs in `where` clause - -parse_bad_item_kind = {$descr} is not supported in {$ctx} - .help = consider moving the {$descr} out to a nearby module scope - -parse_bad_return_type_notation_output = - return type not allowed with return type notation - .suggestion = remove the return type - -parse_bare_cr = {$double_quotes -> - [true] bare CR not allowed in string, use `\r` instead - *[false] character constant must be escaped: `\r` - } - .escape = escape the character - -parse_bare_cr_in_frontmatter = bare CR not allowed in frontmatter - -parse_bare_cr_in_raw_string = bare CR not allowed in raw string - -parse_binder_and_polarity = `for<...>` binder not allowed with `{$polarity}` trait polarity modifier - .label = there is not a well-defined meaning for a higher-ranked `{$polarity}` trait - -parse_binder_before_modifiers = `for<...>` binder should be placed before trait bound modifiers - .label = place the `for<...>` binder before any modifiers - -parse_bounds_not_allowed_on_trait_aliases = bounds are not allowed on trait aliases - -parse_box_not_pat = expected pattern, found {$descr} - .note = `box` is a reserved keyword - .suggestion = escape `box` to use it as an identifier - -parse_box_syntax_removed = `box_syntax` has been removed -parse_box_syntax_removed_suggestion = use `Box::new()` instead - -parse_cannot_be_raw_ident = `{$ident}` cannot be a raw identifier - -parse_cannot_be_raw_lifetime = `{$ident}` cannot be a raw lifetime - -parse_catch_after_try = keyword `catch` cannot follow a `try` block - .help = try using `match` on the result of the `try` block instead - -parse_colon_as_semi = statements are terminated with a semicolon - .suggestion = use a semicolon instead - -parse_comma_after_base_struct = cannot use a comma after the base struct - .note = the base struct must always be the last field - .suggestion = remove this comma - -parse_comparison_interpreted_as_generic = - `<` is interpreted as a start of generic arguments for `{$type}`, not a comparison - .label_args = interpreted as generic arguments - .label_comparison = not interpreted as comparison - .suggestion = try comparing the cast value - -parse_comparison_operators_cannot_be_chained = comparison operators cannot be chained - .sugg_parentheses_for_function_args = or use `(...)` if you meant to specify fn arguments - .sugg_split_comparison = split the comparison into two - .sugg_parenthesize = parenthesize the comparison -parse_compound_assignment_expression_in_let = can't reassign to an uninitialized variable - .suggestion = initialize the variable - .help = if you meant to overwrite, remove the `let` binding - -parse_const_generic_without_braces = expressions must be enclosed in braces to be used as const generic arguments - .suggestion = enclose the `const` expression in braces - -parse_const_global_cannot_be_mutable = const globals cannot be mutable - .label = cannot be mutable - .suggestion = you might want to declare a static instead - -parse_const_let_mutually_exclusive = `const` and `let` are mutually exclusive - .suggestion = remove `let` - -parse_cr_doc_comment = bare CR not allowed in {$block -> - [true] block doc-comment - *[false] doc-comment -} - -parse_default_not_followed_by_item = `default` is not followed by an item - .label = the `default` qualifier - .note = only `fn`, `const`, `type`, or `impl` items may be prefixed by `default` - -parse_delegation_non_trait_impl_reuse = only trait impls can be reused - -parse_do_catch_syntax_removed = found removed `do catch` syntax - .note = following RFC #2388, the new non-placeholder syntax is `try` - .suggestion = replace with the new syntax - -parse_doc_comment_does_not_document_anything = found a documentation comment that doesn't document anything - .help = doc comments must come before what they document, if a comment was intended use `//` - .suggestion = missing comma here - -parse_doc_comment_on_param_type = documentation comments cannot be applied to a function parameter's type - .label = doc comments are not allowed here - -parse_dot_dot_dot_for_remaining_fields = expected field pattern, found `{$token_str}` - .suggestion = to omit remaining fields, use `..` - -parse_dot_dot_dot_range_to_pattern_not_allowed = range-to patterns with `...` are not allowed - .suggestion = use `..=` instead - -parse_dot_dot_range_attribute = attributes are not allowed on range expressions starting with `..` - -parse_dotdotdot = unexpected token: `...` - .suggest_exclusive_range = use `..` for an exclusive range - .suggest_inclusive_range = or `..=` for an inclusive range - -parse_dotdotdot_rest_pattern = unexpected `...` - .label = not a valid pattern - .suggestion = for a rest pattern, use `..` instead of `...` - .note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list - -parse_dotdotdot_rest_type = unexpected `...` - .note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list - -parse_double_colon_in_bound = expected `:` followed by trait or lifetime - .suggestion = use single colon - -parse_dyn_after_mut = `mut` must precede `dyn` - .suggestion = place `mut` before `dyn` - -parse_empty_exponent_float = expected at least one digit in exponent - -parse_empty_unicode_escape = empty unicode escape - .label = this escape must have at least 1 hex digit - -parse_enum_pattern_instead_of_identifier = expected identifier, found enum pattern - -parse_enum_struct_mutually_exclusive = `enum` and `struct` are mutually exclusive - .suggestion = replace `enum struct` with - -parse_eq_field_init = expected `:`, found `=` - .suggestion = replace equals symbol with a colon - -parse_escape_only_char = {$byte -> - [true] byte - *[false] character - } constant must be escaped: `{$escaped_msg}` - .escape = escape the character - -parse_expect_dotdot_not_dotdotdot = expected `..`, found `...` - .suggestion = use `..` to fill in the rest of the fields - -parse_expect_eq_instead_of_eqeq = expected `=`, found `==` - .suggestion = consider using `=` here - -parse_expect_label_found_ident = expected a label, found an identifier - .suggestion = labels start with a tick - -parse_expect_path = expected a path - -parse_expected_binding_left_of_at = left-hand side of `@` must be a binding - .label_lhs = interpreted as a pattern, not a binding - .label_rhs = also a pattern - .note = bindings are `x`, `mut x`, `ref x`, and `ref mut x` - -parse_expected_builtin_ident = expected identifier after `builtin #` - -parse_expected_comma_after_pattern_field = expected `,` - -parse_expected_else_block = expected `{"{"}`, found {$first_tok} - .label = expected an `if` or a block after this `else` - .suggestion = add an `if` if this is the condition of a chained `else if` statement - -parse_expected_expression_found_let = expected expression, found `let` statement - .note = only supported directly in conditions of `if` and `while` expressions - .not_supported_or = `||` operators are not supported in let chain expressions - .not_supported_parentheses = `let`s wrapped in parentheses are not supported in a context with let chains - -parse_expected_fn_path_found_fn_keyword = expected identifier, found keyword `fn` - .suggestion = use `Fn` to refer to the trait - -parse_expected_identifier = expected identifier - -parse_expected_identifier_found_doc_comment = expected identifier, found doc comment -parse_expected_identifier_found_doc_comment_str = expected identifier, found doc comment `{$token}` -parse_expected_identifier_found_keyword = expected identifier, found keyword -parse_expected_identifier_found_keyword_str = expected identifier, found keyword `{$token}` -parse_expected_identifier_found_metavar = expected identifier, found metavariable -# This one deliberately doesn't print a token. -parse_expected_identifier_found_metavar_str = expected identifier, found metavariable -parse_expected_identifier_found_reserved_identifier = expected identifier, found reserved identifier -parse_expected_identifier_found_reserved_identifier_str = expected identifier, found reserved identifier `{$token}` -parse_expected_identifier_found_reserved_keyword = expected identifier, found reserved keyword -parse_expected_identifier_found_reserved_keyword_str = expected identifier, found reserved keyword `{$token}` -parse_expected_identifier_found_str = expected identifier, found `{$token}` - -parse_expected_mut_or_const_in_raw_pointer_type = expected `mut` or `const` keyword in raw pointer type - .suggestion = add `mut` or `const` here - -parse_expected_semi_found_doc_comment_str = expected `;`, found doc comment `{$token}` -parse_expected_semi_found_keyword_str = expected `;`, found keyword `{$token}` -# This one deliberately doesn't print a token. -parse_expected_semi_found_metavar_str = expected `;`, found metavariable -parse_expected_semi_found_reserved_identifier_str = expected `;`, found reserved identifier `{$token}` -parse_expected_semi_found_reserved_keyword_str = expected `;`, found reserved keyword `{$token}` -parse_expected_semi_found_str = expected `;`, found `{$token}` - -parse_expected_statement_after_outer_attr = expected statement after outer attribute - -parse_expected_struct_field = expected one of `,`, `:`, or `{"}"}`, found `{$token}` - .label = expected one of `,`, `:`, or `{"}"}` - .ident_label = while parsing this struct field - -parse_expected_trait_in_trait_impl_found_type = expected a trait, found type - -parse_expr_rarrow_call = `->` is not valid syntax for field accesses and method calls - .suggestion = try using `.` instead - .help = the `.` operator will automatically dereference the value, except if the value is a raw pointer - -parse_extern_crate_name_with_dashes = crate name using dashes are not valid in `extern crate` statements - .label = dash-separated idents are not valid - .suggestion = if the original crate name uses dashes you need to use underscores in the code - -parse_extern_item_cannot_be_const = extern items cannot be `const` - .suggestion = try using a static value - .note = for more information, visit https://doc.rust-lang.org/std/keyword.extern.html - -parse_extra_if_in_let_else = remove the `if` if you meant to write a `let...else` statement - -parse_extra_impl_keyword_in_trait_impl = unexpected `impl` keyword - .suggestion = remove the extra `impl` - .note = this is parsed as an `impl Trait` type, but a trait is expected at this position - - -parse_field_expression_with_generic = field expressions cannot have generic arguments - -parse_float_literal_requires_integer_part = float literals must have an integer part - .suggestion = must have an integer part - -parse_float_literal_unsupported_base = {$base} float literal is not supported - -parse_fn_pointer_cannot_be_async = an `fn` pointer type cannot be `async` - .label = `async` because of this - .suggestion = remove the `async` qualifier - .note = allowed qualifiers are: `unsafe` and `extern` - -parse_fn_pointer_cannot_be_const = an `fn` pointer type cannot be `const` - .label = `const` because of this - .suggestion = remove the `const` qualifier - .note = allowed qualifiers are: `unsafe` and `extern` - -parse_fn_ptr_with_generics = function pointer types may not have generic parameters - .suggestion = consider moving the lifetime {$arity -> - [one] parameter - *[other] parameters - } to {$for_param_list_exists -> - [true] the - *[false] a - } `for` parameter list - -parse_fn_trait_missing_paren = `Fn` bounds require arguments in parentheses - .add_paren = add the missing parentheses - -parse_forgot_paren = perhaps you forgot parentheses? - -parse_found_expr_would_be_stmt = expected expression, found `{$token}` - .label = expected expression - -parse_frontmatter_extra_characters_after_close = extra characters after frontmatter close are not allowed -parse_frontmatter_invalid_close_preceding_whitespace = invalid preceding whitespace for frontmatter close - .note = frontmatter close should not be preceded by whitespace -parse_frontmatter_invalid_infostring = invalid infostring for frontmatter - .note = frontmatter infostrings must be a single identifier immediately following the opening -parse_frontmatter_invalid_opening_preceding_whitespace = invalid preceding whitespace for frontmatter opening - .note = frontmatter opening should not be preceded by whitespace -parse_frontmatter_length_mismatch = frontmatter close does not match the opening - .label_opening = the opening here has {$len_opening} dashes... - .label_close = ...while the close has {$len_close} dashes -parse_frontmatter_too_many_dashes = too many `-` symbols: frontmatter openings may be delimited by up to 255 `-` symbols, but found {$len_opening} -parse_frontmatter_unclosed = unclosed frontmatter - .note = frontmatter opening here was not closed -parse_function_body_equals_expr = function body cannot be `= expression;` - .suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;` - -parse_generic_args_in_pat_require_turbofish_syntax = generic args in patterns require the turbofish syntax - -parse_generic_parameters_without_angle_brackets = generic parameters without surrounding angle brackets - .suggestion = surround the type parameters with angle brackets - -parse_generics_in_path = unexpected generic arguments in path - -parse_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml` -parse_help_set_edition_standalone = pass `--edition {$edition}` to `rustc` - -parse_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label} - .label = this {$label} contains {$count -> - [one] an invisible - *[other] invisible - } unicode text flow control {$count -> - [one] codepoint - *[other] codepoints - } - .note = these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen - .suggestion_remove = if their presence wasn't intentional, you can remove them - .suggestion_escape = if you want to keep them but make them visible in your source code, you can escape them - .no_suggestion_note_escape = if you want to keep them but make them visible in your source code, you can escape them: {$escaped} - -parse_if_expression_missing_condition = missing condition for `if` expression - .condition_label = expected condition here - .block_label = if this block is the condition of the `if` expression, then it must be followed by another block - -parse_if_expression_missing_then_block = this `if` expression is missing a block after the condition - .add_then_block = add a block here - .condition_possibly_unfinished = this binary operation is possibly unfinished - -parse_in_in_typo = - expected iterable, found keyword `in` - .suggestion = remove the duplicated `in` - -parse_inappropriate_default = {$article} {$descr} cannot be `default` - .label = `default` because of this - .note = only associated `fn`, `const`, and `type` items can be `default` - -parse_inclusive_range_extra_equals = unexpected `=` after inclusive range - .suggestion_remove_eq = use `..=` instead - .note = inclusive ranges end with a single equals sign (`..=`) - -parse_inclusive_range_match_arrow = unexpected `>` after inclusive range - .label = this is parsed as an inclusive range `..=` - .suggestion = add a space between the pattern and `=>` - -parse_inclusive_range_no_end = inclusive range with no end - .suggestion_open_range = use `..` instead - .note = inclusive ranges must be bounded at the end (`..=b` or `a..=b`) - -parse_incorrect_parens_trait_bounds = incorrect parentheses around trait bounds -parse_incorrect_parens_trait_bounds_sugg = fix the parentheses - -parse_incorrect_semicolon = - expected item, found `;` - .suggestion = remove this semicolon - .help = {$name} declarations are not followed by a semicolon - -parse_incorrect_type_on_self = type not allowed for shorthand `self` parameter - .suggestion = move the modifiers on `self` to the type - -parse_incorrect_use_of_await = incorrect use of `await` - .parentheses_suggestion = `await` is not a method call, remove the parentheses - -parse_incorrect_use_of_await_postfix_suggestion = `await` is a postfix operation - -parse_incorrect_use_of_use = incorrect use of `use` - .parentheses_suggestion = `use` is not a method call, try removing the parentheses - -parse_incorrect_visibility_restriction = incorrect visibility restriction - .help = some possible visibility restrictions are: - `pub(crate)`: visible only on the current crate - `pub(super)`: visible only in the current module's parent - `pub(in path::to::module)`: visible only on the specified path - .suggestion = make this visible only to module `{$inner_str}` with `in` - -parse_inner_attr_explanation = inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files -parse_inner_attr_not_permitted = an inner attribute is not permitted in this context - .label_does_not_annotate_this = {parse_label_inner_attr_does_not_annotate_this} - .sugg_change_inner_to_outer = {parse_sugg_change_inner_attr_to_outer} - -parse_inner_attr_not_permitted_after_outer_attr = an inner attribute is not permitted following an outer attribute - .label_attr = not permitted following an outer attribute - .label_prev_attr = previous outer attribute - .label_does_not_annotate_this = {parse_label_inner_attr_does_not_annotate_this} - .sugg_change_inner_to_outer = {parse_sugg_change_inner_attr_to_outer} - -parse_inner_attr_not_permitted_after_outer_doc_comment = an inner attribute is not permitted following an outer doc comment - .label_attr = not permitted following an outer doc comment - .label_prev_doc_comment = previous doc comment - .label_does_not_annotate_this = {parse_label_inner_attr_does_not_annotate_this} - .sugg_change_inner_to_outer = {parse_sugg_change_inner_attr_to_outer} - -parse_inner_doc_comment_not_permitted = expected outer doc comment - .note = inner doc comments like this (starting with `//!` or `/*!`) can only appear before items - .suggestion = you might have meant to write a regular comment - .label_does_not_annotate_this = the inner doc comment doesn't annotate this {$item} - .sugg_change_inner_to_outer = to annotate the {$item}, change the doc comment from inner to outer style - -parse_invalid_block_macro_segment = cannot use a `block` macro fragment here - .label = the `block` fragment is within this context - .suggestion = wrap this in another block - -parse_invalid_char_in_escape = {parse_invalid_char_in_escape_msg}: `{$ch}` - .label = {parse_invalid_char_in_escape_msg} - -parse_invalid_char_in_escape_msg = invalid character in {$is_hex -> - [true] numeric character - *[false] unicode - } escape - - -parse_invalid_comparison_operator = invalid comparison operator `{$invalid}` - .use_instead = `{$invalid}` is not a valid comparison operator, use `{$correct}` - .spaceship_operator_invalid = `<=>` is not a valid comparison operator, use `std::cmp::Ordering` - -parse_invalid_curly_in_let_else = right curly brace `{"}"}` before `else` in a `let...else` statement not allowed -parse_invalid_digit_literal = invalid digit for a base {$base} literal - -parse_invalid_dyn_keyword = invalid `dyn` keyword - .help = `dyn` is only needed at the start of a trait `+`-separated list - .suggestion = remove this keyword - -parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else` -parse_invalid_identifier_with_leading_number = identifiers cannot start with a number - -parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid - .label = invalid suffix `{$suffix}` - -parse_invalid_logical_operator = `{$incorrect}` is not a logical operator - .note = unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators - .use_amp_amp_for_conjunction = use `&&` to perform logical conjunction - .use_pipe_pipe_for_disjunction = use `||` to perform logical disjunction - -parse_invalid_meta_item = expected unsuffixed literal, found {$descr} - .quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal - -parse_invalid_offset_of = offset_of expects dot-separated field and variant names - -parse_invalid_path_sep_in_fn_definition = invalid path separator in function definition - .suggestion = remove invalid path separator - -parse_invalid_unicode_escape = invalid unicode character escape - .label = invalid escape - .help = unicode escape must {$surrogate -> - [true] not be a surrogate - *[false] be at most 10FFFF - } - -parse_invalid_variable_declaration = - invalid variable declaration - -parse_keyword_label = labels cannot use keyword names - -parse_keyword_lifetime = - lifetimes cannot use keyword names - -parse_kw_bad_case = keyword `{$kw}` is written in the wrong case - .suggestion = write it in {$case} - -parse_label_inner_attr_does_not_annotate_this = the inner attribute doesn't annotate this {$item} -parse_label_unexpected_token = unexpected token - -parse_label_while_parsing_or_pattern_here = while parsing this or-pattern starting here - -parse_labeled_loop_in_break = parentheses are required around this expression to avoid confusion with a labeled break expression - -parse_leading_plus_not_supported = leading `+` is not supported - .label = unexpected `+` - .suggestion_remove_plus = try removing the `+` - -parse_leading_underscore_unicode_escape = {parse_leading_underscore_unicode_escape_label}: `_` -parse_leading_underscore_unicode_escape_label = invalid start of unicode escape - -parse_left_arrow_operator = unexpected token: `<-` - .suggestion = if you meant to write a comparison against a negative value, add a space in between `<` and `-` - -parse_let_chain_pre_2024 = let chains are only allowed in Rust 2024 or later - -parse_lifetime_after_mut = lifetime must precede `mut` - .suggestion = place the lifetime before `mut` - -parse_lifetime_in_borrow_expression = borrow expressions cannot be annotated with lifetimes - .suggestion = remove the lifetime annotation - .label = annotated with lifetime here - -parse_lifetime_in_eq_constraint = lifetimes are not permitted in this context - .label = lifetime is not allowed here - .context_label = this introduces an associated item binding - .help = if you meant to specify a trait object, write `dyn /* Trait */ + {$lifetime}` - .colon_sugg = you might have meant to write a bound here - -parse_lone_slash = invalid trailing slash in literal - .label = {parse_lone_slash} - -parse_loop_else = `{$loop_kind}...else` loops are not supported - .note = consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run - .loop_keyword = `else` is attached to this loop - -parse_macro_expands_to_adt_field = macros cannot expand to {$adt_ty} fields - -parse_macro_expands_to_enum_variant = macros cannot expand to enum variants - -parse_macro_invocation_visibility = can't qualify macro invocation with `pub` - .suggestion = remove the visibility - .help = try adjusting the macro to put `{$vis}` inside the invocation - -parse_macro_invocation_with_qualified_path = macros cannot use qualified paths - -parse_macro_name_remove_bang = macro names aren't followed by a `!` - .suggestion = remove the `!` - -parse_macro_rules_missing_bang = expected `!` after `macro_rules` - .suggestion = add a `!` - -parse_macro_rules_visibility = can't qualify macro_rules invocation with `{$vis}` - .suggestion = try exporting the macro - -parse_malformed_loop_label = malformed loop label - .suggestion = use the correct loop label format - -parse_match_arm_body_without_braces = `match` arm body without braces - .label_statements = {$num_statements -> - [one] this statement is not surrounded by a body - *[other] these statements are not surrounded by a body - } - .label_arrow = while parsing the `match` arm starting here - .suggestion_add_braces = surround the {$num_statements -> - [one] statement - *[other] statements - } with a body - .suggestion_use_comma_not_semicolon = replace `;` with `,` to end a `match` arm expression - -parse_maybe_comparison = you might have meant to compare for equality - -parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of `fn` - .suggestion = replace `fn` with `impl` here - -parse_maybe_missing_let = you might have meant to continue the let-chain - -parse_maybe_recover_from_bad_qpath_stage_2 = - missing angle brackets in associated item path - .suggestion = types that don't start with an identifier need to be surrounded with angle brackets in qualified paths - -parse_maybe_recover_from_bad_type_plus = - expected a path on the left-hand side of `+` - -parse_maybe_report_ambiguous_plus = - ambiguous `+` in a type - .suggestion = use parentheses to disambiguate - -parse_mismatched_closing_delimiter = mismatched closing delimiter: `{$delimiter}` - .label_unmatched = mismatched closing delimiter - .label_opening_candidate = closing delimiter possibly meant for this - .label_unclosed = unclosed delimiter - -parse_misplaced_return_type = place the return type after the function parameters - -parse_missing_comma_after_match_arm = expected `,` following `match` arm - .suggestion = missing a comma here to end this `match` arm - -parse_missing_const_type = missing type for `{$kind}` item - .suggestion = provide a type for the item - -parse_missing_enum_for_enum_definition = missing `enum` for enum definition - .suggestion = add `enum` here to parse `{$ident}` as an enum - -parse_missing_enum_or_struct_for_item_definition = missing `enum` or `struct` for enum or struct definition - -parse_missing_expression_in_for_loop = missing expression to iterate on in `for` loop - .suggestion = try adding an expression to the `for` loop - -parse_missing_fn_for_function_definition = missing `fn` for function definition - .suggestion = add `fn` here to parse `{$ident}` as a function - -parse_missing_fn_for_method_definition = missing `fn` for method definition - .suggestion = add `fn` here to parse `{$ident}` as a method - -parse_missing_fn_or_struct_for_item_definition = missing `fn` or `struct` for function or struct definition - .suggestion = if you meant to call a macro, try - .help = if you meant to call a macro, remove the `pub` and add a trailing `!` after the identifier - -parse_missing_fn_params = missing parameters for function definition - .suggestion = add a parameter list - -parse_missing_for_in_trait_impl = missing `for` in a trait impl - .suggestion = add `for` here - -parse_missing_in_in_for_loop = missing `in` in `for` loop - .use_in = try using `in` here instead - .add_in = try adding `in` here - -parse_missing_let_before_mut = missing keyword -parse_missing_plus_in_bounds = expected `+` between lifetime and {$sym} - .suggestion = add `+` - -parse_missing_semicolon_before_array = expected `;`, found `[` - .suggestion = consider adding `;` here - -parse_missing_struct_for_struct_definition = missing `struct` for struct definition - .suggestion = add `struct` here to parse `{$ident}` as a struct - -parse_missing_trait_in_trait_impl = missing trait in a trait impl - .suggestion_add_trait = add a trait here - .suggestion_remove_for = for an inherent impl, drop this `for` - -parse_misspelled_kw = {$is_incorrect_case -> - [true] write keyword `{$similar_kw}` in lowercase - *[false] there is a keyword `{$similar_kw}` with a similar name -} - -parse_modifier_lifetime = `{$modifier}` may only modify trait bounds, not lifetime bounds - .suggestion = remove the `{$modifier}` - -parse_modifiers_and_polarity = `{$modifiers_concatenated}` trait not allowed with `{$polarity}` trait polarity modifier - .label = there is not a well-defined meaning for a `{$modifiers_concatenated} {$polarity}` trait - -parse_more_than_one_char = character literal may only contain one codepoint - .followed_by = this `{$chr}` is followed by the combining {$len -> - [one] mark - *[other] marks - } `{$escaped_marks}` - .non_printing = there are non-printing characters, the full sequence is `{$escaped}` - .consider_normalized = consider using the normalized form `{$ch}` of this character - .remove_non = consider removing the non-printing characters - .use_double_quotes = if you meant to write a {$is_byte -> - [true] byte string - *[false] string - } literal, use double quotes - -parse_multiple_skipped_lines = multiple lines skipped by escaped newline - .label = skipping everything up to and including this point - -parse_multiple_where_clauses = cannot define duplicate `where` clauses on an item - .label = previous `where` clause starts here - .suggestion = consider joining the two `where` clauses into one - -parse_mut_on_nested_ident_pattern = `mut` must be attached to each individual binding - .suggestion = add `mut` to each binding -parse_mut_on_non_ident_pattern = `mut` must be followed by a named binding - .suggestion = remove the `mut` prefix - -parse_need_plus_after_trait_object_lifetime = lifetimes must be followed by `+` to form a trait object type - .suggestion = consider adding a trait bound after the potential lifetime bound - -parse_nested_adt = `{$kw_str}` definition cannot be nested inside `{$keyword}` - .suggestion = consider creating a new `{$kw_str}` definition instead of nesting - -parse_nested_c_variadic_type = C-variadic type `...` may not be nested inside another type - -parse_no_brace_unicode_escape = incorrect unicode escape sequence - .label = {parse_no_brace_unicode_escape} - .use_braces = format of unicode escape sequences uses braces - .format_of_unicode = format of unicode escape sequences is `\u{"{...}"}` - -parse_no_digits_literal = no valid digits found for number - -parse_non_string_abi_literal = non-string ABI literal - .suggestion = specify the ABI with a string literal - -parse_nonterminal_expected_ident = expected ident, found `{$token}` -parse_nonterminal_expected_item_keyword = expected an item keyword -parse_nonterminal_expected_lifetime = expected a lifetime, found `{$token}` - -parse_nonterminal_expected_statement = expected a statement - -parse_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide - -parse_note_mut_pattern_usage = `mut` may be followed by `variable` and `variable @ pattern` - -parse_note_pattern_alternatives_use_single_vert = alternatives in or-patterns are separated with `|`, not `||` - -parse_nul_in_c_str = null characters in C string literals are not supported - -parse_or_in_let_chain = `||` operators are not supported in let chain conditions - -parse_or_pattern_not_allowed_in_fn_parameters = function parameters require top-level or-patterns in parentheses -parse_or_pattern_not_allowed_in_let_binding = `let` bindings require top-level or-patterns in parentheses - -parse_outer_attr_explanation = outer attributes, like `#[test]`, annotate the item following them - -parse_outer_attribute_not_allowed_on_if_else = outer attributes are not allowed on `if` and `else` branches - .branch_label = the attributes are attached to this branch - .ctx_label = the branch belongs to this `{$ctx}` - .suggestion = remove the attributes - -parse_overlong_unicode_escape = overlong unicode escape - .label = must have at most 6 hex digits - -parse_parentheses_with_struct_fields = invalid `struct` delimiters or `fn` call arguments - .suggestion_braces_for_struct = if `{$type}` is a struct, use braces as delimiters - .suggestion_no_fields_for_fn = if `{$type}` is a function, use the arguments directly - -parse_path_double_colon = path separator must be a double colon - .suggestion = use a double colon instead - - -parse_path_found_attribute_in_params = `Trait(...)` syntax does not support attributes in parameters - .suggestion = remove the attributes - -parse_path_found_c_variadic_params = `Trait(...)` syntax does not support c_variadic parameters - .suggestion = remove the `...` - -parse_path_found_named_params = `Trait(...)` syntax does not support named parameters - .suggestion = remove the parameter name - -parse_pattern_method_param_without_body = patterns aren't allowed in methods without bodies - .suggestion = give this argument a name or use an underscore to ignore it - -parse_pattern_on_wrong_side_of_at = pattern on wrong side of `@` - .label_pattern = pattern on the left, should be on the right - .label_binding = binding on the right, should be on the left - .suggestion = switch the order - -parse_question_mark_in_type = invalid `?` in type - .label = `?` is only allowed on expressions, not types - .suggestion = if you meant to express that the type might not contain a value, use the `Option` wrapper type - -parse_recover_import_as_use = expected item, found {$token_name} - .suggestion = items are imported using the `use` keyword - -parse_remove_let = expected pattern, found `let` - .suggestion = remove the unnecessary `let` keyword - -parse_repeated_mut_in_pattern = `mut` on a binding may not be repeated - .suggestion = remove the additional `mut`s - -parse_require_colon_after_labeled_expression = labeled expression must be followed by `:` - .note = labels are used before loops and blocks, allowing e.g., `break 'label` to them - .label = the label - .suggestion = add `:` after the label - -parse_reserved_multihash = reserved multi-hash token is forbidden - .note = sequences of two or more # are reserved for future use since Rust 2024 - .suggestion_whitespace = consider inserting whitespace here - -parse_reserved_string = invalid string literal - .note = unprefixed guarded string literals are reserved for future use since Rust 2024 - .suggestion_whitespace = consider inserting whitespace here - -parse_return_types_use_thin_arrow = return types are denoted using `->` - .suggestion = use `->` instead - -parse_self_argument_pointer = cannot pass `self` by raw pointer - .label = cannot pass `self` by raw pointer - -parse_self_param_not_first = unexpected `self` parameter in function - .label = must be the first parameter of an associated function - -parse_shift_interpreted_as_generic = - `<<` is interpreted as a start of generic arguments for `{$type}`, not a shift - .label_args = interpreted as generic arguments - .label_comparison = not interpreted as shift - .suggestion = try shifting the cast value - -parse_single_colon_import_path = expected `::`, found `:` - .suggestion = use double colon - .note = import paths are delimited using `::` - -parse_static_with_generics = static items may not have generic parameters - -parse_struct_literal_body_without_path = - struct literal body without path - .suggestion = you might have forgotten to add the struct literal inside the block - -parse_struct_literal_body_without_path_late = - struct literal body without path - .label = struct name missing for struct literal - .suggestion = add the correct type - -parse_struct_literal_not_allowed_here = struct literals are not allowed here - .suggestion = surround the struct literal with parentheses - -parse_struct_literal_placeholder_path = - placeholder `_` is not allowed for the path in struct literals - .label = not allowed in struct literals - .suggestion = replace it with the correct type - -parse_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes - .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) - -parse_sugg_add_let_for_stmt = you might have meant to introduce a new binding - -parse_sugg_add_semi = add `;` here -parse_sugg_change_inner_attr_to_outer = to annotate the {$item}, change the attribute from inner to outer style - -parse_sugg_change_this_to_semi = change this to `;` -parse_sugg_escape_identifier = escape `{$ident_name}` to use it as an identifier - -parse_sugg_remove_comma = remove this comma -parse_sugg_remove_leading_vert_in_pattern = remove the `|` -parse_sugg_turbofish_syntax = use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments - -parse_sugg_wrap_expression_in_parentheses = wrap the expression in parentheses - -parse_sugg_wrap_macro_in_parentheses = use parentheses instead of braces for this macro - -parse_sugg_wrap_pattern_in_parens = wrap the pattern in parentheses - -parse_switch_mut_let_order = - switch the order of `mut` and `let` - -parse_switch_ref_box_order = switch the order of `ref` and `box` - .suggestion = swap them - -parse_ternary_operator = Rust has no ternary operator - -parse_tilde_is_not_unary_operator = `~` cannot be used as a unary operator - .suggestion = use `!` to perform bitwise not - -parse_too_many_hashes = too many `#` symbols: raw strings may be delimited by up to 255 `#` symbols, but found {$num} - -parse_too_short_hex_escape = numeric character escape is too short - -parse_trailing_vert_not_allowed = a trailing `{$token}` is not allowed in an or-pattern - -parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto` -parse_trait_alias_cannot_be_unsafe = trait aliases cannot be `unsafe` - -parse_trait_impl_modifier_in_inherent_impl = inherent impls cannot be {$modifier_name} - .because = {$modifier_name} because of this - .type = inherent impl for this type - .note = only trait implementations may be annotated with `{$modifier}` - -parse_transpose_dyn_or_impl = `for<...>` expected after `{$kw}`, not before - .suggestion = move `{$kw}` before the `for<...>` - -parse_unclosed_unicode_escape = unterminated unicode escape - .label = missing a closing `{"}"}` - .terminate = terminate the unicode escape - -parse_underscore_literal_suffix = underscore literal suffix is not allowed - -parse_unexpected_const_in_generic_param = expected lifetime, type, or constant, found keyword `const` - .suggestion = the `const` keyword is only needed in the definition of the type - -parse_unexpected_const_param_declaration = unexpected `const` parameter declaration - .label = expected a `const` expression, not a parameter declaration - .suggestion = `const` parameters must be declared for the `impl` - -parse_unexpected_default_value_for_lifetime_in_generic_parameters = unexpected default lifetime parameter - .label = lifetime parameters cannot have default values - -parse_unexpected_expr_in_pat = - expected {$is_bound -> - [true] a pattern range bound - *[false] a pattern - }, found an expression - - .label = not a pattern - .note = arbitrary expressions are not allowed in patterns: - -parse_unexpected_expr_in_pat_const_sugg = consider extracting the expression into a `const` - -parse_unexpected_expr_in_pat_create_guard_sugg = consider moving the expression to a match arm guard - -parse_unexpected_expr_in_pat_update_guard_sugg = consider moving the expression to the match arm guard - -parse_unexpected_if_with_if = unexpected `if` in the condition expression - .suggestion = remove the `if` - -parse_unexpected_lifetime_in_pattern = unexpected lifetime `{$symbol}` in pattern - .suggestion = remove the lifetime - -parse_unexpected_paren_in_range_pat = range pattern bounds cannot have parentheses -parse_unexpected_paren_in_range_pat_sugg = remove these parentheses - -parse_unexpected_parentheses_in_for_head = unexpected parentheses surrounding `for` loop head - .suggestion = remove parentheses in `for` loop - -parse_unexpected_parentheses_in_match_arm_pattern = unexpected parentheses surrounding `match` arm pattern - .suggestion = remove parentheses surrounding the pattern - -parse_unexpected_self_in_generic_parameters = unexpected keyword `Self` in generic parameters - .note = you cannot use `Self` as a generic parameter because it is reserved for associated items - -parse_unexpected_token_after_dot = unexpected token: {$actual} - -parse_unexpected_token_after_label = expected `while`, `for`, `loop` or `{"{"}` after a label - .suggestion_remove_label = consider removing the label - .suggestion_enclose_in_block = consider enclosing expression in a block - -parse_unexpected_token_after_not = unexpected {$negated_desc} after identifier -parse_unexpected_token_after_not_bitwise = use `!` to perform bitwise not -parse_unexpected_token_after_not_default = use `!` to perform logical negation or bitwise not - -parse_unexpected_token_after_not_logical = use `!` to perform logical negation -parse_unexpected_token_after_struct_name = expected `where`, `{"{"}`, `(`, or `;` after struct name -parse_unexpected_token_after_struct_name_found_doc_comment = expected `where`, `{"{"}`, `(`, or `;` after struct name, found doc comment `{$token}` -parse_unexpected_token_after_struct_name_found_keyword = expected `where`, `{"{"}`, `(`, or `;` after struct name, found keyword `{$token}` -# This one deliberately doesn't print a token. -parse_unexpected_token_after_struct_name_found_metavar = expected `where`, `{"{"}`, `(`, or `;` after struct name, found metavar -parse_unexpected_token_after_struct_name_found_other = expected `where`, `{"{"}`, `(`, or `;` after struct name, found `{$token}` - -parse_unexpected_token_after_struct_name_found_reserved_identifier = expected `where`, `{"{"}`, `(`, or `;` after struct name, found reserved identifier `{$token}` -parse_unexpected_token_after_struct_name_found_reserved_keyword = expected `where`, `{"{"}`, `(`, or `;` after struct name, found reserved keyword `{$token}` -parse_unexpected_vert_vert_before_function_parameter = unexpected `||` before function parameter - .suggestion = remove the `||` - -parse_unexpected_vert_vert_in_pattern = unexpected token `||` in pattern - .suggestion = use a single `|` to separate multiple alternative patterns - -parse_unicode_escape_in_byte = unicode escape in byte string - .label = {parse_unicode_escape_in_byte} - .help = unicode escape sequences cannot be used as a byte or in a byte string - -parse_unknown_builtin_construct = unknown `builtin #` construct `{$name}` - -parse_unknown_prefix = prefix `{$prefix}` is unknown - .label = unknown prefix - .note = prefixed identifiers and literals are reserved since Rust 2021 - .suggestion_br = use `br` for a raw byte string - .suggestion_cr = use `cr` for a raw C-string - .suggestion_str = if you meant to write a string literal, use double quotes - .suggestion_whitespace = consider inserting whitespace here - -parse_unknown_start_of_token = unknown start of token: {$escaped} - .sugg_quotes = Unicode characters '“' (Left Double Quotation Mark) and '”' (Right Double Quotation Mark) look like '{$ascii_str}' ({$ascii_name}), but are not - .sugg_other = Unicode character '{$ch}' ({$u_name}) looks like '{$ascii_str}' ({$ascii_name}), but it is not - .help_null = source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used - .help_invisible_char = invisible characters like '{$escaped}' are not usually visible in text editors - .note_repeats = character appears {$repeats -> - [one] once more - *[other] {$repeats} more times - } - -parse_unmatched_angle = unmatched angle {$plural -> - [true] brackets - *[false] bracket - } - .suggestion = remove extra angle {$plural -> - [true] brackets - *[false] bracket - } - -parse_unmatched_angle_brackets = {$num_extra_brackets -> - [one] unmatched angle bracket - *[other] unmatched angle brackets - } - .suggestion = {$num_extra_brackets -> - [one] remove extra angle bracket - *[other] remove extra angle brackets - } - - -parse_unskipped_whitespace = whitespace symbol '{$ch}' is not skipped - .label = {parse_unskipped_whitespace} - -parse_use_empty_block_not_semi = expected { "`{}`" }, found `;` - .suggestion = try using { "`{}`" } instead - -parse_use_eq_instead = unexpected `==` - .suggestion = try using `=` instead - -parse_use_if_else = use an `if-else` expression instead - -parse_use_let_not_auto = write `let` instead of `auto` to introduce a new variable -parse_use_let_not_var = write `let` instead of `var` to introduce a new variable - -parse_varargs_without_pattern = missing pattern for `...` argument - .suggestion = name the argument, or use `_` to continue ignoring it - -parse_visibility_not_followed_by_item = visibility `{$vis}` is not followed by an item - .label = the visibility - .help = you likely meant to define an item, e.g., `{$vis} fn foo() {"{}"}` - -parse_where_clause_before_const_body = where clauses are not allowed before const item bodies - .label = unexpected where clause - .name_label = while parsing this const item - .body_label = the item body - .suggestion = move the body before the where clause - -parse_where_clause_before_tuple_struct_body = where clauses are not allowed before tuple struct bodies - .label = unexpected where clause - .name_label = while parsing this tuple struct - .body_label = the struct body - .suggestion = move the body before the where clause - -parse_where_generics = generic parameters on `where` clauses are reserved for future use - .label = currently unsupported - -parse_zero_chars = empty character literal - .label = {parse_zero_chars} diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 4e789a321649..e73d9a1bf56a 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3,24 +3,21 @@ use std::borrow::Cow; use std::path::PathBuf; -use rustc_ast::token::Token; +use rustc_ast::token::{self, InvisibleOrigin, MetaVarKind, Token}; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{Path, Visibility}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, - Level, Subdiagnostic, SuggestionStyle, + Level, Subdiagnostic, SuggestionStyle, inline_fluent, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; use rustc_span::{Ident, Span, Symbol}; -use crate::fluent_generated as fluent; -use crate::parser::{ForbiddenLetReason, TokenDescription}; - #[derive(Diagnostic)] -#[diag(parse_maybe_report_ambiguous_plus)] +#[diag("ambiguous `+` in a type")] pub(crate) struct AmbiguousPlus { #[primary_span] pub span: Span, @@ -29,7 +26,7 @@ pub(crate) struct AmbiguousPlus { } #[derive(Diagnostic)] -#[diag(parse_maybe_recover_from_bad_type_plus, code = E0178)] +#[diag("expected a path on the left-hand side of `+`", code = E0178)] pub(crate) struct BadTypePlus { #[primary_span] pub span: Span, @@ -38,7 +35,7 @@ pub(crate) struct BadTypePlus { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_add_paren, applicability = "machine-applicable")] +#[multipart_suggestion("try adding parentheses", applicability = "machine-applicable")] pub(crate) struct AddParen { #[suggestion_part(code = "(")] pub lo: Span, @@ -52,12 +49,12 @@ pub(crate) enum BadTypePlusSub { #[subdiagnostic] suggestion: AddParen, }, - #[label(parse_forgot_paren)] + #[label("perhaps you forgot parentheses?")] ForgotParen { #[primary_span] span: Span, }, - #[label(parse_expect_path)] + #[label("expected a path")] ExpectPath { #[primary_span] span: Span, @@ -65,7 +62,7 @@ pub(crate) enum BadTypePlusSub { } #[derive(Diagnostic)] -#[diag(parse_maybe_recover_from_bad_qpath_stage_2)] +#[diag("missing angle brackets in associated item path")] pub(crate) struct BadQPathStage2 { #[primary_span] pub span: Span, @@ -74,21 +71,24 @@ pub(crate) struct BadQPathStage2 { } #[derive(Diagnostic)] -#[diag(parse_trait_impl_modifier_in_inherent_impl)] -#[note] +#[diag("inherent impls cannot be {$modifier_name}")] +#[note("only trait implementations may be annotated with `{$modifier}`")] pub(crate) struct TraitImplModifierInInherentImpl { #[primary_span] pub span: Span, pub modifier: &'static str, pub modifier_name: &'static str, - #[label(parse_because)] + #[label("{$modifier_name} because of this")] pub modifier_span: Span, - #[label(parse_type)] + #[label("inherent impl for this type")] pub self_ty: Span, } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + "types that don't start with an identifier need to be surrounded with angle brackets in qualified paths", + applicability = "machine-applicable" +)] pub(crate) struct WrapType { #[suggestion_part(code = "<")] pub lo: Span, @@ -97,22 +97,27 @@ pub(crate) struct WrapType { } #[derive(Diagnostic)] -#[diag(parse_incorrect_semicolon)] +#[diag("expected item, found `;`")] pub(crate) struct IncorrectSemicolon<'a> { #[primary_span] - #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] + #[suggestion( + "remove this semicolon", + style = "verbose", + code = "", + applicability = "machine-applicable" + )] pub span: Span, - #[help] + #[help("{$name} declarations are not followed by a semicolon")] pub show_help: bool, pub name: &'a str, } #[derive(Diagnostic)] -#[diag(parse_incorrect_use_of_await)] +#[diag("incorrect use of `await`")] pub(crate) struct IncorrectUseOfAwait { #[primary_span] #[suggestion( - parse_parentheses_suggestion, + "`await` is not a method call, remove the parentheses", style = "verbose", code = "", applicability = "machine-applicable" @@ -121,11 +126,11 @@ pub(crate) struct IncorrectUseOfAwait { } #[derive(Diagnostic)] -#[diag(parse_incorrect_use_of_use)] +#[diag("incorrect use of `use`")] pub(crate) struct IncorrectUseOfUse { #[primary_span] #[suggestion( - parse_parentheses_suggestion, + "`use` is not a method call, try removing the parentheses", style = "verbose", code = "", applicability = "machine-applicable" @@ -134,10 +139,7 @@ pub(crate) struct IncorrectUseOfUse { } #[derive(Subdiagnostic)] -#[multipart_suggestion( - parse_incorrect_use_of_await_postfix_suggestion, - applicability = "machine-applicable" -)] +#[multipart_suggestion("`await` is a postfix operation", applicability = "machine-applicable")] pub(crate) struct AwaitSuggestion { #[suggestion_part(code = "")] pub removal: Span, @@ -147,7 +149,7 @@ pub(crate) struct AwaitSuggestion { } #[derive(Diagnostic)] -#[diag(parse_incorrect_use_of_await)] +#[diag("incorrect use of `await`")] pub(crate) struct IncorrectAwait { #[primary_span] pub span: Span, @@ -156,16 +158,21 @@ pub(crate) struct IncorrectAwait { } #[derive(Diagnostic)] -#[diag(parse_in_in_typo)] +#[diag("expected iterable, found keyword `in`")] pub(crate) struct InInTypo { #[primary_span] pub span: Span, - #[suggestion(code = "", style = "verbose", applicability = "machine-applicable")] + #[suggestion( + "remove the duplicated `in`", + code = "", + style = "verbose", + applicability = "machine-applicable" + )] pub sugg_span: Span, } #[derive(Diagnostic)] -#[diag(parse_invalid_variable_declaration)] +#[diag("invalid variable declaration")] pub(crate) struct InvalidVariableDeclaration { #[primary_span] pub span: Span, @@ -176,28 +183,28 @@ pub(crate) struct InvalidVariableDeclaration { #[derive(Subdiagnostic)] pub(crate) enum InvalidVariableDeclarationSub { #[suggestion( - parse_switch_mut_let_order, + "switch the order of `mut` and `let`", style = "verbose", applicability = "maybe-incorrect", code = "let mut" )] SwitchMutLetOrder(#[primary_span] Span), #[suggestion( - parse_missing_let_before_mut, + "missing keyword", applicability = "machine-applicable", style = "verbose", code = "let mut" )] MissingLet(#[primary_span] Span), #[suggestion( - parse_use_let_not_auto, + "write `let` instead of `auto` to introduce a new variable", style = "verbose", applicability = "machine-applicable", code = "let" )] UseLetNotAuto(#[primary_span] Span), #[suggestion( - parse_use_let_not_var, + "write `let` instead of `var` to introduce a new variable", style = "verbose", applicability = "machine-applicable", code = "let" @@ -206,15 +213,20 @@ pub(crate) enum InvalidVariableDeclarationSub { } #[derive(Diagnostic)] -#[diag(parse_switch_ref_box_order)] +#[diag("switch the order of `ref` and `box`")] pub(crate) struct SwitchRefBoxOrder { #[primary_span] - #[suggestion(applicability = "machine-applicable", style = "verbose", code = "box ref")] + #[suggestion( + "swap them", + applicability = "machine-applicable", + style = "verbose", + code = "box ref" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_invalid_comparison_operator)] +#[diag("invalid comparison operator `{$invalid}`")] pub(crate) struct InvalidComparisonOperator { #[primary_span] pub span: Span, @@ -226,7 +238,7 @@ pub(crate) struct InvalidComparisonOperator { #[derive(Subdiagnostic)] pub(crate) enum InvalidComparisonOperatorSub { #[suggestion( - parse_use_instead, + "`{$invalid}` is not a valid comparison operator, use `{$correct}`", style = "verbose", applicability = "machine-applicable", code = "{correct}" @@ -237,13 +249,13 @@ pub(crate) enum InvalidComparisonOperatorSub { invalid: String, correct: String, }, - #[label(parse_spaceship_operator_invalid)] + #[label("`<=>` is not a valid comparison operator, use `std::cmp::Ordering`")] Spaceship(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(parse_invalid_logical_operator)] -#[note] +#[diag("`{$incorrect}` is not a logical operator")] +#[note("unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators")] pub(crate) struct InvalidLogicalOperator { #[primary_span] pub span: Span, @@ -255,14 +267,14 @@ pub(crate) struct InvalidLogicalOperator { #[derive(Subdiagnostic)] pub(crate) enum InvalidLogicalOperatorSub { #[suggestion( - parse_use_amp_amp_for_conjunction, + "use `&&` to perform logical conjunction", style = "verbose", applicability = "machine-applicable", code = "&&" )] Conjunction(#[primary_span] Span), #[suggestion( - parse_use_pipe_pipe_for_disjunction, + "use `||` to perform logical disjunction", style = "verbose", applicability = "machine-applicable", code = "||" @@ -271,15 +283,20 @@ pub(crate) enum InvalidLogicalOperatorSub { } #[derive(Diagnostic)] -#[diag(parse_tilde_is_not_unary_operator)] +#[diag("`~` cannot be used as a unary operator")] pub(crate) struct TildeAsUnaryOperator( #[primary_span] - #[suggestion(style = "verbose", applicability = "machine-applicable", code = "!")] + #[suggestion( + "use `!` to perform bitwise not", + style = "verbose", + applicability = "machine-applicable", + code = "!" + )] pub Span, ); #[derive(Diagnostic)] -#[diag(parse_unexpected_token_after_not)] +#[diag("unexpected {$negated_desc} after identifier")] pub(crate) struct NotAsNegationOperator { #[primary_span] pub negated: Span, @@ -291,7 +308,7 @@ pub(crate) struct NotAsNegationOperator { #[derive(Subdiagnostic)] pub(crate) enum NotAsNegationOperatorSub { #[suggestion( - parse_unexpected_token_after_not_default, + "use `!` to perform logical negation or bitwise not", style = "verbose", applicability = "machine-applicable", code = "!" @@ -299,7 +316,7 @@ pub(crate) enum NotAsNegationOperatorSub { SuggestNotDefault(#[primary_span] Span), #[suggestion( - parse_unexpected_token_after_not_bitwise, + "use `!` to perform bitwise not", style = "verbose", applicability = "machine-applicable", code = "!" @@ -307,7 +324,7 @@ pub(crate) enum NotAsNegationOperatorSub { SuggestNotBitwise(#[primary_span] Span), #[suggestion( - parse_unexpected_token_after_not_logical, + "use `!` to perform logical negation", style = "verbose", applicability = "machine-applicable", code = "!" @@ -316,46 +333,59 @@ pub(crate) enum NotAsNegationOperatorSub { } #[derive(Diagnostic)] -#[diag(parse_malformed_loop_label)] +#[diag("malformed loop label")] pub(crate) struct MalformedLoopLabel { #[primary_span] pub span: Span, - #[suggestion(applicability = "machine-applicable", code = "'", style = "verbose")] + #[suggestion( + "use the correct loop label format", + applicability = "machine-applicable", + code = "'", + style = "verbose" + )] pub suggestion: Span, } #[derive(Diagnostic)] -#[diag(parse_lifetime_in_borrow_expression)] +#[diag("borrow expressions cannot be annotated with lifetimes")] pub(crate) struct LifetimeInBorrowExpression { #[primary_span] pub span: Span, - #[suggestion(applicability = "machine-applicable", code = "", style = "verbose")] - #[label] + #[suggestion( + "remove the lifetime annotation", + applicability = "machine-applicable", + code = "", + style = "verbose" + )] + #[label("annotated with lifetime here")] pub lifetime_span: Span, } #[derive(Diagnostic)] -#[diag(parse_field_expression_with_generic)] +#[diag("field expressions cannot have generic arguments")] pub(crate) struct FieldExpressionWithGeneric(#[primary_span] pub Span); #[derive(Diagnostic)] -#[diag(parse_macro_invocation_with_qualified_path)] +#[diag("macros cannot use qualified paths")] pub(crate) struct MacroInvocationWithQualifiedPath(#[primary_span] pub Span); #[derive(Diagnostic)] -#[diag(parse_unexpected_token_after_label)] +#[diag("expected `while`, `for`, `loop` or `{\"{\"}` after a label")] pub(crate) struct UnexpectedTokenAfterLabel { #[primary_span] - #[label(parse_unexpected_token_after_label)] + #[label("expected `while`, `for`, `loop` or `{\"{\"}` after a label")] pub span: Span, - #[suggestion(parse_suggestion_remove_label, style = "verbose", code = "")] + #[suggestion("consider removing the label", style = "verbose", code = "")] pub remove_label: Option, #[subdiagnostic] pub enclose_in_block: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion_enclose_in_block, applicability = "machine-applicable")] +#[multipart_suggestion( + "consider enclosing expression in a block", + applicability = "machine-applicable" +)] pub(crate) struct UnexpectedTokenAfterLabelSugg { #[suggestion_part(code = "{{ ")] pub left: Span, @@ -364,66 +394,91 @@ pub(crate) struct UnexpectedTokenAfterLabelSugg { } #[derive(Diagnostic)] -#[diag(parse_require_colon_after_labeled_expression)] -#[note] +#[diag("labeled expression must be followed by `:`")] +#[note("labels are used before loops and blocks, allowing e.g., `break 'label` to them")] pub(crate) struct RequireColonAfterLabeledExpression { #[primary_span] pub span: Span, - #[label] + #[label("the label")] pub label: Span, - #[suggestion(style = "verbose", applicability = "machine-applicable", code = ": ")] + #[suggestion( + "add `:` after the label", + style = "verbose", + applicability = "machine-applicable", + code = ": " + )] pub label_end: Span, } #[derive(Diagnostic)] -#[diag(parse_do_catch_syntax_removed)] -#[note] +#[diag("found removed `do catch` syntax")] +#[note("following RFC #2388, the new non-placeholder syntax is `try`")] pub(crate) struct DoCatchSyntaxRemoved { #[primary_span] - #[suggestion(applicability = "machine-applicable", code = "try", style = "verbose")] + #[suggestion( + "replace with the new syntax", + applicability = "machine-applicable", + code = "try", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_float_literal_requires_integer_part)] +#[diag("float literals must have an integer part")] pub(crate) struct FloatLiteralRequiresIntegerPart { #[primary_span] pub span: Span, - #[suggestion(applicability = "machine-applicable", code = "0", style = "verbose")] + #[suggestion( + "must have an integer part", + applicability = "machine-applicable", + code = "0", + style = "verbose" + )] pub suggestion: Span, } #[derive(Diagnostic)] -#[diag(parse_missing_semicolon_before_array)] +#[diag("expected `;`, found `[`")] pub(crate) struct MissingSemicolonBeforeArray { #[primary_span] pub open_delim: Span, - #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = ";")] + #[suggestion( + "consider adding `;` here", + style = "verbose", + applicability = "maybe-incorrect", + code = ";" + )] pub semicolon: Span, } #[derive(Diagnostic)] -#[diag(parse_expect_dotdot_not_dotdotdot)] +#[diag("expected `..`, found `...`")] pub(crate) struct MissingDotDot { #[primary_span] pub token_span: Span, - #[suggestion(applicability = "maybe-incorrect", code = "..", style = "verbose")] + #[suggestion( + "use `..` to fill in the rest of the fields", + applicability = "maybe-incorrect", + code = "..", + style = "verbose" + )] pub sugg_span: Span, } #[derive(Diagnostic)] -#[diag(parse_invalid_block_macro_segment)] +#[diag("cannot use a `block` macro fragment here")] pub(crate) struct InvalidBlockMacroSegment { #[primary_span] pub span: Span, - #[label] + #[label("the `block` fragment is within this context")] pub context: Span, #[subdiagnostic] pub wrap: WrapInExplicitBlock, } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion("wrap this in another block", applicability = "machine-applicable")] pub(crate) struct WrapInExplicitBlock { #[suggestion_part(code = "{{ ")] pub lo: Span, @@ -432,7 +487,7 @@ pub(crate) struct WrapInExplicitBlock { } #[derive(Diagnostic)] -#[diag(parse_if_expression_missing_then_block)] +#[diag("this `if` expression is missing a block after the condition")] pub(crate) struct IfExpressionMissingThenBlock { #[primary_span] pub if_span: Span, @@ -444,14 +499,14 @@ pub(crate) struct IfExpressionMissingThenBlock { #[derive(Subdiagnostic)] pub(crate) enum IfExpressionMissingThenBlockSub { - #[help(parse_condition_possibly_unfinished)] + #[help("this binary operation is possibly unfinished")] UnfinishedCondition(#[primary_span] Span), - #[help(parse_add_then_block)] + #[help("add a block here")] AddThenBlock(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(parse_ternary_operator)] +#[diag("Rust has no ternary operator")] pub(crate) struct TernaryOperator { #[primary_span] pub span: Span, @@ -459,12 +514,16 @@ pub(crate) struct TernaryOperator { #[subdiagnostic] pub sugg: Option, /// Otherwise, just print the suggestion message - #[help(parse_use_if_else)] + #[help("use an `if-else` expression instead")] pub no_sugg: bool, } #[derive(Subdiagnostic, Copy, Clone)] -#[multipart_suggestion(parse_use_if_else, applicability = "maybe-incorrect", style = "verbose")] +#[multipart_suggestion( + "use an `if-else` expression instead", + applicability = "maybe-incorrect", + style = "verbose" +)] pub(crate) struct TernaryOperatorSuggestion { #[suggestion_part(code = "if ")] pub before_cond: Span, @@ -478,7 +537,7 @@ pub(crate) struct TernaryOperatorSuggestion { #[derive(Subdiagnostic)] #[suggestion( - parse_extra_if_in_let_else, + "remove the `if` if you meant to write a `let...else` statement", applicability = "maybe-incorrect", code = "", style = "verbose" @@ -489,18 +548,20 @@ pub(crate) struct IfExpressionLetSomeSub { } #[derive(Diagnostic)] -#[diag(parse_if_expression_missing_condition)] +#[diag("missing condition for `if` expression")] pub(crate) struct IfExpressionMissingCondition { #[primary_span] - #[label(parse_condition_label)] + #[label("expected condition here")] pub if_span: Span, - #[label(parse_block_label)] + #[label( + "if this block is the condition of the `if` expression, then it must be followed by another block" + )] pub block_span: Span, } #[derive(Diagnostic)] -#[diag(parse_expected_expression_found_let)] -#[note] +#[diag("expected expression, found `let` statement")] +#[note("only supported directly in conditions of `if` and `while` expressions")] pub(crate) struct ExpectedExpressionFoundLet { #[primary_span] pub span: Span, @@ -513,7 +574,25 @@ pub(crate) struct ExpectedExpressionFoundLet { } #[derive(Diagnostic)] -#[diag(parse_or_in_let_chain)] +#[diag("let-chain with missing `let`")] +pub(crate) struct LetChainMissingLet { + #[primary_span] + pub span: Span, + #[label("expected `let` expression, found assignment")] + pub label_span: Span, + #[label("let expression later in the condition")] + pub rhs_span: Span, + #[suggestion( + "add `let` before the expression", + applicability = "maybe-incorrect", + code = "let ", + style = "verbose" + )] + pub sug_span: Span, +} + +#[derive(Diagnostic)] +#[diag("`||` operators are not supported in let chain conditions")] pub(crate) struct OrInLetChain { #[primary_span] pub span: Span, @@ -521,7 +600,7 @@ pub(crate) struct OrInLetChain { #[derive(Subdiagnostic, Clone, Copy)] #[multipart_suggestion( - parse_maybe_missing_let, + "you might have meant to continue the let-chain", applicability = "maybe-incorrect", style = "verbose" )] @@ -532,7 +611,7 @@ pub(crate) struct MaybeMissingLet { #[derive(Subdiagnostic, Clone, Copy)] #[multipart_suggestion( - parse_maybe_comparison, + "you might have meant to compare for equality", applicability = "maybe-incorrect", style = "verbose" )] @@ -542,56 +621,71 @@ pub(crate) struct MaybeComparison { } #[derive(Diagnostic)] -#[diag(parse_expect_eq_instead_of_eqeq)] +#[diag("expected `=`, found `==`")] pub(crate) struct ExpectedEqForLetExpr { #[primary_span] pub span: Span, - #[suggestion(applicability = "maybe-incorrect", code = "=", style = "verbose")] + #[suggestion( + "consider using `=` here", + applicability = "maybe-incorrect", + code = "=", + style = "verbose" + )] pub sugg_span: Span, } #[derive(Diagnostic)] -#[diag(parse_expected_else_block)] +#[diag("expected `{\"{\"}`, found {$first_tok}")] pub(crate) struct ExpectedElseBlock { #[primary_span] pub first_tok_span: Span, pub first_tok: String, - #[label] + #[label("expected an `if` or a block after this `else`")] pub else_span: Span, - #[suggestion(applicability = "maybe-incorrect", code = "if ", style = "verbose")] + #[suggestion( + "add an `if` if this is the condition of a chained `else if` statement", + applicability = "maybe-incorrect", + code = "if ", + style = "verbose" + )] pub condition_start: Span, } #[derive(Diagnostic)] -#[diag(parse_expected_struct_field)] +#[diag("expected one of `,`, `:`, or `{\"}\"}`, found `{$token}`")] pub(crate) struct ExpectedStructField { #[primary_span] - #[label] + #[label("expected one of `,`, `:`, or `{\"}\"}`")] pub span: Span, pub token: Token, - #[label(parse_ident_label)] + #[label("while parsing this struct field")] pub ident_span: Span, } #[derive(Diagnostic)] -#[diag(parse_outer_attribute_not_allowed_on_if_else)] +#[diag("outer attributes are not allowed on `if` and `else` branches")] pub(crate) struct OuterAttributeNotAllowedOnIfElse { #[primary_span] pub last: Span, - #[label(parse_branch_label)] + #[label("the attributes are attached to this branch")] pub branch_span: Span, - #[label(parse_ctx_label)] + #[label("the branch belongs to this `{$ctx}`")] pub ctx_span: Span, pub ctx: String, - #[suggestion(applicability = "machine-applicable", code = "", style = "verbose")] + #[suggestion( + "remove the attributes", + applicability = "machine-applicable", + code = "", + style = "verbose" + )] pub attributes: Span, } #[derive(Diagnostic)] -#[diag(parse_missing_in_in_for_loop)] +#[diag("missing `in` in `for` loop")] pub(crate) struct MissingInInForLoop { #[primary_span] pub span: Span, @@ -603,20 +697,36 @@ pub(crate) struct MissingInInForLoop { pub(crate) enum MissingInInForLoopSub { // User wrote `for pat of expr {}` // Has been misleading, at least in the past (closed Issue #48492), thus maybe-incorrect - #[suggestion(parse_use_in, style = "verbose", applicability = "maybe-incorrect", code = "in")] + #[suggestion( + "try using `in` here instead", + style = "verbose", + applicability = "maybe-incorrect", + code = "in" + )] InNotOf(#[primary_span] Span), // User wrote `for pat = expr {}` - #[suggestion(parse_use_in, style = "verbose", applicability = "maybe-incorrect", code = "in")] + #[suggestion( + "try using `in` here instead", + style = "verbose", + applicability = "maybe-incorrect", + code = "in" + )] InNotEq(#[primary_span] Span), - #[suggestion(parse_add_in, style = "verbose", applicability = "maybe-incorrect", code = " in ")] + #[suggestion( + "try adding `in` here", + style = "verbose", + applicability = "maybe-incorrect", + code = " in " + )] AddIn(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(parse_missing_expression_in_for_loop)] +#[diag("missing expression to iterate on in `for` loop")] pub(crate) struct MissingExpressionInForLoop { #[primary_span] #[suggestion( + "try adding an expression to the `for` loop", code = "/* expression */ ", applicability = "has-placeholders", style = "verbose" @@ -625,63 +735,80 @@ pub(crate) struct MissingExpressionInForLoop { } #[derive(Diagnostic)] -#[diag(parse_loop_else)] -#[note] +#[diag("`{$loop_kind}...else` loops are not supported")] +#[note( + "consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run" +)] pub(crate) struct LoopElseNotSupported { #[primary_span] pub span: Span, pub loop_kind: &'static str, - #[label(parse_loop_keyword)] + #[label("`else` is attached to this loop")] pub loop_kw: Span, } #[derive(Diagnostic)] -#[diag(parse_missing_comma_after_match_arm)] +#[diag("expected `,` following `match` arm")] pub(crate) struct MissingCommaAfterMatchArm { #[primary_span] - #[suggestion(applicability = "machine-applicable", code = ",", style = "verbose")] + #[suggestion( + "missing a comma here to end this `match` arm", + applicability = "machine-applicable", + code = ",", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_catch_after_try)] -#[help] +#[diag("keyword `catch` cannot follow a `try` block")] +#[help("try using `match` on the result of the `try` block instead")] pub(crate) struct CatchAfterTry { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_comma_after_base_struct)] -#[note] +#[diag("cannot use a comma after the base struct")] +#[note("the base struct must always be the last field")] pub(crate) struct CommaAfterBaseStruct { #[primary_span] pub span: Span, - #[suggestion(style = "verbose", applicability = "machine-applicable", code = "")] + #[suggestion( + "remove this comma", + style = "verbose", + applicability = "machine-applicable", + code = "" + )] pub comma: Span, } #[derive(Diagnostic)] -#[diag(parse_eq_field_init)] +#[diag("expected `:`, found `=`")] pub(crate) struct EqFieldInit { #[primary_span] pub span: Span, - #[suggestion(applicability = "machine-applicable", code = ":", style = "verbose")] + #[suggestion( + "replace equals symbol with a colon", + applicability = "machine-applicable", + code = ":", + style = "verbose" + )] pub eq: Span, } #[derive(Diagnostic)] -#[diag(parse_dotdotdot)] +#[diag("unexpected token: `...`")] pub(crate) struct DotDotDot { #[primary_span] #[suggestion( - parse_suggest_exclusive_range, + "use `..` for an exclusive range", applicability = "maybe-incorrect", code = "..", style = "verbose" )] #[suggestion( - parse_suggest_inclusive_range, + "or `..=` for an inclusive range", applicability = "maybe-incorrect", code = "..=", style = "verbose" @@ -690,67 +817,74 @@ pub(crate) struct DotDotDot { } #[derive(Diagnostic)] -#[diag(parse_left_arrow_operator)] +#[diag("unexpected token: `<-`")] pub(crate) struct LeftArrowOperator { #[primary_span] - #[suggestion(applicability = "maybe-incorrect", code = "< -", style = "verbose")] + #[suggestion( + "if you meant to write a comparison against a negative value, add a space in between `<` and `-`", + applicability = "maybe-incorrect", + code = "< -", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_remove_let)] +#[diag("expected pattern, found `let`")] pub(crate) struct RemoveLet { #[primary_span] pub span: Span, - #[suggestion(applicability = "machine-applicable", code = "", style = "verbose")] + #[suggestion( + "remove the unnecessary `let` keyword", + applicability = "machine-applicable", + code = "", + style = "verbose" + )] pub suggestion: Span, } #[derive(Diagnostic)] -#[diag(parse_use_eq_instead)] +#[diag("unexpected `==`")] pub(crate) struct UseEqInstead { #[primary_span] - #[suggestion(style = "verbose", applicability = "machine-applicable", code = "=")] + #[suggestion( + "try using `=` instead", + style = "verbose", + applicability = "machine-applicable", + code = "=" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_use_empty_block_not_semi)] +#[diag("expected { \"`{}`\" }, found `;`")] pub(crate) struct UseEmptyBlockNotSemi { #[primary_span] - #[suggestion(style = "hidden", applicability = "machine-applicable", code = "{{}}")] + #[suggestion( + r#"try using { "`{}`" } instead"#, + style = "hidden", + applicability = "machine-applicable", + code = "{{}}" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_comparison_interpreted_as_generic)] +#[diag("`<` is interpreted as a start of generic arguments for `{$type}`, not a comparison")] pub(crate) struct ComparisonInterpretedAsGeneric { #[primary_span] - #[label(parse_label_comparison)] + #[label("not interpreted as comparison")] pub comparison: Span, pub r#type: Path, - #[label(parse_label_args)] + #[label("interpreted as generic arguments")] pub args: Span, #[subdiagnostic] - pub suggestion: ComparisonOrShiftInterpretedAsGenericSugg, -} - -#[derive(Diagnostic)] -#[diag(parse_shift_interpreted_as_generic)] -pub(crate) struct ShiftInterpretedAsGeneric { - #[primary_span] - #[label(parse_label_comparison)] - pub shift: Span, - pub r#type: Path, - #[label(parse_label_args)] - pub args: Span, - #[subdiagnostic] - pub suggestion: ComparisonOrShiftInterpretedAsGenericSugg, + pub suggestion: ComparisonInterpretedAsGenericSugg, } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] -pub(crate) struct ComparisonOrShiftInterpretedAsGenericSugg { +#[multipart_suggestion("try comparing the cast value", applicability = "machine-applicable")] +pub(crate) struct ComparisonInterpretedAsGenericSugg { #[suggestion_part(code = "(")] pub left: Span, #[suggestion_part(code = ")")] @@ -758,10 +892,32 @@ pub(crate) struct ComparisonOrShiftInterpretedAsGenericSugg { } #[derive(Diagnostic)] -#[diag(parse_found_expr_would_be_stmt)] +#[diag("`<<` is interpreted as a start of generic arguments for `{$type}`, not a shift")] +pub(crate) struct ShiftInterpretedAsGeneric { + #[primary_span] + #[label("not interpreted as shift")] + pub shift: Span, + pub r#type: Path, + #[label("interpreted as generic arguments")] + pub args: Span, + #[subdiagnostic] + pub suggestion: ShiftInterpretedAsGenericSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("try shifting the cast value", applicability = "machine-applicable")] +pub(crate) struct ShiftInterpretedAsGenericSugg { + #[suggestion_part(code = "(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} + +#[derive(Diagnostic)] +#[diag("expected expression, found `{$token}`")] pub(crate) struct FoundExprWouldBeStmt { #[primary_span] - #[label] + #[label("expected expression")] pub span: Span, pub token: Token, #[subdiagnostic] @@ -769,81 +925,83 @@ pub(crate) struct FoundExprWouldBeStmt { } #[derive(Diagnostic)] -#[diag(parse_frontmatter_extra_characters_after_close)] +#[diag("extra characters after frontmatter close are not allowed")] pub(crate) struct FrontmatterExtraCharactersAfterClose { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_frontmatter_invalid_infostring)] -#[note] +#[diag("invalid infostring for frontmatter")] +#[note("frontmatter infostrings must be a single identifier immediately following the opening")] pub(crate) struct FrontmatterInvalidInfostring { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_frontmatter_invalid_opening_preceding_whitespace)] +#[diag("invalid preceding whitespace for frontmatter opening")] pub(crate) struct FrontmatterInvalidOpeningPrecedingWhitespace { #[primary_span] pub span: Span, - #[note] + #[note("frontmatter opening should not be preceded by whitespace")] pub note_span: Span, } #[derive(Diagnostic)] -#[diag(parse_frontmatter_unclosed)] +#[diag("unclosed frontmatter")] pub(crate) struct FrontmatterUnclosed { #[primary_span] pub span: Span, - #[note] + #[note("frontmatter opening here was not closed")] pub note_span: Span, } #[derive(Diagnostic)] -#[diag(parse_frontmatter_invalid_close_preceding_whitespace)] +#[diag("invalid preceding whitespace for frontmatter close")] pub(crate) struct FrontmatterInvalidClosingPrecedingWhitespace { #[primary_span] pub span: Span, - #[note] + #[note("frontmatter close should not be preceded by whitespace")] pub note_span: Span, } #[derive(Diagnostic)] -#[diag(parse_frontmatter_length_mismatch)] +#[diag("frontmatter close does not match the opening")] pub(crate) struct FrontmatterLengthMismatch { #[primary_span] pub span: Span, - #[label(parse_label_opening)] + #[label("the opening here has {$len_opening} dashes...")] pub opening: Span, - #[label(parse_label_close)] + #[label("...while the close has {$len_close} dashes")] pub close: Span, pub len_opening: usize, pub len_close: usize, } #[derive(Diagnostic)] -#[diag(parse_frontmatter_too_many_dashes)] +#[diag( + "too many `-` symbols: frontmatter openings may be delimited by up to 255 `-` symbols, but found {$len_opening}" +)] pub(crate) struct FrontmatterTooManyDashes { pub len_opening: usize, } #[derive(Diagnostic)] -#[diag(parse_bare_cr_in_frontmatter)] +#[diag("bare CR not allowed in frontmatter")] pub(crate) struct BareCrFrontmatter { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_leading_plus_not_supported)] +#[diag("leading `+` is not supported")] pub(crate) struct LeadingPlusNotSupported { #[primary_span] - #[label] + #[label("unexpected `+`")] pub span: Span, #[suggestion( - parse_suggestion_remove_plus, + "try removing the `+`", style = "verbose", code = "", applicability = "machine-applicable" @@ -854,7 +1012,7 @@ pub(crate) struct LeadingPlusNotSupported { } #[derive(Diagnostic)] -#[diag(parse_parentheses_with_struct_fields)] +#[diag("invalid `struct` delimiters or `fn` call arguments")] pub(crate) struct ParenthesesWithStructFields { #[primary_span] pub span: Span, @@ -866,7 +1024,10 @@ pub(crate) struct ParenthesesWithStructFields { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion_braces_for_struct, applicability = "maybe-incorrect")] +#[multipart_suggestion( + "if `{$type}` is a struct, use braces as delimiters", + applicability = "maybe-incorrect" +)] pub(crate) struct BracesForStructLiteral { #[suggestion_part(code = " {{ ")] pub first: Span, @@ -875,14 +1036,19 @@ pub(crate) struct BracesForStructLiteral { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion_no_fields_for_fn, applicability = "maybe-incorrect")] +#[multipart_suggestion( + "if `{$type}` is a function, use the arguments directly", + applicability = "maybe-incorrect" +)] pub(crate) struct NoFieldsForFnCall { #[suggestion_part(code = "")] pub fields: Vec, } #[derive(Diagnostic)] -#[diag(parse_labeled_loop_in_break)] +#[diag( + "parentheses are required around this expression to avoid confusion with a labeled break expression" +)] pub(crate) struct LabeledLoopInBreak { #[primary_span] pub span: Span, @@ -893,7 +1059,7 @@ pub(crate) struct LabeledLoopInBreak { #[derive(Subdiagnostic)] pub(crate) enum WrapInParentheses { #[multipart_suggestion( - parse_sugg_wrap_expression_in_parentheses, + "wrap the expression in parentheses", applicability = "machine-applicable" )] Expression { @@ -903,7 +1069,7 @@ pub(crate) enum WrapInParentheses { right: Span, }, #[multipart_suggestion( - parse_sugg_wrap_macro_in_parentheses, + "use parentheses instead of braces for this macro", applicability = "machine-applicable" )] MacroArgs { @@ -915,7 +1081,7 @@ pub(crate) enum WrapInParentheses { } #[derive(Diagnostic)] -#[diag(parse_array_brackets_instead_of_braces)] +#[diag("this is a block expression, not an array")] pub(crate) struct ArrayBracketsInsteadOfBraces { #[primary_span] pub span: Span, @@ -924,7 +1090,10 @@ pub(crate) struct ArrayBracketsInsteadOfBraces { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")] +#[multipart_suggestion( + "to make an array, use square brackets instead of curly braces", + applicability = "maybe-incorrect" +)] pub(crate) struct ArrayBracketsInsteadOfBracesSugg { #[suggestion_part(code = "[")] pub left: Span, @@ -933,12 +1102,17 @@ pub(crate) struct ArrayBracketsInsteadOfBracesSugg { } #[derive(Diagnostic)] -#[diag(parse_match_arm_body_without_braces)] +#[diag("`match` arm body without braces")] pub(crate) struct MatchArmBodyWithoutBraces { #[primary_span] - #[label(parse_label_statements)] + #[label( + "{$num_statements -> + [one] this statement is not surrounded by a body + *[other] these statements are not surrounded by a body + }" + )] pub statements: Span, - #[label(parse_label_arrow)] + #[label("while parsing the `match` arm starting here")] pub arrow: Span, pub num_statements: usize, #[subdiagnostic] @@ -946,12 +1120,12 @@ pub(crate) struct MatchArmBodyWithoutBraces { } #[derive(Diagnostic)] -#[diag(parse_inclusive_range_extra_equals)] -#[note] +#[diag("unexpected `=` after inclusive range")] +#[note("inclusive ranges end with a single equals sign (`..=`)")] pub(crate) struct InclusiveRangeExtraEquals { #[primary_span] #[suggestion( - parse_suggestion_remove_eq, + "use `..=` instead", style = "verbose", code = "..=", applicability = "maybe-incorrect" @@ -960,24 +1134,29 @@ pub(crate) struct InclusiveRangeExtraEquals { } #[derive(Diagnostic)] -#[diag(parse_inclusive_range_match_arrow)] +#[diag("unexpected `>` after inclusive range")] pub(crate) struct InclusiveRangeMatchArrow { #[primary_span] pub arrow: Span, - #[label] + #[label("this is parsed as an inclusive range `..=`")] pub span: Span, - #[suggestion(style = "verbose", code = " ", applicability = "machine-applicable")] + #[suggestion( + "add a space between the pattern and `=>`", + style = "verbose", + code = " ", + applicability = "machine-applicable" + )] pub after_pat: Span, } #[derive(Diagnostic)] -#[diag(parse_inclusive_range_no_end, code = E0586)] -#[note] +#[diag("inclusive range with no end", code = E0586)] +#[note("inclusive ranges must be bounded at the end (`..=b` or `a..=b`)")] pub(crate) struct InclusiveRangeNoEnd { #[primary_span] pub span: Span, #[suggestion( - parse_suggestion_open_range, + "use `..` instead", code = "", applicability = "machine-applicable", style = "verbose" @@ -987,7 +1166,13 @@ pub(crate) struct InclusiveRangeNoEnd { #[derive(Subdiagnostic)] pub(crate) enum MatchArmBodyWithoutBracesSugg { - #[multipart_suggestion(parse_suggestion_add_braces, applicability = "machine-applicable")] + #[multipart_suggestion( + "surround the {$num_statements -> + [one] statement + *[other] statements + } with a body", + applicability = "machine-applicable" + )] AddBraces { #[suggestion_part(code = "{{ ")] left: Span, @@ -995,7 +1180,7 @@ pub(crate) enum MatchArmBodyWithoutBracesSugg { right: Span, }, #[suggestion( - parse_suggestion_use_comma_not_semicolon, + "replace `;` with `,` to end a `match` arm expression", code = ",", applicability = "machine-applicable", style = "verbose" @@ -1007,7 +1192,7 @@ pub(crate) enum MatchArmBodyWithoutBracesSugg { } #[derive(Diagnostic)] -#[diag(parse_struct_literal_not_allowed_here)] +#[diag("struct literals are not allowed here")] pub(crate) struct StructLiteralNotAllowedHere { #[primary_span] pub span: Span, @@ -1016,7 +1201,10 @@ pub(crate) struct StructLiteralNotAllowedHere { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + "surround the struct literal with parentheses", + applicability = "machine-applicable" +)] pub(crate) struct StructLiteralNotAllowedHereSugg { #[suggestion_part(code = "(")] pub left: Span, @@ -1025,80 +1213,105 @@ pub(crate) struct StructLiteralNotAllowedHereSugg { } #[derive(Diagnostic)] -#[diag(parse_invalid_literal_suffix_on_tuple_index)] +#[diag("suffixes on a tuple index are invalid")] pub(crate) struct InvalidLiteralSuffixOnTupleIndex { #[primary_span] - #[label] + #[label("invalid suffix `{$suffix}`")] pub span: Span, pub suffix: Symbol, } #[derive(Diagnostic)] -#[diag(parse_non_string_abi_literal)] +#[diag("non-string ABI literal")] pub(crate) struct NonStringAbiLiteral { #[primary_span] - #[suggestion(code = "\"C\"", applicability = "maybe-incorrect", style = "verbose")] + #[suggestion( + "specify the ABI with a string literal", + code = "\"C\"", + applicability = "maybe-incorrect", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_mismatched_closing_delimiter)] +#[diag("mismatched closing delimiter: `{$delimiter}`")] pub(crate) struct MismatchedClosingDelimiter { #[primary_span] pub spans: Vec, pub delimiter: String, - #[label(parse_label_unmatched)] + #[label("mismatched closing delimiter")] pub unmatched: Span, - #[label(parse_label_opening_candidate)] + #[label("closing delimiter possibly meant for this")] pub opening_candidate: Option, - #[label(parse_label_unclosed)] + #[label("unclosed delimiter")] pub unclosed: Option, } #[derive(Diagnostic)] -#[diag(parse_incorrect_visibility_restriction, code = E0704)] -#[help] +#[diag("incorrect visibility restriction", code = E0704)] +#[help( + "some possible visibility restrictions are: + `pub(crate)`: visible only on the current crate + `pub(super)`: visible only in the current module's parent + `pub(in path::to::module)`: visible only on the specified path" +)] pub(crate) struct IncorrectVisibilityRestriction { #[primary_span] - #[suggestion(code = "in {inner_str}", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "make this visible only to module `{$inner_str}` with `in`", + code = "in {inner_str}", + applicability = "machine-applicable", + style = "verbose" + )] pub span: Span, pub inner_str: String, } #[derive(Diagnostic)] -#[diag(parse_assignment_else_not_allowed)] +#[diag(" ... else {\"{\"} ... {\"}\"} is not allowed")] pub(crate) struct AssignmentElseNotAllowed { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_expected_statement_after_outer_attr)] +#[diag("expected statement after outer attribute")] pub(crate) struct ExpectedStatementAfterOuterAttr { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_doc_comment_does_not_document_anything, code = E0585)] -#[help] +#[diag("found a documentation comment that doesn't document anything", code = E0585)] +#[help("doc comments must come before what they document, if a comment was intended use `//`")] pub(crate) struct DocCommentDoesNotDocumentAnything { #[primary_span] pub span: Span, - #[suggestion(code = ",", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "missing comma here", + code = ",", + applicability = "machine-applicable", + style = "verbose" + )] pub missing_comma: Option, } #[derive(Diagnostic)] -#[diag(parse_const_let_mutually_exclusive)] +#[diag("`const` and `let` are mutually exclusive")] pub(crate) struct ConstLetMutuallyExclusive { #[primary_span] - #[suggestion(code = "const", applicability = "maybe-incorrect", style = "verbose")] + #[suggestion( + "remove `let`", + code = "const", + applicability = "maybe-incorrect", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_invalid_expression_in_let_else)] +#[diag("a `{$operator}` expression cannot be directly assigned in `let...else`")] pub(crate) struct InvalidExpressionInLetElse { #[primary_span] pub span: Span, @@ -1108,7 +1321,7 @@ pub(crate) struct InvalidExpressionInLetElse { } #[derive(Diagnostic)] -#[diag(parse_invalid_curly_in_let_else)] +#[diag("right curly brace `{\"}\"}` before `else` in a `let...else` statement not allowed")] pub(crate) struct InvalidCurlyInLetElse { #[primary_span] pub span: Span, @@ -1117,25 +1330,32 @@ pub(crate) struct InvalidCurlyInLetElse { } #[derive(Diagnostic)] -#[diag(parse_compound_assignment_expression_in_let)] -#[help] +#[diag("can't reassign to an uninitialized variable")] +#[help("if you meant to overwrite, remove the `let` binding")] pub(crate) struct CompoundAssignmentExpressionInLet { #[primary_span] pub span: Span, - #[suggestion(style = "verbose", code = "", applicability = "maybe-incorrect")] + #[suggestion( + "initialize the variable", + style = "verbose", + code = "", + applicability = "maybe-incorrect" + )] pub suggestion: Span, } #[derive(Diagnostic)] -#[diag(parse_suffixed_literal_in_attribute)] -#[help] +#[diag("suffixed literals are not allowed in attributes")] +#[help( + "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)" +)] pub(crate) struct SuffixedLiteralInAttribute { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_invalid_meta_item)] +#[diag("expected unsuffixed literal, found {$descr}")] pub(crate) struct InvalidMetaItem { #[primary_span] pub span: Span, @@ -1145,7 +1365,10 @@ pub(crate) struct InvalidMetaItem { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_quote_ident_sugg, applicability = "machine-applicable")] +#[multipart_suggestion( + "surround the identifier with quotation marks to make it into a string literal", + applicability = "machine-applicable" +)] pub(crate) struct InvalidMetaItemQuoteIdentSugg { #[suggestion_part(code = "\"")] pub before: Span, @@ -1155,7 +1378,7 @@ pub(crate) struct InvalidMetaItemQuoteIdentSugg { #[derive(Subdiagnostic)] #[suggestion( - parse_sugg_escape_identifier, + "escape `{$ident_name}` to use it as an identifier", style = "verbose", applicability = "maybe-incorrect", code = "r#" @@ -1168,7 +1391,7 @@ pub(crate) struct SuggEscapeIdentifier { #[derive(Subdiagnostic)] #[suggestion( - parse_sugg_remove_comma, + "remove this comma", applicability = "machine-applicable", code = "", style = "verbose" @@ -1180,7 +1403,7 @@ pub(crate) struct SuggRemoveComma { #[derive(Subdiagnostic)] #[suggestion( - parse_sugg_add_let_for_stmt, + "you might have meant to introduce a new binding", style = "verbose", applicability = "maybe-incorrect", code = "let " @@ -1192,17 +1415,17 @@ pub(crate) struct SuggAddMissingLetStmt { #[derive(Subdiagnostic)] pub(crate) enum ExpectedIdentifierFound { - #[label(parse_expected_identifier_found_reserved_identifier)] + #[label("expected identifier, found reserved identifier")] ReservedIdentifier(#[primary_span] Span), - #[label(parse_expected_identifier_found_keyword)] + #[label("expected identifier, found keyword")] Keyword(#[primary_span] Span), - #[label(parse_expected_identifier_found_reserved_keyword)] + #[label("expected identifier, found reserved keyword")] ReservedKeyword(#[primary_span] Span), - #[label(parse_expected_identifier_found_doc_comment)] + #[label("expected identifier, found doc comment")] DocComment(#[primary_span] Span), - #[label(parse_expected_identifier_found_metavar)] + #[label("expected identifier, found metavariable")] MetaVar(#[primary_span] Span), - #[label(parse_expected_identifier)] + #[label("expected identifier")] Other(#[primary_span] Span), } @@ -1240,22 +1463,22 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedIdentifier { level, match token_descr { Some(TokenDescription::ReservedIdentifier) => { - fluent::parse_expected_identifier_found_reserved_identifier_str + inline_fluent!("expected identifier, found reserved identifier `{$token}`") } Some(TokenDescription::Keyword) => { - fluent::parse_expected_identifier_found_keyword_str + inline_fluent!("expected identifier, found keyword `{$token}`") } Some(TokenDescription::ReservedKeyword) => { - fluent::parse_expected_identifier_found_reserved_keyword_str + inline_fluent!("expected identifier, found reserved keyword `{$token}`") } Some(TokenDescription::DocComment) => { - fluent::parse_expected_identifier_found_doc_comment_str + inline_fluent!("expected identifier, found doc comment `{$token}`") } Some(TokenDescription::MetaVar(_)) => { add_token = false; - fluent::parse_expected_identifier_found_metavar_str + inline_fluent!("expected identifier, found metavariable") } - None => fluent::parse_expected_identifier_found_str, + None => inline_fluent!("expected identifier, found `{$token}`"), }, ); diag.span(self.span); @@ -1282,7 +1505,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedIdentifier { } #[derive(Subdiagnostic)] -#[help(parse_invalid_identifier_with_leading_number)] +#[help("identifiers cannot start with a number")] pub(crate) struct HelpIdentifierStartsWithNumber { #[primary_span] pub num_span: Span, @@ -1307,20 +1530,22 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedSemi { level, match token_descr { Some(TokenDescription::ReservedIdentifier) => { - fluent::parse_expected_semi_found_reserved_identifier_str + inline_fluent!("expected `;`, found reserved identifier `{$token}`") + } + Some(TokenDescription::Keyword) => { + inline_fluent!("expected `;`, found keyword `{$token}`") } - Some(TokenDescription::Keyword) => fluent::parse_expected_semi_found_keyword_str, Some(TokenDescription::ReservedKeyword) => { - fluent::parse_expected_semi_found_reserved_keyword_str + inline_fluent!("expected `;`, found reserved keyword `{$token}`") } Some(TokenDescription::DocComment) => { - fluent::parse_expected_semi_found_doc_comment_str + inline_fluent!("expected `;`, found doc comment `{$token}`") } Some(TokenDescription::MetaVar(_)) => { add_token = false; - fluent::parse_expected_semi_found_metavar_str + inline_fluent!("expected `;`, found metavariable") } - None => fluent::parse_expected_semi_found_str, + None => inline_fluent!("expected `;`, found `{$token}`"), }, ); diag.span(self.span); @@ -1329,7 +1554,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedSemi { } if let Some(unexpected_token_label) = self.unexpected_token_label { - diag.span_label(unexpected_token_label, fluent::parse_label_unexpected_token); + diag.span_label(unexpected_token_label, inline_fluent!("unexpected token")); } self.sugg.add_to_diag(&mut diag); @@ -1341,23 +1566,18 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedSemi { #[derive(Subdiagnostic)] pub(crate) enum ExpectedSemiSugg { #[suggestion( - parse_sugg_change_this_to_semi, + "change this to `;`", code = ";", applicability = "machine-applicable", style = "short" )] ChangeToSemi(#[primary_span] Span), - #[suggestion( - parse_sugg_add_semi, - code = ";", - applicability = "machine-applicable", - style = "short" - )] + #[suggestion("add `;` here", code = ";", applicability = "machine-applicable", style = "short")] AddSemi(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(parse_struct_literal_body_without_path)] +#[diag("struct literal body without path")] pub(crate) struct StructLiteralBodyWithoutPath { #[primary_span] pub span: Span, @@ -1366,7 +1586,10 @@ pub(crate) struct StructLiteralBodyWithoutPath { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "has-placeholders")] +#[multipart_suggestion( + "you might have forgotten to add the struct literal inside the block", + applicability = "has-placeholders" +)] pub(crate) struct StructLiteralBodyWithoutPathSugg { #[suggestion_part(code = "{{ SomeStruct ")] pub before: Span, @@ -1375,16 +1598,29 @@ pub(crate) struct StructLiteralBodyWithoutPathSugg { } #[derive(Diagnostic)] -#[diag(parse_unmatched_angle_brackets)] +#[diag( + "{$num_extra_brackets -> + [one] unmatched angle bracket + *[other] unmatched angle brackets + }" +)] pub(crate) struct UnmatchedAngleBrackets { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "{$num_extra_brackets -> + [one] remove extra angle bracket + *[other] remove extra angle brackets + }", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] pub span: Span, pub num_extra_brackets: usize, } #[derive(Diagnostic)] -#[diag(parse_generic_parameters_without_angle_brackets)] +#[diag("generic parameters without surrounding angle brackets")] pub(crate) struct GenericParamsWithoutAngleBrackets { #[primary_span] pub span: Span, @@ -1393,7 +1629,10 @@ pub(crate) struct GenericParamsWithoutAngleBrackets { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + "surround the type parameters with angle brackets", + applicability = "machine-applicable" +)] pub(crate) struct GenericParamsWithoutAngleBracketsSugg { #[suggestion_part(code = "<")] pub left: Span, @@ -1402,19 +1641,19 @@ pub(crate) struct GenericParamsWithoutAngleBracketsSugg { } #[derive(Diagnostic)] -#[diag(parse_comparison_operators_cannot_be_chained)] +#[diag("comparison operators cannot be chained")] pub(crate) struct ComparisonOperatorsCannotBeChained { #[primary_span] pub span: Vec, #[suggestion( - parse_sugg_turbofish_syntax, + "use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments", style = "verbose", code = "::", applicability = "maybe-incorrect" )] pub suggest_turbofish: Option, - #[help(parse_sugg_turbofish_syntax)] - #[help(parse_sugg_parentheses_for_function_args)] + #[help("use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments")] + #[help("or use `(...)` if you meant to specify fn arguments")] pub help_turbofish: bool, #[subdiagnostic] pub chaining_sugg: Option, @@ -1423,7 +1662,7 @@ pub(crate) struct ComparisonOperatorsCannotBeChained { #[derive(Subdiagnostic)] pub(crate) enum ComparisonOperatorsCannotBeChainedSugg { #[suggestion( - parse_sugg_split_comparison, + "split the comparison into two", style = "verbose", code = " && {middle_term}", applicability = "maybe-incorrect" @@ -1433,7 +1672,7 @@ pub(crate) enum ComparisonOperatorsCannotBeChainedSugg { span: Span, middle_term: String, }, - #[multipart_suggestion(parse_sugg_parenthesize, applicability = "maybe-incorrect")] + #[multipart_suggestion("parenthesize the comparison", applicability = "maybe-incorrect")] Parenthesize { #[suggestion_part(code = "(")] left: Span, @@ -1443,17 +1682,20 @@ pub(crate) enum ComparisonOperatorsCannotBeChainedSugg { } #[derive(Diagnostic)] -#[diag(parse_question_mark_in_type)] +#[diag("invalid `?` in type")] pub(crate) struct QuestionMarkInType { #[primary_span] - #[label] + #[label("`?` is only allowed on expressions, not types")] pub span: Span, #[subdiagnostic] pub sugg: QuestionMarkInTypeSugg, } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + "if you meant to express that the type might not contain a value, use the `Option` wrapper type", + applicability = "machine-applicable" +)] pub(crate) struct QuestionMarkInTypeSugg { #[suggestion_part(code = "Option<")] pub left: Span, @@ -1462,7 +1704,7 @@ pub(crate) struct QuestionMarkInTypeSugg { } #[derive(Diagnostic)] -#[diag(parse_unexpected_parentheses_in_for_head)] +#[diag("unexpected parentheses surrounding `for` loop head")] pub(crate) struct ParenthesesInForHead { #[primary_span] pub span: Vec, @@ -1471,7 +1713,7 @@ pub(crate) struct ParenthesesInForHead { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion("remove parentheses in `for` loop", applicability = "machine-applicable")] pub(crate) struct ParenthesesInForHeadSugg { #[suggestion_part(code = " ")] pub left: Span, @@ -1480,7 +1722,7 @@ pub(crate) struct ParenthesesInForHeadSugg { } #[derive(Diagnostic)] -#[diag(parse_unexpected_parentheses_in_match_arm_pattern)] +#[diag("unexpected parentheses surrounding `match` arm pattern")] pub(crate) struct ParenthesesInMatchPat { #[primary_span] pub span: Vec, @@ -1489,7 +1731,10 @@ pub(crate) struct ParenthesesInMatchPat { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + "remove parentheses surrounding the pattern", + applicability = "machine-applicable" +)] pub(crate) struct ParenthesesInMatchPatSugg { #[suggestion_part(code = "")] pub left: Span, @@ -1498,67 +1743,82 @@ pub(crate) struct ParenthesesInMatchPatSugg { } #[derive(Diagnostic)] -#[diag(parse_doc_comment_on_param_type)] +#[diag("documentation comments cannot be applied to a function parameter's type")] pub(crate) struct DocCommentOnParamType { #[primary_span] - #[label] + #[label("doc comments are not allowed here")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_attribute_on_param_type)] +#[diag("attributes cannot be applied to a function parameter's type")] pub(crate) struct AttributeOnParamType { #[primary_span] - #[label] + #[label("attributes are not allowed here")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_attribute_on_type)] +#[diag("attributes cannot be applied to types")] pub(crate) struct AttributeOnType { #[primary_span] - #[label] + #[label("attributes are not allowed here")] pub span: Span, - #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] + #[suggestion( + "remove attribute from here", + code = "", + applicability = "machine-applicable", + style = "tool-only" + )] pub fix_span: Span, } #[derive(Diagnostic)] -#[diag(parse_attribute_on_generic_arg)] +#[diag("attributes cannot be applied to generic arguments")] pub(crate) struct AttributeOnGenericArg { #[primary_span] - #[label] + #[label("attributes are not allowed here")] pub span: Span, - #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] + #[suggestion( + "remove attribute from here", + code = "", + applicability = "machine-applicable", + style = "tool-only" + )] pub fix_span: Span, } #[derive(Diagnostic)] -#[diag(parse_attribute_on_empty_type)] +#[diag("attributes cannot be applied here")] pub(crate) struct AttributeOnEmptyType { #[primary_span] - #[label] + #[label("attributes are not allowed here")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_pattern_method_param_without_body, code = E0642)] +#[diag("patterns aren't allowed in methods without bodies", code = E0642)] pub(crate) struct PatternMethodParamWithoutBody { #[primary_span] - #[suggestion(code = "_", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "give this argument a name or use an underscore to ignore it", + code = "_", + applicability = "machine-applicable", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_self_param_not_first)] +#[diag("unexpected `self` parameter in function")] pub(crate) struct SelfParamNotFirst { #[primary_span] - #[label] + #[label("must be the first parameter of an associated function")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_const_generic_without_braces)] +#[diag("expressions must be enclosed in braces to be used as const generic arguments")] pub(crate) struct ConstGenericWithoutBraces { #[primary_span] pub span: Span, @@ -1567,7 +1827,10 @@ pub(crate) struct ConstGenericWithoutBraces { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + "enclose the `const` expression in braces", + applicability = "machine-applicable" +)] pub(crate) struct ConstGenericWithoutBracesSugg { #[suggestion_part(code = "{{ ")] pub left: Span, @@ -1576,10 +1839,10 @@ pub(crate) struct ConstGenericWithoutBracesSugg { } #[derive(Diagnostic)] -#[diag(parse_unexpected_const_param_declaration)] +#[diag("unexpected `const` parameter declaration")] pub(crate) struct UnexpectedConstParamDeclaration { #[primary_span] - #[label] + #[label("expected a `const` expression, not a parameter declaration")] pub span: Span, #[subdiagnostic] pub sugg: Option, @@ -1587,7 +1850,10 @@ pub(crate) struct UnexpectedConstParamDeclaration { #[derive(Subdiagnostic)] pub(crate) enum UnexpectedConstParamDeclarationSugg { - #[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] + #[multipart_suggestion( + "`const` parameters must be declared for the `impl`", + applicability = "machine-applicable" + )] AddParam { #[suggestion_part(code = "<{snippet}>")] impl_generics: Span, @@ -1596,7 +1862,10 @@ pub(crate) enum UnexpectedConstParamDeclarationSugg { snippet: String, ident: String, }, - #[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] + #[multipart_suggestion( + "`const` parameters must be declared for the `impl`", + applicability = "machine-applicable" + )] AppendParam { #[suggestion_part(code = ", {snippet}")] impl_generics_end: Span, @@ -1608,41 +1877,61 @@ pub(crate) enum UnexpectedConstParamDeclarationSugg { } #[derive(Diagnostic)] -#[diag(parse_unexpected_const_in_generic_param)] +#[diag("expected lifetime, type, or constant, found keyword `const`")] pub(crate) struct UnexpectedConstInGenericParam { #[primary_span] pub span: Span, - #[suggestion(style = "verbose", code = "", applicability = "maybe-incorrect")] + #[suggestion( + "the `const` keyword is only needed in the definition of the type", + style = "verbose", + code = "", + applicability = "maybe-incorrect" + )] pub to_remove: Option, } #[derive(Diagnostic)] -#[diag(parse_async_move_order_incorrect)] +#[diag("the order of `move` and `async` is incorrect")] pub(crate) struct AsyncMoveOrderIncorrect { #[primary_span] - #[suggestion(style = "verbose", code = "async move", applicability = "maybe-incorrect")] + #[suggestion( + "try switching the order", + style = "verbose", + code = "async move", + applicability = "maybe-incorrect" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_async_use_order_incorrect)] +#[diag("the order of `use` and `async` is incorrect")] pub(crate) struct AsyncUseOrderIncorrect { #[primary_span] - #[suggestion(style = "verbose", code = "async use", applicability = "maybe-incorrect")] + #[suggestion( + "try switching the order", + style = "verbose", + code = "async use", + applicability = "maybe-incorrect" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_double_colon_in_bound)] +#[diag("expected `:` followed by trait or lifetime")] pub(crate) struct DoubleColonInBound { #[primary_span] pub span: Span, - #[suggestion(code = ": ", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "use single colon", + code = ": ", + applicability = "machine-applicable", + style = "verbose" + )] pub between: Span, } #[derive(Diagnostic)] -#[diag(parse_fn_ptr_with_generics)] +#[diag("function pointer types may not have generic parameters")] pub(crate) struct FnPtrWithGenerics { #[primary_span] pub span: Span, @@ -1652,7 +1941,7 @@ pub(crate) struct FnPtrWithGenerics { #[derive(Subdiagnostic)] #[multipart_suggestion( - parse_misplaced_return_type, + "place the return type after the function parameters", style = "verbose", applicability = "maybe-incorrect" )] @@ -1665,7 +1954,16 @@ pub(crate) struct MisplacedReturnType { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")] +#[multipart_suggestion( + "consider moving the lifetime {$arity -> + [one] parameter + *[other] parameters + } to {$for_param_list_exists -> + [true] the + *[false] a + } `for` parameter list", + applicability = "maybe-incorrect" +)] pub(crate) struct FnPtrWithGenericsSugg { #[suggestion_part(code = "{snippet}")] pub left: Span, @@ -1682,10 +1980,10 @@ pub(crate) struct FnTraitMissingParen { impl Subdiagnostic for FnTraitMissingParen { fn add_to_diag(self, diag: &mut Diag<'_, G>) { - diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren); + diag.span_label(self.span, inline_fluent!("`Fn` bounds require arguments in parentheses")); diag.span_suggestion_short( self.span.shrink_to_hi(), - crate::fluent_generated::parse_add_paren, + inline_fluent!("try adding parentheses"), "()", Applicability::MachineApplicable, ); @@ -1693,95 +1991,128 @@ impl Subdiagnostic for FnTraitMissingParen { } #[derive(Diagnostic)] -#[diag(parse_unexpected_if_with_if)] +#[diag("unexpected `if` in the condition expression")] pub(crate) struct UnexpectedIfWithIf( #[primary_span] - #[suggestion(applicability = "machine-applicable", code = " ", style = "verbose")] + #[suggestion( + "remove the `if`", + applicability = "machine-applicable", + code = " ", + style = "verbose" + )] pub Span, ); #[derive(Diagnostic)] -#[diag(parse_maybe_fn_typo_with_impl)] +#[diag("you might have meant to write `impl` instead of `fn`")] pub(crate) struct FnTypoWithImpl { #[primary_span] - #[suggestion(applicability = "maybe-incorrect", code = "impl", style = "verbose")] + #[suggestion( + "replace `fn` with `impl` here", + applicability = "maybe-incorrect", + code = "impl", + style = "verbose" + )] pub fn_span: Span, } #[derive(Diagnostic)] -#[diag(parse_expected_fn_path_found_fn_keyword)] +#[diag("expected identifier, found keyword `fn`")] pub(crate) struct ExpectedFnPathFoundFnKeyword { #[primary_span] - #[suggestion(applicability = "machine-applicable", code = "Fn", style = "verbose")] + #[suggestion( + "use `Fn` to refer to the trait", + applicability = "machine-applicable", + code = "Fn", + style = "verbose" + )] pub fn_token_span: Span, } #[derive(Diagnostic)] -#[diag(parse_path_found_named_params)] +#[diag("`Trait(...)` syntax does not support named parameters")] pub(crate) struct FnPathFoundNamedParams { #[primary_span] - #[suggestion(applicability = "machine-applicable", code = "")] + #[suggestion("remove the parameter name", applicability = "machine-applicable", code = "")] pub named_param_span: Span, } #[derive(Diagnostic)] -#[diag(parse_path_found_c_variadic_params)] +#[diag("`Trait(...)` syntax does not support c_variadic parameters")] pub(crate) struct PathFoundCVariadicParams { #[primary_span] - #[suggestion(applicability = "machine-applicable", code = "")] + #[suggestion("remove the `...`", applicability = "machine-applicable", code = "")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_path_found_attribute_in_params)] +#[diag("`Trait(...)` syntax does not support attributes in parameters")] pub(crate) struct PathFoundAttributeInParams { #[primary_span] - #[suggestion(applicability = "machine-applicable", code = "")] + #[suggestion("remove the attributes", applicability = "machine-applicable", code = "")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_path_double_colon)] +#[diag("path separator must be a double colon")] pub(crate) struct PathSingleColon { #[primary_span] pub span: Span, - #[suggestion(applicability = "machine-applicable", code = ":", style = "verbose")] + #[suggestion( + "use a double colon instead", + applicability = "machine-applicable", + code = ":", + style = "verbose" + )] pub suggestion: Span, } #[derive(Diagnostic)] -#[diag(parse_path_double_colon)] +#[diag("path separator must be a double colon")] pub(crate) struct PathTripleColon { #[primary_span] - #[suggestion(applicability = "maybe-incorrect", code = "", style = "verbose")] + #[suggestion( + "use a double colon instead", + applicability = "maybe-incorrect", + code = "", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_colon_as_semi)] +#[diag("statements are terminated with a semicolon")] pub(crate) struct ColonAsSemi { #[primary_span] - #[suggestion(applicability = "machine-applicable", code = ";", style = "verbose")] + #[suggestion( + "use a semicolon instead", + applicability = "machine-applicable", + code = ";", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_where_clause_before_tuple_struct_body)] +#[diag("where clauses are not allowed before tuple struct bodies")] pub(crate) struct WhereClauseBeforeTupleStructBody { #[primary_span] - #[label] + #[label("unexpected where clause")] pub span: Span, - #[label(parse_name_label)] + #[label("while parsing this tuple struct")] pub name: Span, - #[label(parse_body_label)] + #[label("the struct body")] pub body: Span, #[subdiagnostic] pub sugg: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + "move the body before the where clause", + applicability = "machine-applicable" +)] pub(crate) struct WhereClauseBeforeTupleStructBodySugg { #[suggestion_part(code = "{snippet}")] pub left: Span, @@ -1791,38 +2122,38 @@ pub(crate) struct WhereClauseBeforeTupleStructBodySugg { } #[derive(Diagnostic)] -#[diag(parse_async_fn_in_2015, code = E0670)] +#[diag("`async fn` is not permitted in Rust 2015", code = E0670)] pub(crate) struct AsyncFnIn2015 { #[primary_span] - #[label] + #[label("to use `async fn`, switch to Rust 2018 or later")] pub span: Span, #[subdiagnostic] pub help: HelpUseLatestEdition, } #[derive(Subdiagnostic)] -#[label(parse_async_block_in_2015)] +#[label("`async` blocks are only allowed in Rust 2018 or later")] pub(crate) struct AsyncBlockIn2015 { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_async_move_block_in_2015)] +#[diag("`async move` blocks are only allowed in Rust 2018 or later")] pub(crate) struct AsyncMoveBlockIn2015 { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_async_use_block_in_2015)] +#[diag("`async use` blocks are only allowed in Rust 2018 or later")] pub(crate) struct AsyncUseBlockIn2015 { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_async_bound_modifier_in_2015)] +#[diag("`async` trait bounds are only allowed in Rust 2018 or later")] pub(crate) struct AsyncBoundModifierIn2015 { #[primary_span] pub span: Span, @@ -1831,22 +2162,22 @@ pub(crate) struct AsyncBoundModifierIn2015 { } #[derive(Diagnostic)] -#[diag(parse_let_chain_pre_2024)] +#[diag("let chains are only allowed in Rust 2024 or later")] pub(crate) struct LetChainPre2024 { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_self_argument_pointer)] +#[diag("cannot pass `self` by raw pointer")] pub(crate) struct SelfArgumentPointer { #[primary_span] - #[label] + #[label("cannot pass `self` by raw pointer")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_unexpected_token_after_dot)] +#[diag("unexpected token: {$actual}")] pub(crate) struct UnexpectedTokenAfterDot { #[primary_span] pub span: Span, @@ -1854,64 +2185,84 @@ pub(crate) struct UnexpectedTokenAfterDot { } #[derive(Diagnostic)] -#[diag(parse_visibility_not_followed_by_item)] -#[help] +#[diag("visibility `{$vis}` is not followed by an item")] +#[help("you likely meant to define an item, e.g., `{$vis} fn foo() {\"{}\"}`")] pub(crate) struct VisibilityNotFollowedByItem { #[primary_span] - #[label] + #[label("the visibility")] pub span: Span, pub vis: Visibility, } #[derive(Diagnostic)] -#[diag(parse_default_not_followed_by_item)] -#[note] +#[diag("`default` is not followed by an item")] +#[note("only `fn`, `const`, `type`, or `impl` items may be prefixed by `default`")] pub(crate) struct DefaultNotFollowedByItem { #[primary_span] - #[label] + #[label("the `default` qualifier")] pub span: Span, } #[derive(Diagnostic)] pub(crate) enum MissingKeywordForItemDefinition { - #[diag(parse_missing_enum_for_enum_definition)] + #[diag("missing `enum` for enum definition")] Enum { #[primary_span] span: Span, - #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "enum ")] + #[suggestion( + "add `enum` here to parse `{$ident}` as an enum", + style = "verbose", + applicability = "maybe-incorrect", + code = "enum " + )] insert_span: Span, ident: Ident, }, - #[diag(parse_missing_enum_or_struct_for_item_definition)] + #[diag("missing `enum` or `struct` for enum or struct definition")] EnumOrStruct { #[primary_span] span: Span, }, - #[diag(parse_missing_struct_for_struct_definition)] + #[diag("missing `struct` for struct definition")] Struct { #[primary_span] span: Span, - #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "struct ")] + #[suggestion( + "add `struct` here to parse `{$ident}` as a struct", + style = "verbose", + applicability = "maybe-incorrect", + code = "struct " + )] insert_span: Span, ident: Ident, }, - #[diag(parse_missing_fn_for_function_definition)] + #[diag("missing `fn` for function definition")] Function { #[primary_span] span: Span, - #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "fn ")] + #[suggestion( + "add `fn` here to parse `{$ident}` as a function", + style = "verbose", + applicability = "maybe-incorrect", + code = "fn " + )] insert_span: Span, ident: Ident, }, - #[diag(parse_missing_fn_for_method_definition)] + #[diag("missing `fn` for method definition")] Method { #[primary_span] span: Span, - #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "fn ")] + #[suggestion( + "add `fn` here to parse `{$ident}` as a method", + style = "verbose", + applicability = "maybe-incorrect", + code = "fn " + )] insert_span: Span, ident: Ident, }, - #[diag(parse_missing_fn_or_struct_for_item_definition)] + #[diag("missing `fn` or `struct` for function or struct definition")] Ambiguous { #[primary_span] span: Span, @@ -1923,7 +2274,7 @@ pub(crate) enum MissingKeywordForItemDefinition { #[derive(Subdiagnostic)] pub(crate) enum AmbiguousMissingKwForItemSub { #[suggestion( - parse_suggestion, + "if you meant to call a macro, try", applicability = "maybe-incorrect", code = "{snippet}!", style = "verbose" @@ -1933,39 +2284,51 @@ pub(crate) enum AmbiguousMissingKwForItemSub { span: Span, snippet: String, }, - #[help(parse_help)] + #[help( + "if you meant to call a macro, remove the `pub` and add a trailing `!` after the identifier" + )] HelpMacro, } #[derive(Diagnostic)] -#[diag(parse_missing_fn_params)] +#[diag("missing parameters for function definition")] pub(crate) struct MissingFnParams { #[primary_span] - #[suggestion(code = "()", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "add a parameter list", + code = "()", + applicability = "machine-applicable", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_invalid_path_sep_in_fn_definition)] +#[diag("invalid path separator in function definition")] pub(crate) struct InvalidPathSepInFnDefinition { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "remove invalid path separator", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_missing_trait_in_trait_impl)] +#[diag("missing trait in a trait impl")] pub(crate) struct MissingTraitInTraitImpl { #[primary_span] #[suggestion( - parse_suggestion_add_trait, + "add a trait here", code = " Trait ", applicability = "has-placeholders", style = "verbose" )] pub span: Span, #[suggestion( - parse_suggestion_remove_for, + "for an inherent impl, drop this `for`", code = "", applicability = "maybe-incorrect", style = "verbose" @@ -1974,102 +2337,130 @@ pub(crate) struct MissingTraitInTraitImpl { } #[derive(Diagnostic)] -#[diag(parse_missing_for_in_trait_impl)] +#[diag("missing `for` in a trait impl")] pub(crate) struct MissingForInTraitImpl { #[primary_span] - #[suggestion(style = "verbose", code = " for ", applicability = "machine-applicable")] + #[suggestion( + "add `for` here", + style = "verbose", + code = " for ", + applicability = "machine-applicable" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_expected_trait_in_trait_impl_found_type)] +#[diag("expected a trait, found type")] pub(crate) struct ExpectedTraitInTraitImplFoundType { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_extra_impl_keyword_in_trait_impl)] +#[diag("unexpected `impl` keyword")] pub(crate) struct ExtraImplKeywordInTraitImpl { #[primary_span] - #[suggestion(code = "", applicability = "maybe-incorrect", style = "short")] + #[suggestion( + "remove the extra `impl`", + code = "", + applicability = "maybe-incorrect", + style = "short" + )] pub extra_impl_kw: Span, - #[note] + #[note("this is parsed as an `impl Trait` type, but a trait is expected at this position")] pub impl_trait_span: Span, } #[derive(Diagnostic)] -#[diag(parse_bounds_not_allowed_on_trait_aliases)] +#[diag("bounds are not allowed on trait aliases")] pub(crate) struct BoundsNotAllowedOnTraitAliases { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_trait_alias_cannot_be_auto)] +#[diag("trait aliases cannot be `auto`")] pub(crate) struct TraitAliasCannotBeAuto { #[primary_span] - #[label(parse_trait_alias_cannot_be_auto)] + #[label("trait aliases cannot be `auto`")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_trait_alias_cannot_be_unsafe)] +#[diag("trait aliases cannot be `unsafe`")] pub(crate) struct TraitAliasCannotBeUnsafe { #[primary_span] - #[label(parse_trait_alias_cannot_be_unsafe)] + #[label("trait aliases cannot be `unsafe`")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_associated_static_item_not_allowed)] +#[diag("associated `static` items are not allowed")] pub(crate) struct AssociatedStaticItemNotAllowed { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_extern_crate_name_with_dashes)] +#[diag("crate name using dashes are not valid in `extern crate` statements")] pub(crate) struct ExternCrateNameWithDashes { #[primary_span] - #[label] + #[label("dash-separated idents are not valid")] pub span: Span, #[subdiagnostic] pub sugg: ExternCrateNameWithDashesSugg, } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + "if the original crate name uses dashes you need to use underscores in the code", + applicability = "machine-applicable" +)] pub(crate) struct ExternCrateNameWithDashesSugg { #[suggestion_part(code = "_")] pub dashes: Vec, } #[derive(Diagnostic)] -#[diag(parse_extern_item_cannot_be_const)] -#[note] +#[diag("extern items cannot be `const`")] +#[note("for more information, visit https://doc.rust-lang.org/std/keyword.extern.html")] pub(crate) struct ExternItemCannotBeConst { #[primary_span] pub ident_span: Span, - #[suggestion(code = "static ", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "try using a static value", + code = "static ", + applicability = "machine-applicable", + style = "verbose" + )] pub const_span: Option, } #[derive(Diagnostic)] -#[diag(parse_const_global_cannot_be_mutable)] +#[diag("const globals cannot be mutable")] pub(crate) struct ConstGlobalCannotBeMutable { #[primary_span] - #[label] + #[label("cannot be mutable")] pub ident_span: Span, - #[suggestion(code = "static", style = "verbose", applicability = "maybe-incorrect")] + #[suggestion( + "you might want to declare a static instead", + code = "static", + style = "verbose", + applicability = "maybe-incorrect" + )] pub const_span: Span, } #[derive(Diagnostic)] -#[diag(parse_missing_const_type)] +#[diag("missing type for `{$kind}` item")] pub(crate) struct MissingConstType { #[primary_span] - #[suggestion(code = "{colon} ", style = "verbose", applicability = "has-placeholders")] + #[suggestion( + "provide a type for the item", + code = "{colon} ", + style = "verbose", + applicability = "has-placeholders" + )] pub span: Span, pub kind: &'static str, @@ -2077,53 +2468,64 @@ pub(crate) struct MissingConstType { } #[derive(Diagnostic)] -#[diag(parse_enum_struct_mutually_exclusive)] +#[diag("`enum` and `struct` are mutually exclusive")] pub(crate) struct EnumStructMutuallyExclusive { #[primary_span] - #[suggestion(code = "enum", style = "verbose", applicability = "machine-applicable")] + #[suggestion( + "replace `enum struct` with", + code = "enum", + style = "verbose", + applicability = "machine-applicable" + )] pub span: Span, } #[derive(Diagnostic)] pub(crate) enum UnexpectedTokenAfterStructName { - #[diag(parse_unexpected_token_after_struct_name_found_reserved_identifier)] + #[diag( + "expected `where`, `{\"{\"}`, `(`, or `;` after struct name, found reserved identifier `{$token}`" + )] ReservedIdentifier { #[primary_span] - #[label(parse_unexpected_token_after_struct_name)] + #[label("expected `where`, `{\"{\"}`, `(`, or `;` after struct name")] span: Span, token: Token, }, - #[diag(parse_unexpected_token_after_struct_name_found_keyword)] + #[diag("expected `where`, `{\"{\"}`, `(`, or `;` after struct name, found keyword `{$token}`")] Keyword { #[primary_span] - #[label(parse_unexpected_token_after_struct_name)] + #[label("expected `where`, `{\"{\"}`, `(`, or `;` after struct name")] span: Span, token: Token, }, - #[diag(parse_unexpected_token_after_struct_name_found_reserved_keyword)] + #[diag( + "expected `where`, `{\"{\"}`, `(`, or `;` after struct name, found reserved keyword `{$token}`" + )] ReservedKeyword { #[primary_span] - #[label(parse_unexpected_token_after_struct_name)] + #[label("expected `where`, `{\"{\"}`, `(`, or `;` after struct name")] span: Span, token: Token, }, - #[diag(parse_unexpected_token_after_struct_name_found_doc_comment)] + #[diag( + "expected `where`, `{\"{\"}`, `(`, or `;` after struct name, found doc comment `{$token}`" + )] DocComment { #[primary_span] - #[label(parse_unexpected_token_after_struct_name)] + #[label("expected `where`, `{\"{\"}`, `(`, or `;` after struct name")] span: Span, token: Token, }, - #[diag(parse_unexpected_token_after_struct_name_found_metavar)] + #[diag("expected `where`, `{\"{\"}`, `(`, or `;` after struct name, found metavar")] MetaVar { #[primary_span] - #[label(parse_unexpected_token_after_struct_name)] + #[label("expected `where`, `{\"{\"}`, `(`, or `;` after struct name")] span: Span, }, - #[diag(parse_unexpected_token_after_struct_name_found_other)] + #[diag("expected `where`, `{\"{\"}`, `(`, or `;` after struct name, found `{$token}`")] Other { #[primary_span] - #[label(parse_unexpected_token_after_struct_name)] + #[label("expected `where`, `{\"{\"}`, `(`, or `;` after struct name")] span: Span, token: Token, }, @@ -2143,45 +2545,50 @@ impl UnexpectedTokenAfterStructName { } #[derive(Diagnostic)] -#[diag(parse_unexpected_self_in_generic_parameters)] -#[note] +#[diag("unexpected keyword `Self` in generic parameters")] +#[note("you cannot use `Self` as a generic parameter because it is reserved for associated items")] pub(crate) struct UnexpectedSelfInGenericParameters { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_unexpected_default_value_for_lifetime_in_generic_parameters)] +#[diag("unexpected default lifetime parameter")] pub(crate) struct UnexpectedDefaultValueForLifetimeInGenericParameters { #[primary_span] - #[label] + #[label("lifetime parameters cannot have default values")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_multiple_where_clauses)] +#[diag("cannot define duplicate `where` clauses on an item")] pub(crate) struct MultipleWhereClauses { #[primary_span] pub span: Span, - #[label] + #[label("previous `where` clause starts here")] pub previous: Span, - #[suggestion(style = "verbose", code = ",", applicability = "maybe-incorrect")] + #[suggestion( + "consider joining the two `where` clauses into one", + style = "verbose", + code = ",", + applicability = "maybe-incorrect" + )] pub between: Span, } #[derive(Diagnostic)] pub(crate) enum UnexpectedNonterminal { - #[diag(parse_nonterminal_expected_item_keyword)] + #[diag("expected an item keyword")] Item(#[primary_span] Span), - #[diag(parse_nonterminal_expected_statement)] + #[diag("expected a statement")] Statement(#[primary_span] Span), - #[diag(parse_nonterminal_expected_ident)] + #[diag("expected ident, found `{$token}`")] Ident { #[primary_span] span: Span, token: Token, }, - #[diag(parse_nonterminal_expected_lifetime)] + #[diag("expected a lifetime, found `{$token}`")] Lifetime { #[primary_span] span: Span, @@ -2191,14 +2598,14 @@ pub(crate) enum UnexpectedNonterminal { #[derive(Diagnostic)] pub(crate) enum TopLevelOrPatternNotAllowed { - #[diag(parse_or_pattern_not_allowed_in_let_binding)] + #[diag("`let` bindings require top-level or-patterns in parentheses")] LetBinding { #[primary_span] span: Span, #[subdiagnostic] sub: Option, }, - #[diag(parse_or_pattern_not_allowed_in_fn_parameters)] + #[diag("function parameters require top-level or-patterns in parentheses")] FunctionParameter { #[primary_span] span: Span, @@ -2208,7 +2615,7 @@ pub(crate) enum TopLevelOrPatternNotAllowed { } #[derive(Diagnostic)] -#[diag(parse_cannot_be_raw_ident)] +#[diag("`{$ident}` cannot be a raw identifier")] pub(crate) struct CannotBeRawIdent { #[primary_span] pub span: Span, @@ -2216,7 +2623,7 @@ pub(crate) struct CannotBeRawIdent { } #[derive(Diagnostic)] -#[diag(parse_cannot_be_raw_lifetime)] +#[diag("`{$ident}` cannot be a raw lifetime")] pub(crate) struct CannotBeRawLifetime { #[primary_span] pub span: Span, @@ -2224,21 +2631,26 @@ pub(crate) struct CannotBeRawLifetime { } #[derive(Diagnostic)] -#[diag(parse_keyword_lifetime)] +#[diag("lifetimes cannot use keyword names")] pub(crate) struct KeywordLifetime { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_keyword_label)] +#[diag("labels cannot use keyword names")] pub(crate) struct KeywordLabel { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_cr_doc_comment)] +#[diag( + "bare CR not allowed in {$block -> + [true] block doc-comment + *[false] doc-comment + }" +)] pub(crate) struct CrDocComment { #[primary_span] pub span: Span, @@ -2246,14 +2658,14 @@ pub(crate) struct CrDocComment { } #[derive(Diagnostic)] -#[diag(parse_no_digits_literal, code = E0768)] +#[diag("no valid digits found for number", code = E0768)] pub(crate) struct NoDigitsLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_invalid_digit_literal)] +#[diag("invalid digit for a base {$base} literal")] pub(crate) struct InvalidDigitLiteral { #[primary_span] pub span: Span, @@ -2261,14 +2673,14 @@ pub(crate) struct InvalidDigitLiteral { } #[derive(Diagnostic)] -#[diag(parse_empty_exponent_float)] +#[diag("expected at least one digit in exponent")] pub(crate) struct EmptyExponentFloat { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_float_literal_unsupported_base)] +#[diag("{$base} float literal is not supported")] pub(crate) struct FloatLiteralUnsupportedBase { #[primary_span] pub span: Span, @@ -2276,11 +2688,11 @@ pub(crate) struct FloatLiteralUnsupportedBase { } #[derive(Diagnostic)] -#[diag(parse_unknown_prefix)] -#[note] +#[diag("prefix `{$prefix}` is unknown")] +#[note("prefixed identifiers and literals are reserved since Rust 2021")] pub(crate) struct UnknownPrefix<'a> { #[primary_span] - #[label] + #[label("unknown prefix")] pub span: Span, pub prefix: &'a str, #[subdiagnostic] @@ -2288,7 +2700,7 @@ pub(crate) struct UnknownPrefix<'a> { } #[derive(Subdiagnostic)] -#[note(parse_macro_expands_to_adt_field)] +#[note("macros cannot expand to {$adt_ty} fields")] pub(crate) struct MacroExpandsToAdtField<'a> { pub adt_ty: &'a str, } @@ -2296,28 +2708,28 @@ pub(crate) struct MacroExpandsToAdtField<'a> { #[derive(Subdiagnostic)] pub(crate) enum UnknownPrefixSugg { #[suggestion( - parse_suggestion_br, + "use `br` for a raw byte string", code = "br", applicability = "maybe-incorrect", style = "verbose" )] UseBr(#[primary_span] Span), #[suggestion( - parse_suggestion_cr, + "use `cr` for a raw C-string", code = "cr", applicability = "maybe-incorrect", style = "verbose" )] UseCr(#[primary_span] Span), #[suggestion( - parse_suggestion_whitespace, + "consider inserting whitespace here", code = " ", applicability = "maybe-incorrect", style = "verbose" )] Whitespace(#[primary_span] Span), #[multipart_suggestion( - parse_suggestion_str, + "if you meant to write a string literal, use double quotes", applicability = "maybe-incorrect", style = "verbose" )] @@ -2330,8 +2742,8 @@ pub(crate) enum UnknownPrefixSugg { } #[derive(Diagnostic)] -#[diag(parse_reserved_multihash)] -#[note] +#[diag("reserved multi-hash token is forbidden")] +#[note("sequences of two or more # are reserved for future use since Rust 2024")] pub(crate) struct ReservedMultihash { #[primary_span] pub span: Span, @@ -2339,8 +2751,8 @@ pub(crate) struct ReservedMultihash { pub sugg: Option, } #[derive(Diagnostic)] -#[diag(parse_reserved_string)] -#[note] +#[diag("invalid string literal")] +#[note("unprefixed guarded string literals are reserved for future use since Rust 2024")] pub(crate) struct ReservedString { #[primary_span] pub span: Span, @@ -2349,7 +2761,7 @@ pub(crate) struct ReservedString { } #[derive(Subdiagnostic)] #[suggestion( - parse_suggestion_whitespace, + "consider inserting whitespace here", code = " ", applicability = "maybe-incorrect", style = "verbose" @@ -2357,7 +2769,9 @@ pub(crate) struct ReservedString { pub(crate) struct GuardedStringSugg(#[primary_span] pub Span); #[derive(Diagnostic)] -#[diag(parse_too_many_hashes)] +#[diag( + "too many `#` symbols: raw strings may be delimited by up to 255 `#` symbols, but found {$num}" +)] pub(crate) struct TooManyHashes { #[primary_span] pub span: Span, @@ -2365,25 +2779,27 @@ pub(crate) struct TooManyHashes { } #[derive(Diagnostic)] -#[diag(parse_unknown_start_of_token)] +#[diag("unknown start of token: {$escaped}")] pub(crate) struct UnknownTokenStart { #[primary_span] pub span: Span, pub escaped: String, #[subdiagnostic] pub sugg: Option, - #[subdiagnostic] - pub null: Option, + #[help( + "source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used" + )] + pub null: bool, #[subdiagnostic] pub repeat: Option, - #[subdiagnostic] - pub invisible: Option, + #[help("invisible characters like '{$escaped}' are not usually visible in text editors")] + pub invisible: bool, } #[derive(Subdiagnostic)] pub(crate) enum TokenSubstitution { #[suggestion( - parse_sugg_quotes, + "Unicode characters '“' (Left Double Quotation Mark) and '”' (Right Double Quotation Mark) look like '{$ascii_str}' ({$ascii_name}), but are not", code = "{suggestion}", applicability = "maybe-incorrect", style = "verbose" @@ -2396,7 +2812,7 @@ pub(crate) enum TokenSubstitution { ascii_name: &'static str, }, #[suggestion( - parse_sugg_other, + "Unicode character '{$ch}' ({$u_name}) looks like '{$ascii_str}' ({$ascii_name}), but it is not", code = "{suggestion}", applicability = "maybe-incorrect", style = "verbose" @@ -2413,35 +2829,42 @@ pub(crate) enum TokenSubstitution { } #[derive(Subdiagnostic)] -#[note(parse_note_repeats)] +#[note( + "character appears {$repeats -> + [one] once more + *[other] {$repeats} more times + }" +)] pub(crate) struct UnknownTokenRepeat { pub repeats: usize, } -#[derive(Subdiagnostic)] -#[help(parse_help_invisible_char)] -pub(crate) struct InvisibleCharacter; - -#[derive(Subdiagnostic)] -#[help(parse_help_null)] -pub(crate) struct UnknownTokenNull; - #[derive(Diagnostic)] pub(crate) enum UnescapeError { - #[diag(parse_invalid_unicode_escape)] - #[help] + #[diag("invalid unicode character escape")] + #[help( + "unicode escape must {$surrogate -> + [true] not be a surrogate + *[false] be at most 10FFFF + }" + )] InvalidUnicodeEscape { #[primary_span] - #[label] + #[label("invalid escape")] span: Span, surrogate: bool, }, - #[diag(parse_escape_only_char)] + #[diag( + "{$byte -> + [true] byte + *[false] character + } constant must be escaped: `{$escaped_msg}`" + )] EscapeOnlyChar { #[primary_span] span: Span, #[suggestion( - parse_escape, + "escape the character", applicability = "machine-applicable", code = "{escaped_sugg}", style = "verbose" @@ -2451,11 +2874,16 @@ pub(crate) enum UnescapeError { escaped_msg: String, byte: bool, }, - #[diag(parse_bare_cr)] + #[diag( + r#"{$double_quotes -> + [true] bare CR not allowed in string, use `\r` instead + *[false] character constant must be escaped: `\r` + }"# + )] BareCr { #[primary_span] #[suggestion( - parse_escape, + "escape the character", applicability = "machine-applicable", code = "\\r", style = "verbose" @@ -2463,93 +2891,103 @@ pub(crate) enum UnescapeError { span: Span, double_quotes: bool, }, - #[diag(parse_bare_cr_in_raw_string)] + #[diag("bare CR not allowed in raw string")] BareCrRawString(#[primary_span] Span), - #[diag(parse_too_short_hex_escape)] + #[diag("numeric character escape is too short")] TooShortHexEscape(#[primary_span] Span), - #[diag(parse_invalid_char_in_escape)] + #[diag( + "invalid character in {$is_hex -> + [true] numeric character + *[false] unicode + } escape: `{$ch}`" + )] InvalidCharInEscape { #[primary_span] - #[label] + #[label( + "invalid character in {$is_hex -> + [true] numeric character + *[false] unicode + } escape" + )] span: Span, is_hex: bool, ch: String, }, - #[diag(parse_leading_underscore_unicode_escape)] + #[diag("invalid start of unicode escape: `_`")] LeadingUnderscoreUnicodeEscape { #[primary_span] - #[label(parse_leading_underscore_unicode_escape_label)] + #[label("invalid start of unicode escape")] span: Span, ch: String, }, - #[diag(parse_overlong_unicode_escape)] + #[diag("overlong unicode escape")] OverlongUnicodeEscape( #[primary_span] - #[label] + #[label("must have at most 6 hex digits")] Span, ), - #[diag(parse_unclosed_unicode_escape)] + #[diag("unterminated unicode escape")] UnclosedUnicodeEscape( #[primary_span] - #[label] + #[label(r#"missing a closing `{"}"}`"#)] Span, #[suggestion( - parse_terminate, + "terminate the unicode escape", code = "}}", applicability = "maybe-incorrect", style = "verbose" )] Span, ), - #[diag(parse_no_brace_unicode_escape)] + #[diag("incorrect unicode escape sequence")] NoBraceInUnicodeEscape { #[primary_span] span: Span, - #[label] + #[label("incorrect unicode escape sequence")] label: Option, #[subdiagnostic] sub: NoBraceUnicodeSub, }, - #[diag(parse_unicode_escape_in_byte)] - #[help] + #[diag("unicode escape in byte string")] + #[help("unicode escape sequences cannot be used as a byte or in a byte string")] UnicodeEscapeInByte( #[primary_span] - #[label] + #[label("unicode escape in byte string")] Span, ), - #[diag(parse_empty_unicode_escape)] + #[diag("empty unicode escape")] EmptyUnicodeEscape( #[primary_span] - #[label] + #[label("this escape must have at least 1 hex digit")] Span, ), - #[diag(parse_zero_chars)] + #[diag("empty character literal")] ZeroChars( #[primary_span] - #[label] + #[label("empty character literal")] Span, ), - #[diag(parse_lone_slash)] + #[diag("invalid trailing slash in literal")] LoneSlash( #[primary_span] - #[label] + #[label("invalid trailing slash in literal")] Span, ), - #[diag(parse_unskipped_whitespace)] + #[diag("whitespace symbol '{$ch}' is not skipped")] UnskippedWhitespace { #[primary_span] span: Span, - #[label] + #[label("whitespace symbol '{$ch}' is not skipped")] char_span: Span, ch: String, }, - #[diag(parse_multiple_skipped_lines)] + #[diag("multiple lines skipped by escaped newline")] MultipleSkippedLinesWarning( #[primary_span] - #[label] + #[label("skipping everything up to and including this point")] Span, ), - #[diag(parse_more_than_one_char)] + #[diag("character literal may only contain one codepoint")] MoreThanOneChar { #[primary_span] span: Span, @@ -2558,7 +2996,7 @@ pub(crate) enum UnescapeError { #[subdiagnostic] suggestion: MoreThanOneCharSugg, }, - #[diag(parse_nul_in_c_str)] + #[diag("null characters in C string literals are not supported")] NulInCStr { #[primary_span] span: Span, @@ -2568,7 +3006,7 @@ pub(crate) enum UnescapeError { #[derive(Subdiagnostic)] pub(crate) enum MoreThanOneCharSugg { #[suggestion( - parse_consider_normalized, + "consider using the normalized form `{$ch}` of this character", code = "{normalized}", applicability = "machine-applicable", style = "verbose" @@ -2580,7 +3018,7 @@ pub(crate) enum MoreThanOneCharSugg { normalized: String, }, #[suggestion( - parse_remove_non, + "consider removing the non-printing characters", code = "{ch}", applicability = "maybe-incorrect", style = "verbose" @@ -2591,7 +3029,10 @@ pub(crate) enum MoreThanOneCharSugg { ch: String, }, #[suggestion( - parse_use_double_quotes, + "if you meant to write a {$is_byte -> + [true] byte string + *[false] string + } literal, use double quotes", code = "{sugg}", applicability = "machine-applicable", style = "verbose" @@ -2602,7 +3043,13 @@ pub(crate) enum MoreThanOneCharSugg { is_byte: bool, sugg: String, }, - #[multipart_suggestion(parse_use_double_quotes, applicability = "machine-applicable")] + #[multipart_suggestion( + "if you meant to write a {$is_byte -> + [true] byte string + *[false] string + } literal, use double quotes", + applicability = "machine-applicable" + )] Quotes { #[suggestion_part(code = "{prefix}\"")] start: Span, @@ -2615,7 +3062,12 @@ pub(crate) enum MoreThanOneCharSugg { #[derive(Subdiagnostic)] pub(crate) enum MoreThanOneCharNote { - #[note(parse_followed_by)] + #[note( + "this `{$chr}` is followed by the combining {$len -> + [one] mark + *[other] marks + } `{$escaped_marks}`" + )] AllCombining { #[primary_span] span: Span, @@ -2623,7 +3075,7 @@ pub(crate) enum MoreThanOneCharNote { len: usize, escaped_marks: String, }, - #[note(parse_non_printing)] + #[note("there are non-printing characters, the full sequence is `{$escaped}`")] NonPrinting { #[primary_span] span: Span, @@ -2634,7 +3086,7 @@ pub(crate) enum MoreThanOneCharNote { #[derive(Subdiagnostic)] pub(crate) enum NoBraceUnicodeSub { #[suggestion( - parse_use_braces, + "format of unicode escape sequences uses braces", code = "{suggestion}", applicability = "maybe-incorrect", style = "verbose" @@ -2644,12 +3096,12 @@ pub(crate) enum NoBraceUnicodeSub { span: Span, suggestion: String, }, - #[help(parse_format_of_unicode)] + #[help(r#"format of unicode escape sequences is `\u{"{...}"}`"#)] Help, } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_sugg_wrap_pattern_in_parens, applicability = "machine-applicable")] +#[multipart_suggestion("wrap the pattern in parentheses", applicability = "machine-applicable")] pub(crate) struct WrapInParens { #[suggestion_part(code = "(")] pub(crate) lo: Span, @@ -2660,7 +3112,7 @@ pub(crate) struct WrapInParens { #[derive(Subdiagnostic)] pub(crate) enum TopLevelOrPatternNotAllowedSugg { #[suggestion( - parse_sugg_remove_leading_vert_in_pattern, + "remove the `|`", code = "", applicability = "machine-applicable", style = "tool-only" @@ -2678,27 +3130,37 @@ pub(crate) enum TopLevelOrPatternNotAllowedSugg { } #[derive(Diagnostic)] -#[diag(parse_unexpected_vert_vert_before_function_parameter)] -#[note(parse_note_pattern_alternatives_use_single_vert)] +#[diag("unexpected `||` before function parameter")] +#[note("alternatives in or-patterns are separated with `|`, not `||`")] pub(crate) struct UnexpectedVertVertBeforeFunctionParam { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "remove the `||`", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_unexpected_vert_vert_in_pattern)] +#[diag("unexpected token `||` in pattern")] pub(crate) struct UnexpectedVertVertInPattern { #[primary_span] - #[suggestion(code = "|", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "use a single `|` to separate multiple alternative patterns", + code = "|", + applicability = "machine-applicable", + style = "verbose" + )] pub span: Span, - #[label(parse_label_while_parsing_or_pattern_here)] + #[label("while parsing this or-pattern starting here")] pub start: Option, } #[derive(Subdiagnostic)] #[suggestion( - parse_trailing_vert_not_allowed, + "a trailing `{$token}` is not allowed in an or-pattern", code = "", applicability = "machine-applicable", style = "tool-only" @@ -2709,59 +3171,71 @@ pub(crate) struct TrailingVertSuggestion { } #[derive(Diagnostic)] -#[diag(parse_trailing_vert_not_allowed)] +#[diag("a trailing `{$token}` is not allowed in an or-pattern")] pub(crate) struct TrailingVertNotAllowed { #[primary_span] pub span: Span, #[subdiagnostic] pub suggestion: TrailingVertSuggestion, - #[label(parse_label_while_parsing_or_pattern_here)] + #[label("while parsing this or-pattern starting here")] pub start: Option, pub token: Token, - #[note(parse_note_pattern_alternatives_use_single_vert)] + #[note("alternatives in or-patterns are separated with `|`, not `||`")] pub note_double_vert: bool, } #[derive(Diagnostic)] -#[diag(parse_dotdotdot_rest_pattern)] +#[diag("unexpected `...`")] pub(crate) struct DotDotDotRestPattern { #[primary_span] - #[label] + #[label("not a valid pattern")] pub span: Span, - #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] + #[suggestion( + "for a rest pattern, use `..` instead of `...`", + style = "verbose", + code = "", + applicability = "machine-applicable" + )] pub suggestion: Option, - #[note] + #[note( + "only `extern \"C\"` and `extern \"C-unwind\"` functions may have a C variable argument list" + )] pub var_args: Option<()>, } #[derive(Diagnostic)] -#[diag(parse_pattern_on_wrong_side_of_at)] +#[diag("pattern on wrong side of `@`")] pub(crate) struct PatternOnWrongSideOfAt { #[primary_span] - #[suggestion(code = "{whole_pat}", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "switch the order", + code = "{whole_pat}", + applicability = "machine-applicable", + style = "verbose" + )] pub whole_span: Span, pub whole_pat: String, - #[label(parse_label_pattern)] + #[label("pattern on the left, should be on the right")] pub pattern: Span, - #[label(parse_label_binding)] + #[label("binding on the right, should be on the left")] pub binding: Span, } #[derive(Diagnostic)] -#[diag(parse_expected_binding_left_of_at)] -#[note] +#[diag("left-hand side of `@` must be a binding")] +#[note("bindings are `x`, `mut x`, `ref x`, and `ref mut x`")] pub(crate) struct ExpectedBindingLeftOfAt { #[primary_span] pub whole_span: Span, - #[label(parse_label_lhs)] + #[label("interpreted as a pattern, not a binding")] pub lhs: Span, - #[label(parse_label_rhs)] + #[label("also a pattern")] pub rhs: Span, } #[derive(Subdiagnostic)] #[multipart_suggestion( - parse_ambiguous_range_pattern_suggestion, + "add parentheses to clarify the precedence", applicability = "machine-applicable" )] pub(crate) struct ParenRangeSuggestion { @@ -2772,7 +3246,7 @@ pub(crate) struct ParenRangeSuggestion { } #[derive(Diagnostic)] -#[diag(parse_ambiguous_range_pattern)] +#[diag("the range pattern here has ambiguous interpretation")] pub(crate) struct AmbiguousRangePattern { #[primary_span] pub span: Span, @@ -2781,100 +3255,144 @@ pub(crate) struct AmbiguousRangePattern { } #[derive(Diagnostic)] -#[diag(parse_unexpected_lifetime_in_pattern)] +#[diag("unexpected lifetime `{$symbol}` in pattern")] pub(crate) struct UnexpectedLifetimeInPattern { #[primary_span] pub span: Span, pub symbol: Symbol, - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "remove the lifetime", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] pub suggestion: Span, } #[derive(Diagnostic)] pub(crate) enum InvalidMutInPattern { - #[diag(parse_mut_on_nested_ident_pattern)] - #[note(parse_note_mut_pattern_usage)] + #[diag("`mut` must be attached to each individual binding")] + #[note("`mut` may be followed by `variable` and `variable @ pattern`")] NestedIdent { #[primary_span] - #[suggestion(code = "{pat}", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "add `mut` to each binding", + code = "{pat}", + applicability = "machine-applicable", + style = "verbose" + )] span: Span, pat: String, }, - #[diag(parse_mut_on_non_ident_pattern)] - #[note(parse_note_mut_pattern_usage)] + #[diag("`mut` must be followed by a named binding")] + #[note("`mut` may be followed by `variable` and `variable @ pattern`")] NonIdent { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "remove the `mut` prefix", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] span: Span, }, } #[derive(Diagnostic)] -#[diag(parse_repeated_mut_in_pattern)] +#[diag("`mut` on a binding may not be repeated")] pub(crate) struct RepeatedMutInPattern { #[primary_span] pub span: Span, - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "remove the additional `mut`s", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] pub suggestion: Span, } #[derive(Diagnostic)] -#[diag(parse_dot_dot_dot_range_to_pattern_not_allowed)] +#[diag("range-to patterns with `...` are not allowed")] pub(crate) struct DotDotDotRangeToPatternNotAllowed { #[primary_span] - #[suggestion(style = "verbose", code = "..=", applicability = "machine-applicable")] + #[suggestion( + "use `..=` instead", + style = "verbose", + code = "..=", + applicability = "machine-applicable" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_enum_pattern_instead_of_identifier)] +#[diag("expected identifier, found enum pattern")] pub(crate) struct EnumPatternInsteadOfIdentifier { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_at_dot_dot_in_struct_pattern)] +#[diag("`@ ..` is not supported in struct patterns")] pub(crate) struct AtDotDotInStructPattern { #[primary_span] pub span: Span, - #[suggestion(code = "", style = "verbose", applicability = "machine-applicable")] + #[suggestion( + "bind to each field separately or, if you don't need them, just remove `{$ident} @`", + code = "", + style = "verbose", + applicability = "machine-applicable" + )] pub remove: Span, pub ident: Ident, } #[derive(Diagnostic)] -#[diag(parse_at_in_struct_pattern)] -#[note] -#[help] +#[diag("unexpected `@` in struct pattern")] +#[note("struct patterns use `field: pattern` syntax to bind to fields")] +#[help( + "consider replacing `new_name @ field_name` with `field_name: new_name` if that is what you intended" +)] pub(crate) struct AtInStructPattern { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_dot_dot_dot_for_remaining_fields)] +#[diag("expected field pattern, found `{$token_str}`")] pub(crate) struct DotDotDotForRemainingFields { #[primary_span] - #[suggestion(code = "..", style = "verbose", applicability = "machine-applicable")] + #[suggestion( + "to omit remaining fields, use `..`", + code = "..", + style = "verbose", + applicability = "machine-applicable" + )] pub span: Span, pub token_str: Cow<'static, str>, } #[derive(Diagnostic)] -#[diag(parse_expected_comma_after_pattern_field)] +#[diag("expected `,`")] pub(crate) struct ExpectedCommaAfterPatternField { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_unexpected_expr_in_pat)] -#[note] +#[diag( + "expected {$is_bound -> + [true] a pattern range bound + *[false] a pattern + }, found an expression" +)] +#[note( + "arbitrary expressions are not allowed in patterns: " +)] pub(crate) struct UnexpectedExpressionInPattern { /// The unexpected expr's span. #[primary_span] - #[label] + #[label("not a pattern")] pub span: Span, /// Was a `RangePatternBound` expected? pub is_bound: bool, @@ -2885,7 +3403,7 @@ pub(crate) struct UnexpectedExpressionInPattern { #[derive(Subdiagnostic)] pub(crate) enum UnexpectedExpressionInPatternSugg { #[multipart_suggestion( - parse_unexpected_expr_in_pat_create_guard_sugg, + "consider moving the expression to a match arm guard", applicability = "maybe-incorrect" )] CreateGuard { @@ -2902,7 +3420,7 @@ pub(crate) enum UnexpectedExpressionInPatternSugg { }, #[multipart_suggestion( - parse_unexpected_expr_in_pat_update_guard_sugg, + "consider moving the expression to the match arm guard", applicability = "maybe-incorrect" )] UpdateGuard { @@ -2924,7 +3442,7 @@ pub(crate) enum UnexpectedExpressionInPatternSugg { }, #[multipart_suggestion( - parse_unexpected_expr_in_pat_const_sugg, + "consider extracting the expression into a `const`", applicability = "has-placeholders" )] Const { @@ -2944,7 +3462,7 @@ pub(crate) enum UnexpectedExpressionInPatternSugg { } #[derive(Diagnostic)] -#[diag(parse_unexpected_paren_in_range_pat)] +#[diag("range pattern bounds cannot have parentheses")] pub(crate) struct UnexpectedParenInRangePat { #[primary_span] pub span: Vec, @@ -2953,10 +3471,7 @@ pub(crate) struct UnexpectedParenInRangePat { } #[derive(Subdiagnostic)] -#[multipart_suggestion( - parse_unexpected_paren_in_range_pat_sugg, - applicability = "machine-applicable" -)] +#[multipart_suggestion("remove these parentheses", applicability = "machine-applicable")] pub(crate) struct UnexpectedParenInRangePatSugg { #[suggestion_part(code = "")] pub start_span: Span, @@ -2965,104 +3480,145 @@ pub(crate) struct UnexpectedParenInRangePatSugg { } #[derive(Diagnostic)] -#[diag(parse_return_types_use_thin_arrow)] +#[diag("return types are denoted using `->`")] pub(crate) struct ReturnTypesUseThinArrow { #[primary_span] pub span: Span, - #[suggestion(style = "verbose", code = " -> ", applicability = "machine-applicable")] + #[suggestion( + "use `->` instead", + style = "verbose", + code = " -> ", + applicability = "machine-applicable" + )] pub suggestion: Span, } #[derive(Diagnostic)] -#[diag(parse_need_plus_after_trait_object_lifetime)] +#[diag("lifetimes must be followed by `+` to form a trait object type")] pub(crate) struct NeedPlusAfterTraitObjectLifetime { #[primary_span] pub span: Span, - #[suggestion(code = " + /* Trait */", applicability = "has-placeholders")] + #[suggestion( + "consider adding a trait bound after the potential lifetime bound", + code = " + /* Trait */", + applicability = "has-placeholders" + )] pub suggestion: Span, } #[derive(Diagnostic)] -#[diag(parse_expected_mut_or_const_in_raw_pointer_type)] +#[diag("expected `mut` or `const` keyword in raw pointer type")] pub(crate) struct ExpectedMutOrConstInRawPointerType { #[primary_span] pub span: Span, - #[suggestion(code("mut ", "const "), applicability = "has-placeholders", style = "verbose")] + #[suggestion( + "add `mut` or `const` here", + code("mut ", "const "), + applicability = "has-placeholders", + style = "verbose" + )] pub after_asterisk: Span, } #[derive(Diagnostic)] -#[diag(parse_lifetime_after_mut)] +#[diag("lifetime must precede `mut`")] pub(crate) struct LifetimeAfterMut { #[primary_span] pub span: Span, - #[suggestion(code = "&{snippet} mut", applicability = "maybe-incorrect", style = "verbose")] + #[suggestion( + "place the lifetime before `mut`", + code = "&{snippet} mut", + applicability = "maybe-incorrect", + style = "verbose" + )] pub suggest_lifetime: Option, pub snippet: String, } #[derive(Diagnostic)] -#[diag(parse_dyn_after_mut)] +#[diag("`mut` must precede `dyn`")] pub(crate) struct DynAfterMut { #[primary_span] - #[suggestion(code = "&mut dyn", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "place `mut` before `dyn`", + code = "&mut dyn", + applicability = "machine-applicable", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_fn_pointer_cannot_be_const)] -#[note] +#[diag("an `fn` pointer type cannot be `const`")] +#[note("allowed qualifiers are: `unsafe` and `extern`")] pub(crate) struct FnPointerCannotBeConst { #[primary_span] - #[label] + #[label("`const` because of this")] pub span: Span, - #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] + #[suggestion( + "remove the `const` qualifier", + code = "", + applicability = "maybe-incorrect", + style = "verbose" + )] pub suggestion: Span, } #[derive(Diagnostic)] -#[diag(parse_fn_pointer_cannot_be_async)] -#[note] +#[diag("an `fn` pointer type cannot be `async`")] +#[note("allowed qualifiers are: `unsafe` and `extern`")] pub(crate) struct FnPointerCannotBeAsync { #[primary_span] - #[label] + #[label("`async` because of this")] pub span: Span, - #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] + #[suggestion( + "remove the `async` qualifier", + code = "", + applicability = "maybe-incorrect", + style = "verbose" + )] pub suggestion: Span, } #[derive(Diagnostic)] -#[diag(parse_nested_c_variadic_type, code = E0743)] +#[diag("C-variadic type `...` may not be nested inside another type", code = E0743)] pub(crate) struct NestedCVariadicType { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_dotdotdot_rest_type)] -#[note] +#[diag("unexpected `...`")] +#[note( + "only `extern \"C\"` and `extern \"C-unwind\"` functions may have a C variable argument list" +)] pub(crate) struct InvalidCVariadicType { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_invalid_dyn_keyword)] -#[help] +#[diag("invalid `dyn` keyword")] +#[help("`dyn` is only needed at the start of a trait `+`-separated list")] pub(crate) struct InvalidDynKeyword { #[primary_span] pub span: Span, - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "remove this keyword", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] pub suggestion: Span, } #[derive(Subdiagnostic)] pub(crate) enum HelpUseLatestEdition { - #[help(parse_help_set_edition_cargo)] - #[note(parse_note_edition_guide)] + #[help("set `edition = \"{$edition}\"` in `Cargo.toml`")] + #[note("for more on editions, read https://doc.rust-lang.org/edition-guide")] Cargo { edition: Edition }, - #[help(parse_help_set_edition_standalone)] - #[note(parse_note_edition_guide)] + #[help("pass `--edition {$edition}` to `rustc`")] + #[note("for more on editions, read https://doc.rust-lang.org/edition-guide")] Standalone { edition: Edition }, } @@ -3078,7 +3634,7 @@ impl HelpUseLatestEdition { } #[derive(Diagnostic)] -#[diag(parse_box_syntax_removed)] +#[diag("`box_syntax` has been removed")] pub(crate) struct BoxSyntaxRemoved { #[primary_span] pub span: Span, @@ -3088,7 +3644,7 @@ pub(crate) struct BoxSyntaxRemoved { #[derive(Subdiagnostic)] #[multipart_suggestion( - parse_box_syntax_removed_suggestion, + "use `Box::new()` instead", applicability = "machine-applicable", style = "verbose" )] @@ -3100,65 +3656,70 @@ pub(crate) struct AddBoxNew { } #[derive(Diagnostic)] -#[diag(parse_bad_return_type_notation_output)] +#[diag("return type not allowed with return type notation")] pub(crate) struct BadReturnTypeNotationOutput { #[primary_span] pub span: Span, - #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] + #[suggestion( + "remove the return type", + code = "", + applicability = "maybe-incorrect", + style = "verbose" + )] pub suggestion: Span, } #[derive(Diagnostic)] -#[diag(parse_bad_assoc_type_bounds)] +#[diag("bounds on associated types do not belong here")] pub(crate) struct BadAssocTypeBounds { #[primary_span] - #[label] + #[label("belongs in `where` clause")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_attr_after_generic)] +#[diag("trailing attribute after generic parameter")] pub(crate) struct AttrAfterGeneric { #[primary_span] - #[label] + #[label("attributes must go before parameters")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_attr_without_generics)] +#[diag("attribute without generic parameters")] pub(crate) struct AttrWithoutGenerics { #[primary_span] - #[label] + #[label("attributes are only permitted when preceding parameters")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_where_generics)] +#[diag("generic parameters on `where` clauses are reserved for future use")] pub(crate) struct WhereOnGenerics { #[primary_span] - #[label] + #[label("currently unsupported")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_generics_in_path)] +#[diag("unexpected generic arguments in path")] pub(crate) struct GenericsInPath { #[primary_span] pub span: Vec, } #[derive(Diagnostic)] -#[diag(parse_lifetime_in_eq_constraint)] -#[help] +#[diag("lifetimes are not permitted in this context")] +#[help("if you meant to specify a trait object, write `dyn /* Trait */ + {$lifetime}`")] pub(crate) struct LifetimeInEqConstraint { #[primary_span] - #[label] + #[label("lifetime is not allowed here")] pub span: Span, pub lifetime: Ident, - #[label(parse_context_label)] + #[label("this introduces an associated item binding")] pub binding_label: Span, #[suggestion( - parse_colon_sugg, + "you might have meant to write a bound here", style = "verbose", applicability = "maybe-incorrect", code = ": " @@ -3167,119 +3728,159 @@ pub(crate) struct LifetimeInEqConstraint { } #[derive(Diagnostic)] -#[diag(parse_modifier_lifetime)] +#[diag("`{$modifier}` may only modify trait bounds, not lifetime bounds")] pub(crate) struct ModifierLifetime { #[primary_span] - #[suggestion(style = "tool-only", applicability = "maybe-incorrect", code = "")] + #[suggestion( + "remove the `{$modifier}`", + style = "tool-only", + applicability = "maybe-incorrect", + code = "" + )] pub span: Span, pub modifier: &'static str, } #[derive(Diagnostic)] -#[diag(parse_underscore_literal_suffix)] +#[diag("underscore literal suffix is not allowed")] pub(crate) struct UnderscoreLiteralSuffix { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_expect_label_found_ident)] +#[diag("expected a label, found an identifier")] pub(crate) struct ExpectedLabelFoundIdent { #[primary_span] pub span: Span, - #[suggestion(code = "'", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "labels start with a tick", + code = "'", + applicability = "machine-applicable", + style = "verbose" + )] pub start: Span, } #[derive(Diagnostic)] -#[diag(parse_inappropriate_default)] -#[note] +#[diag("{$article} {$descr} cannot be `default`")] +#[note("only associated `fn`, `const`, and `type` items can be `default`")] pub(crate) struct InappropriateDefault { #[primary_span] - #[label] + #[label("`default` because of this")] pub span: Span, pub article: &'static str, pub descr: &'static str, } #[derive(Diagnostic)] -#[diag(parse_recover_import_as_use)] +#[diag("expected item, found {$token_name}")] pub(crate) struct RecoverImportAsUse { #[primary_span] - #[suggestion(code = "use", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "items are imported using the `use` keyword", + code = "use", + applicability = "machine-applicable", + style = "verbose" + )] pub span: Span, pub token_name: String, } #[derive(Diagnostic)] -#[diag(parse_single_colon_import_path)] -#[note] +#[diag("expected `::`, found `:`")] +#[note("import paths are delimited using `::`")] pub(crate) struct SingleColonImportPath { #[primary_span] - #[suggestion(code = "::", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "use double colon", + code = "::", + applicability = "machine-applicable", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_bad_item_kind)] +#[diag("{$descr} is not supported in {$ctx}")] pub(crate) struct BadItemKind { #[primary_span] pub span: Span, pub descr: &'static str, pub ctx: &'static str, - #[help] + #[help("consider moving the {$descr} out to a nearby module scope")] pub help: bool, } #[derive(Diagnostic)] -#[diag(parse_macro_rules_missing_bang)] +#[diag("expected `!` after `macro_rules`")] pub(crate) struct MacroRulesMissingBang { #[primary_span] pub span: Span, - #[suggestion(code = "!", applicability = "machine-applicable", style = "verbose")] + #[suggestion("add a `!`", code = "!", applicability = "machine-applicable", style = "verbose")] pub hi: Span, } #[derive(Diagnostic)] -#[diag(parse_macro_name_remove_bang)] +#[diag("macro names aren't followed by a `!`")] pub(crate) struct MacroNameRemoveBang { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "short")] + #[suggestion( + "remove the `!`", + code = "", + applicability = "machine-applicable", + style = "short" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_macro_rules_visibility)] +#[diag("can't qualify macro_rules invocation with `{$vis}`")] pub(crate) struct MacroRulesVisibility<'a> { #[primary_span] - #[suggestion(code = "#[macro_export]", applicability = "maybe-incorrect", style = "verbose")] + #[suggestion( + "try exporting the macro", + code = "#[macro_export]", + applicability = "maybe-incorrect", + style = "verbose" + )] pub span: Span, pub vis: &'a str, } #[derive(Diagnostic)] -#[diag(parse_macro_invocation_visibility)] -#[help] +#[diag("can't qualify macro invocation with `pub`")] +#[help("try adjusting the macro to put `{$vis}` inside the invocation")] pub(crate) struct MacroInvocationVisibility<'a> { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "remove the visibility", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] pub span: Span, pub vis: &'a str, } #[derive(Diagnostic)] -#[diag(parse_nested_adt)] +#[diag("`{$kw_str}` definition cannot be nested inside `{$keyword}`")] pub(crate) struct NestedAdt<'a> { #[primary_span] pub span: Span, - #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] + #[suggestion( + "consider creating a new `{$kw_str}` definition instead of nesting", + code = "", + applicability = "maybe-incorrect", + style = "verbose" + )] pub item: Span, pub keyword: &'a str, pub kw_str: Cow<'a, str>, } #[derive(Diagnostic)] -#[diag(parse_function_body_equals_expr)] +#[diag("function body cannot be `= expression;`")] pub(crate) struct FunctionBodyEqualsExpr { #[primary_span] pub span: Span, @@ -3288,7 +3889,10 @@ pub(crate) struct FunctionBodyEqualsExpr { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + r#"surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;`"#, + applicability = "machine-applicable" +)] pub(crate) struct FunctionBodyEqualsExprSugg { #[suggestion_part(code = "{{")] pub eq: Span, @@ -3297,38 +3901,56 @@ pub(crate) struct FunctionBodyEqualsExprSugg { } #[derive(Diagnostic)] -#[diag(parse_box_not_pat)] +#[diag("expected pattern, found {$descr}")] pub(crate) struct BoxNotPat { #[primary_span] pub span: Span, - #[note] + #[note("`box` is a reserved keyword")] pub kw: Span, - #[suggestion(code = "r#", applicability = "maybe-incorrect", style = "verbose")] + #[suggestion( + "escape `box` to use it as an identifier", + code = "r#", + applicability = "maybe-incorrect", + style = "verbose" + )] pub lo: Span, pub descr: String, } #[derive(Diagnostic)] -#[diag(parse_unmatched_angle)] +#[diag( + "unmatched angle {$plural -> + [true] brackets + *[false] bracket + }" +)] pub(crate) struct UnmatchedAngle { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + #[suggestion( + "remove extra angle {$plural -> + [true] brackets + *[false] bracket + }", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] pub span: Span, pub plural: bool, } #[derive(Diagnostic)] -#[diag(parse_missing_plus_in_bounds)] +#[diag("expected `+` between lifetime and {$sym}")] pub(crate) struct MissingPlusBounds { #[primary_span] pub span: Span, - #[suggestion(code = " +", applicability = "maybe-incorrect", style = "verbose")] + #[suggestion("add `+`", code = " +", applicability = "maybe-incorrect", style = "verbose")] pub hi: Span, pub sym: Symbol, } #[derive(Diagnostic)] -#[diag(parse_incorrect_parens_trait_bounds)] +#[diag("incorrect parentheses around trait bounds")] pub(crate) struct IncorrectParensTraitBounds { #[primary_span] pub span: Vec, @@ -3337,10 +3959,7 @@ pub(crate) struct IncorrectParensTraitBounds { } #[derive(Subdiagnostic)] -#[multipart_suggestion( - parse_incorrect_parens_trait_bounds_sugg, - applicability = "machine-applicable" -)] +#[multipart_suggestion("fix the parentheses", applicability = "machine-applicable")] pub(crate) struct IncorrectParensTraitBoundsSugg { #[suggestion_part(code = " ")] pub wrong_span: Span, @@ -3349,10 +3968,15 @@ pub(crate) struct IncorrectParensTraitBoundsSugg { } #[derive(Diagnostic)] -#[diag(parse_kw_bad_case)] +#[diag("keyword `{$kw}` is written in the wrong case")] pub(crate) struct KwBadCase<'a> { #[primary_span] - #[suggestion(code = "{kw}", style = "verbose", applicability = "machine-applicable")] + #[suggestion( + "write it in {$case}", + code = "{kw}", + style = "verbose", + applicability = "machine-applicable" + )] pub span: Span, pub kw: &'a str, pub case: Case, @@ -3376,7 +4000,7 @@ impl IntoDiagArg for Case { } #[derive(Diagnostic)] -#[diag(parse_unknown_builtin_construct)] +#[diag("unknown `builtin #` construct `{$name}`")] pub(crate) struct UnknownBuiltinConstruct { #[primary_span] pub span: Span, @@ -3384,35 +4008,38 @@ pub(crate) struct UnknownBuiltinConstruct { } #[derive(Diagnostic)] -#[diag(parse_expected_builtin_ident)] +#[diag("expected identifier after `builtin #`")] pub(crate) struct ExpectedBuiltinIdent { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_static_with_generics)] +#[diag("static items may not have generic parameters")] pub(crate) struct StaticWithGenerics { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_where_clause_before_const_body)] +#[diag("where clauses are not allowed before const item bodies")] pub(crate) struct WhereClauseBeforeConstBody { #[primary_span] - #[label] + #[label("unexpected where clause")] pub span: Span, - #[label(parse_name_label)] + #[label("while parsing this const item")] pub name: Span, - #[label(parse_body_label)] + #[label("the item body")] pub body: Span, #[subdiagnostic] pub sugg: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + "move the body before the where clause", + applicability = "machine-applicable" +)] pub(crate) struct WhereClauseBeforeConstBodySugg { #[suggestion_part(code = "= {snippet} ")] pub left: Span, @@ -3422,12 +4049,12 @@ pub(crate) struct WhereClauseBeforeConstBodySugg { } #[derive(Diagnostic)] -#[diag(parse_generic_args_in_pat_require_turbofish_syntax)] +#[diag("generic args in patterns require the turbofish syntax")] pub(crate) struct GenericArgsInPatRequireTurbofishSyntax { #[primary_span] pub span: Span, #[suggestion( - parse_sugg_turbofish_syntax, + "use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments", style = "verbose", code = "::", applicability = "maybe-incorrect" @@ -3436,7 +4063,7 @@ pub(crate) struct GenericArgsInPatRequireTurbofishSyntax { } #[derive(Diagnostic)] -#[diag(parse_transpose_dyn_or_impl)] +#[diag("`for<...>` expected after `{$kw}`, not before")] pub(crate) struct TransposeDynOrImpl<'a> { #[primary_span] pub span: Span, @@ -3446,7 +4073,7 @@ pub(crate) struct TransposeDynOrImpl<'a> { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion("move `{$kw}` before the `for<...>`", applicability = "machine-applicable")] pub(crate) struct TransposeDynOrImplSugg<'a> { #[suggestion_part(code = "")] pub removal_span: Span, @@ -3456,68 +4083,77 @@ pub(crate) struct TransposeDynOrImplSugg<'a> { } #[derive(Diagnostic)] -#[diag(parse_array_index_offset_of)] +#[diag("array indexing not supported in offset_of")] pub(crate) struct ArrayIndexInOffsetOf(#[primary_span] pub Span); #[derive(Diagnostic)] -#[diag(parse_invalid_offset_of)] +#[diag("offset_of expects dot-separated field and variant names")] pub(crate) struct InvalidOffsetOf(#[primary_span] pub Span); #[derive(Diagnostic)] -#[diag(parse_async_impl)] +#[diag("`async` trait implementations are unsupported")] pub(crate) struct AsyncImpl { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_expr_rarrow_call)] -#[help] +#[diag("`->` is not valid syntax for field accesses and method calls")] +#[help( + "the `.` operator will automatically dereference the value, except if the value is a raw pointer" +)] pub(crate) struct ExprRArrowCall { #[primary_span] - #[suggestion(style = "verbose", applicability = "machine-applicable", code = ".")] + #[suggestion( + "try using `.` instead", + style = "verbose", + applicability = "machine-applicable", + code = "." + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_dot_dot_range_attribute)] +#[diag("attributes are not allowed on range expressions starting with `..`")] pub(crate) struct DotDotRangeAttribute { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_binder_before_modifiers)] +#[diag("`for<...>` binder should be placed before trait bound modifiers")] pub(crate) struct BinderBeforeModifiers { #[primary_span] pub binder_span: Span, - #[label] + #[label("place the `for<...>` binder before any modifiers")] pub modifiers_span: Span, } #[derive(Diagnostic)] -#[diag(parse_binder_and_polarity)] +#[diag("`for<...>` binder not allowed with `{$polarity}` trait polarity modifier")] pub(crate) struct BinderAndPolarity { #[primary_span] pub polarity_span: Span, - #[label] + #[label("there is not a well-defined meaning for a higher-ranked `{$polarity}` trait")] pub binder_span: Span, pub polarity: &'static str, } #[derive(Diagnostic)] -#[diag(parse_modifiers_and_polarity)] +#[diag("`{$modifiers_concatenated}` trait not allowed with `{$polarity}` trait polarity modifier")] pub(crate) struct PolarityAndModifiers { #[primary_span] pub polarity_span: Span, - #[label] + #[label( + "there is not a well-defined meaning for a `{$modifiers_concatenated} {$polarity}` trait" + )] pub modifiers_span: Span, pub polarity: &'static str, pub modifiers_concatenated: String, } #[derive(Diagnostic)] -#[diag(parse_incorrect_type_on_self)] +#[diag("type not allowed for shorthand `self` parameter")] pub(crate) struct IncorrectTypeOnSelf { #[primary_span] pub span: Span, @@ -3526,7 +4162,10 @@ pub(crate) struct IncorrectTypeOnSelf { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +#[multipart_suggestion( + "move the modifiers on `self` to the type", + applicability = "machine-applicable" +)] pub(crate) struct MoveSelfModifier { #[suggestion_part(code = "")] pub removal_span: Span, @@ -3536,82 +4175,104 @@ pub(crate) struct MoveSelfModifier { } #[derive(Diagnostic)] -#[diag(parse_asm_unsupported_operand)] +#[diag("the `{$symbol}` operand cannot be used with `{$macro_name}!`")] pub(crate) struct AsmUnsupportedOperand<'a> { #[primary_span] - #[label] + #[label( + "the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it" + )] pub(crate) span: Span, pub(crate) symbol: &'a str, pub(crate) macro_name: &'static str, } #[derive(Diagnostic)] -#[diag(parse_asm_underscore_input)] +#[diag("_ cannot be used for input operands")] pub(crate) struct AsmUnderscoreInput { #[primary_span] pub(crate) span: Span, } #[derive(Diagnostic)] -#[diag(parse_asm_sym_no_path)] +#[diag("expected a path for argument to `sym`")] pub(crate) struct AsmSymNoPath { #[primary_span] pub(crate) span: Span, } #[derive(Diagnostic)] -#[diag(parse_asm_requires_template)] +#[diag("requires at least a template string argument")] pub(crate) struct AsmRequiresTemplate { #[primary_span] pub(crate) span: Span, } #[derive(Diagnostic)] -#[diag(parse_asm_expected_comma)] +#[diag("expected token: `,`")] pub(crate) struct AsmExpectedComma { #[primary_span] - #[label] + #[label("expected `,`")] pub(crate) span: Span, } #[derive(Diagnostic)] -#[diag(parse_asm_expected_other)] +#[diag( + "expected operand, {$is_inline_asm -> + [false] options + *[true] clobber_abi, options + }, or additional template string" +)] pub(crate) struct AsmExpectedOther { #[primary_span] - #[label(parse_asm_expected_other)] + #[label( + "expected operand, {$is_inline_asm -> + [false] options + *[true] clobber_abi, options + }, or additional template string" + )] pub(crate) span: Span, pub(crate) is_inline_asm: bool, } #[derive(Diagnostic)] -#[diag(parse_asm_non_abi)] +#[diag("at least one abi must be provided as an argument to `clobber_abi`")] pub(crate) struct NonABI { #[primary_span] pub(crate) span: Span, } #[derive(Diagnostic)] -#[diag(parse_asm_expected_string_literal)] +#[diag("expected string literal")] pub(crate) struct AsmExpectedStringLiteral { #[primary_span] - #[label] + #[label("not a string literal")] pub(crate) span: Span, } #[derive(Diagnostic)] -#[diag(parse_asm_expected_register_class_or_explicit_register)] +#[diag("expected register class or explicit register")] pub(crate) struct ExpectedRegisterClassOrExplicitRegister { #[primary_span] pub(crate) span: Span, } #[derive(LintDiagnostic)] -#[diag(parse_hidden_unicode_codepoints)] -#[note] +#[diag("unicode codepoint changing visible direction of text present in {$label}")] +#[note( + "these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen" +)] pub(crate) struct HiddenUnicodeCodepointsDiag { pub label: String, pub count: usize, - #[label] + #[label( + "this {$label} contains {$count -> + [one] an invisible + *[other] invisible + } unicode text flow control {$count -> + [one] codepoint + *[other] codepoints + }" + )] pub span_label: Span, #[subdiagnostic] pub labels: Option, @@ -3642,13 +4303,13 @@ impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub { match self { HiddenUnicodeCodepointsDiagSub::Escape { spans } => { diag.multipart_suggestion_with_style( - fluent::parse_suggestion_remove, + inline_fluent!("if their presence wasn't intentional, you can remove them"), spans.iter().map(|(_, span)| (*span, "".to_string())).collect(), Applicability::MachineApplicable, SuggestionStyle::HideCodeAlways, ); diag.multipart_suggestion( - fluent::parse_suggestion_escape, + inline_fluent!("if you want to keep them but make them visible in your source code, you can escape them"), spans .into_iter() .map(|(c, span)| { @@ -3671,42 +4332,122 @@ impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub { .collect::>() .join(", "), ); - diag.note(fluent::parse_suggestion_remove); - diag.note(fluent::parse_no_suggestion_note_escape); + diag.note(inline_fluent!( + "if their presence wasn't intentional, you can remove them" + )); + diag.note(inline_fluent!("if you want to keep them but make them visible in your source code, you can escape them: {$escaped}")); } } } } #[derive(LintDiagnostic)] -#[diag(parse_varargs_without_pattern)] +#[diag("missing pattern for `...` argument")] pub(crate) struct VarargsWithoutPattern { - #[suggestion(code = "_: ...", applicability = "machine-applicable")] + #[suggestion( + "name the argument, or use `_` to continue ignoring it", + code = "_: ...", + applicability = "machine-applicable" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_delegation_non_trait_impl_reuse)] +#[diag("only trait impls can be reused")] pub(crate) struct ImplReuseInherentImpl { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_struct_literal_placeholder_path)] +#[diag("placeholder `_` is not allowed for the path in struct literals")] pub(crate) struct StructLiteralPlaceholderPath { #[primary_span] - #[label] - #[suggestion(applicability = "has-placeholders", code = "/* Type */", style = "verbose")] + #[label("not allowed in struct literals")] + #[suggestion( + "replace it with the correct type", + applicability = "has-placeholders", + code = "/* Type */", + style = "verbose" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(parse_struct_literal_body_without_path_late)] +#[diag("struct literal body without path")] pub(crate) struct StructLiteralWithoutPathLate { #[primary_span] - #[label] + #[label("struct name missing for struct literal")] pub span: Span, - #[suggestion(applicability = "has-placeholders", code = "/* Type */ ", style = "verbose")] + #[suggestion( + "add the correct type", + applicability = "has-placeholders", + code = "/* Type */ ", + style = "verbose" + )] pub suggestion_span: Span, } + +/// Used to forbid `let` expressions in certain syntactic locations. +#[derive(Clone, Copy, Subdiagnostic)] +pub(crate) enum ForbiddenLetReason { + /// `let` is not valid and the source environment is not important + OtherForbidden, + /// A let chain with the `||` operator + #[note("`||` operators are not supported in let chain expressions")] + NotSupportedOr(#[primary_span] Span), + /// A let chain with invalid parentheses + /// + /// For example, `let 1 = 1 && (expr && expr)` is allowed + /// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not + #[note("`let`s wrapped in parentheses are not supported in a context with let chains")] + NotSupportedParentheses(#[primary_span] Span), +} + +#[derive(Debug, rustc_macros::Subdiagnostic)] +#[suggestion( + "{$is_incorrect_case -> + [true] write keyword `{$similar_kw}` in lowercase + *[false] there is a keyword `{$similar_kw}` with a similar name + }", + applicability = "machine-applicable", + code = "{similar_kw}", + style = "verbose" +)] +pub(crate) struct MisspelledKw { + // We use a String here because `Symbol::into_diag_arg` calls `Symbol::to_ident_string`, which + // prefix the keyword with a `r#` because it aims to print the symbol as an identifier. + pub similar_kw: String, + #[primary_span] + pub span: Span, + pub is_incorrect_case: bool, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(super) enum TokenDescription { + ReservedIdentifier, + Keyword, + ReservedKeyword, + DocComment, + + // Expanded metavariables are wrapped in invisible delimiters which aren't + // pretty-printed. In error messages we must handle these specially + // otherwise we get confusing things in messages like "expected `(`, found + // ``". It's better to say e.g. "expected `(`, found type metavariable". + MetaVar(MetaVarKind), +} + +impl TokenDescription { + pub(super) fn from_token(token: &Token) -> Option { + match token.kind { + _ if token.is_special_ident() => Some(TokenDescription::ReservedIdentifier), + _ if token.is_used_keyword() => Some(TokenDescription::Keyword), + _ if token.is_unused_keyword() => Some(TokenDescription::ReservedKeyword), + token::DocComment(..) => Some(TokenDescription::DocComment), + token::OpenInvisible(InvisibleOrigin::MetaVar(kind)) => { + Some(TokenDescription::MetaVar(kind)) + } + _ => None, + } + } +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 76f610df1eb0..cd90655125b2 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -459,8 +459,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> { span: self.mk_sp(start, self.pos + Pos::from_usize(repeats * c.len_utf8())), escaped: escaped_char(c), sugg, - null: if c == '\x00' { Some(errors::UnknownTokenNull) } else { None }, - invisible: if INVISIBLE_CHARACTERS.contains(&c) { Some(errors::InvisibleCharacter) } else { None }, + null: c == '\x00', + invisible: INVISIBLE_CHARACTERS.contains(&c), repeat: if repeats > 0 { swallow_next_invalid = repeats; Some(errors::UnknownTokenRepeat { repeats }) diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index c836d52a71e4..014ccdc45ef8 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -1,7 +1,7 @@ //! The main parser interface. // tidy-alphabetical-start -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(box_patterns)] #![feature(debug_closure_helpers)] #![feature(default_field_values)] @@ -75,8 +75,6 @@ const _: () = { } }; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - // Unwrap the result if `Ok`, otherwise emit the diagnostics and abort. pub fn unwrap_or_emit_fatal(expr: Result>>) -> T { match expr { diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index fe8c1deedff7..3d16f37c000e 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -3,7 +3,7 @@ use rustc_ast::token::{self, MetaVarKind}; use rustc_ast::tokenstream::ParserRange; use rustc_ast::{AttrItemKind, Attribute, attr}; use rustc_errors::codes::*; -use rustc_errors::{Diag, PResult}; +use rustc_errors::{Diag, PResult, inline_fluent}; use rustc_span::{BytePos, Span}; use thin_vec::ThinVec; use tracing::debug; @@ -13,7 +13,7 @@ use super::{ Trailing, UsePreAttrPos, }; use crate::parser::FnContext; -use crate::{errors, exp, fluent_generated as fluent}; +use crate::{errors, exp}; // Public for rustfmt usage #[derive(Debug)] @@ -68,7 +68,7 @@ impl<'a> Parser<'a> { let span = self.token.span; let mut err = self .dcx() - .struct_span_err(span, fluent::parse_inner_doc_comment_not_permitted); + .struct_span_err(span, inline_fluent!("expected outer doc comment")); err.code(E0753); if let Some(replacement_span) = self.annotate_following_item_if_applicable( &mut err, @@ -79,10 +79,12 @@ impl<'a> Parser<'a> { }, true, ) { - err.note(fluent::parse_note); + err.note(inline_fluent!( + "inner doc comments like this (starting with `//!` or `/*!`) can only appear before items" + )); err.span_suggestion_verbose( replacement_span, - fluent::parse_suggestion, + inline_fluent!("you might have meant to write a regular comment"), "", rustc_errors::Applicability::MachineApplicable, ); @@ -207,13 +209,25 @@ impl<'a> Parser<'a> { AllowConstBlockItems::Yes, ) { Ok(Some(item)) => { - // FIXME(#100717) err.arg("item", item.kind.descr()); - err.span_label(item.span, fluent::parse_label_does_not_annotate_this); + err.span_label( + item.span, + match attr_type { + OuterAttributeType::Attribute => { + inline_fluent!("the inner attribute doesn't annotate this {$item}") + } + OuterAttributeType::DocComment | OuterAttributeType::DocBlockComment => { + inline_fluent!("the inner doc comment doesn't annotate this {$item}") + } + }, + ); if suggest_to_outer { err.span_suggestion_verbose( replacement_span, - fluent::parse_sugg_change_inner_to_outer, + match attr_type { + OuterAttributeType::Attribute => inline_fluent!("to annotate the {$item}, change the attribute from inner to outer style"), + OuterAttributeType::DocComment | OuterAttributeType::DocBlockComment => inline_fluent!("to annotate the {$item}, change the doc comment from inner to outer style"), + }, match attr_type { OuterAttributeType::Attribute => "", OuterAttributeType::DocBlockComment => "*", @@ -244,28 +258,42 @@ impl<'a> Parser<'a> { self.dcx() .struct_span_err( attr_sp, - fluent::parse_inner_attr_not_permitted_after_outer_doc_comment, + inline_fluent!( + "an inner attribute is not permitted following an outer doc comment" + ), + ) + .with_span_label( + attr_sp, + inline_fluent!("not permitted following an outer doc comment"), ) - .with_span_label(attr_sp, fluent::parse_label_attr) .with_span_label( prev_doc_comment_span, - fluent::parse_label_prev_doc_comment, + inline_fluent!("previous doc comment"), ) } Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }) => self .dcx() .struct_span_err( attr_sp, - fluent::parse_inner_attr_not_permitted_after_outer_attr, + inline_fluent!( + "an inner attribute is not permitted following an outer attribute" + ), ) - .with_span_label(attr_sp, fluent::parse_label_attr) - .with_span_label(prev_outer_attr_sp, fluent::parse_label_prev_attr), - Some(InnerAttrForbiddenReason::InCodeBlock) | None => { - self.dcx().struct_span_err(attr_sp, fluent::parse_inner_attr_not_permitted) - } + .with_span_label( + attr_sp, + inline_fluent!("not permitted following an outer attribute"), + ) + .with_span_label( + prev_outer_attr_sp, + inline_fluent!("previous outer attribute"), + ), + Some(InnerAttrForbiddenReason::InCodeBlock) | None => self.dcx().struct_span_err( + attr_sp, + inline_fluent!("an inner attribute is not permitted in this context"), + ), }; - diag.note(fluent::parse_inner_attr_explanation); + diag.note(inline_fluent!("inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files")); if self .annotate_following_item_if_applicable( &mut diag, @@ -275,7 +303,9 @@ impl<'a> Parser<'a> { ) .is_some() { - diag.note(fluent::parse_outer_attr_explanation); + diag.note(inline_fluent!( + "outer attributes, like `#[test]`, annotate the item following them" + )); }; diag.emit(); } diff --git a/compiler/rustc_parse/src/parser/cfg_select.rs b/compiler/rustc_parse/src/parser/cfg_select.rs index 9692d2c70893..b12209f0b92e 100644 --- a/compiler/rustc_parse/src/parser/cfg_select.rs +++ b/compiler/rustc_parse/src/parser/cfg_select.rs @@ -7,14 +7,19 @@ use crate::exp; use crate::parser::{AttrWrapper, ForceCollect, Parser, Restrictions, Trailing, UsePreAttrPos}; impl<'a> Parser<'a> { - /// Parses a `TokenTree` consisting either of `{ /* ... */ }` (and strip the braces) or an - /// expression followed by a comma (and strip the comma). + /// Parses a `TokenTree` consisting either of `{ /* ... */ }` optionally followed by a comma + /// (and strip the braces and the optional comma) or an expression followed by a comma + /// (and strip the comma). pub fn parse_delimited_token_tree(&mut self) -> PResult<'a, TokenStream> { if self.token == token::OpenBrace { // Strip the outer '{' and '}'. match self.parse_token_tree() { - TokenTree::Token(..) => unreachable!("because of the expect above"), - TokenTree::Delimited(.., tts) => return Ok(tts), + TokenTree::Token(..) => unreachable!("because the current token is a '{{'"), + TokenTree::Delimited(.., tts) => { + // Optionally end with a comma. + let _ = self.eat(exp!(Comma)); + return Ok(tts); + } } } let expr = self.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |p, _| { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 60e12fa05adf..c7eb1b9b5638 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -13,7 +13,7 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, PResult, Subdiagnostic, Suggestions, - pluralize, + inline_fluent, pluralize, }; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; @@ -35,16 +35,16 @@ use crate::errors::{ ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, - PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, - StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, - SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, TernaryOperatorSuggestion, - UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, + MisspelledKw, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, + SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, + SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, + TernaryOperatorSuggestion, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType, }; +use crate::exp; use crate::parser::FnContext; use crate::parser::attr::InnerAttrPolicy; use crate::parser::item::IsDotDotDot; -use crate::{exp, fluent_generated as fluent}; /// Creates a placeholder argument. pub(super) fn dummy_arg(ident: Ident, guar: ErrorGuaranteed) -> Param { @@ -212,22 +212,6 @@ impl std::fmt::Display for UnaryFixity { } } -#[derive(Debug, rustc_macros::Subdiagnostic)] -#[suggestion( - parse_misspelled_kw, - applicability = "machine-applicable", - code = "{similar_kw}", - style = "verbose" -)] -struct MisspelledKw { - // We use a String here because `Symbol::into_diag_arg` calls `Symbol::to_ident_string`, which - // prefix the keyword with a `r#` because it aims to print the symbol as an identifier. - similar_kw: String, - #[primary_span] - span: Span, - is_incorrect_case: bool, -} - /// Checks if the given `lookup` identifier is similar to any keyword symbol in `candidates`. /// /// This is a specialized version of [`Symbol::find_similar`] that constructs an error when a @@ -1288,7 +1272,7 @@ impl<'a> Parser<'a> { // We made sense of it. Improve the error message. e.span_suggestion_verbose( binop.span.shrink_to_lo(), - fluent::parse_sugg_turbofish_syntax, + inline_fluent!("use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments"), "::", Applicability::MaybeIncorrect, ); @@ -3039,27 +3023,53 @@ impl<'a> Parser<'a> { long_kind: &TokenKind, short_kind: &TokenKind, ) -> bool { - (0..3).all(|i| self.look_ahead(i, |tok| tok == long_kind)) - && self.look_ahead(3, |tok| tok == short_kind) + if long_kind == short_kind { + // For conflict marker chars like `%` and `\`. + (0..7).all(|i| self.look_ahead(i, |tok| tok == long_kind)) + } else { + // For conflict marker chars like `<` and `|`. + (0..3).all(|i| self.look_ahead(i, |tok| tok == long_kind)) + && self.look_ahead(3, |tok| tok == short_kind || tok == long_kind) + } } - fn conflict_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> Option { + fn conflict_marker( + &mut self, + long_kind: &TokenKind, + short_kind: &TokenKind, + expected: Option, + ) -> Option<(Span, usize)> { if self.is_vcs_conflict_marker(long_kind, short_kind) { let lo = self.token.span; - for _ in 0..4 { - self.bump(); + if self.psess.source_map().span_to_margin(lo) != Some(0) { + return None; } - return Some(lo.to(self.prev_token.span)); + let mut len = 0; + while self.token.kind == *long_kind || self.token.kind == *short_kind { + if self.token.kind.break_two_token_op(1).is_some() { + len += 2; + } else { + len += 1; + } + self.bump(); + if expected == Some(len) { + break; + } + } + if expected.is_some() && expected != Some(len) { + return None; + } + return Some((lo.to(self.prev_token.span), len)); } None } pub(super) fn recover_vcs_conflict_marker(&mut self) { // <<<<<<< - let Some(start) = self.conflict_marker(&TokenKind::Shl, &TokenKind::Lt) else { + let Some((start, len)) = self.conflict_marker(&TokenKind::Shl, &TokenKind::Lt, None) else { return; }; - let mut spans = Vec::with_capacity(3); + let mut spans = Vec::with_capacity(2); spans.push(start); // ||||||| let mut middlediff3 = None; @@ -3071,13 +3081,19 @@ impl<'a> Parser<'a> { if self.token == TokenKind::Eof { break; } - if let Some(span) = self.conflict_marker(&TokenKind::OrOr, &TokenKind::Or) { + if let Some((span, _)) = + self.conflict_marker(&TokenKind::OrOr, &TokenKind::Or, Some(len)) + { middlediff3 = Some(span); } - if let Some(span) = self.conflict_marker(&TokenKind::EqEq, &TokenKind::Eq) { + if let Some((span, _)) = + self.conflict_marker(&TokenKind::EqEq, &TokenKind::Eq, Some(len)) + { middle = Some(span); } - if let Some(span) = self.conflict_marker(&TokenKind::Shr, &TokenKind::Gt) { + if let Some((span, _)) = + self.conflict_marker(&TokenKind::Shr, &TokenKind::Gt, Some(len)) + { spans.push(span); end = Some(span); break; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 44c6acec8866..5c44bfb0cf3f 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -21,7 +21,6 @@ use rustc_ast::{ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic}; use rustc_literal_escaper::unescape_char; -use rustc_macros::Subdiagnostic; use rustc_session::errors::{ExprParenthesesNeeded, report_lit_error}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; @@ -728,25 +727,26 @@ impl<'a> Parser<'a> { ); let args_span = self.look_ahead(1, |t| t.span).to(span_after_type); - let suggestion = errors::ComparisonOrShiftInterpretedAsGenericSugg { - left: expr.span.shrink_to_lo(), - right: expr.span.shrink_to_hi(), - }; - match self.token.kind { token::Lt => { self.dcx().emit_err(errors::ComparisonInterpretedAsGeneric { comparison: self.token.span, r#type: path, args: args_span, - suggestion, + suggestion: errors::ComparisonInterpretedAsGenericSugg { + left: expr.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }, }) } token::Shl => self.dcx().emit_err(errors::ShiftInterpretedAsGeneric { shift: self.token.span, r#type: path, args: args_span, - suggestion, + suggestion: errors::ShiftInterpretedAsGenericSugg { + left: expr.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }, }), _ => { // We can end up here even without `<` being the next token, for @@ -1524,7 +1524,7 @@ impl<'a> Parser<'a> { }, ) } else if this.check_inline_const(0) { - this.parse_const_block(lo) + this.parse_const_block(lo, false) } else if this.may_recover() && this.is_do_catch_block() { this.recover_do_catch() } else if this.is_try_block() { @@ -1628,16 +1628,8 @@ impl<'a> Parser<'a> { let first_expr = self.parse_expr()?; if self.eat(exp!(Semi)) { // Repeating array syntax: `[ 0; 512 ]` - let count = if self.eat_keyword(exp!(Const)) { - // While we could just disambiguate `Direct` from `AnonConst` by - // treating all const block exprs as `AnonConst`, that would - // complicate the DefCollector and likely all other visitors. - // So we strip the const blockiness and just store it as a block - // in the AST with the extra disambiguator on the AnonConst - self.parse_mgca_const_block(false)? - } else { - self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))? - }; + let count = + self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?; self.expect(close)?; ExprKind::Repeat(first_expr, count) } else if self.eat(exp!(Comma)) { @@ -2774,7 +2766,7 @@ impl<'a> Parser<'a> { let recovered = if !restrictions.contains(Restrictions::ALLOW_LET) { let err = errors::ExpectedExpressionFoundLet { span: self.token.span, - reason: ForbiddenLetReason::OtherForbidden, + reason: errors::ForbiddenLetReason::OtherForbidden, missing_let: None, comparison: None, }; @@ -4189,22 +4181,6 @@ pub(crate) fn could_be_unclosed_char_literal(ident: Ident) -> bool { && unescape_char(ident.without_first_quote().name.as_str()).is_ok() } -/// Used to forbid `let` expressions in certain syntactic locations. -#[derive(Clone, Copy, Subdiagnostic)] -pub(crate) enum ForbiddenLetReason { - /// `let` is not valid and the source environment is not important - OtherForbidden, - /// A let chain with the `||` operator - #[note(parse_not_supported_or)] - NotSupportedOr(#[primary_span] Span), - /// A let chain with invalid parentheses - /// - /// For example, `let 1 = 1 && (expr && expr)` is allowed - /// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not - #[note(parse_not_supported_parentheses)] - NotSupportedParentheses(#[primary_span] Span), -} - /// Whether let chains are allowed on all editions, or it's edition dependent (allowed only on /// 2024 and later). In case of edition dependence, specify the currently present edition. pub enum LetChainsPolicy { @@ -4225,7 +4201,7 @@ struct CondChecker<'a> { parser: &'a Parser<'a>, let_chains_policy: LetChainsPolicy, depth: u32, - forbid_let_reason: Option, + forbid_let_reason: Option, missing_let: Option, comparison: Option, found_incorrect_let_chain: Option, @@ -4248,14 +4224,13 @@ impl<'a> CondChecker<'a> { impl MutVisitor for CondChecker<'_> { fn visit_expr(&mut self, e: &mut Expr) { self.depth += 1; - use ForbiddenLetReason::*; let span = e.span; match e.kind { ExprKind::Let(_, _, _, ref mut recovered @ Recovered::No) => { if let Some(reason) = self.forbid_let_reason { let error = match reason { - NotSupportedOr(or_span) => { + errors::ForbiddenLetReason::NotSupportedOr(or_span) => { self.parser.dcx().emit_err(errors::OrInLetChain { span: or_span }) } _ => { @@ -4289,24 +4264,72 @@ impl MutVisitor for CondChecker<'_> { mut_visit::walk_expr(self, e); } ExprKind::Binary(Spanned { node: BinOpKind::Or, span: or_span }, _, _) - if let None | Some(NotSupportedOr(_)) = self.forbid_let_reason => + if let None | Some(errors::ForbiddenLetReason::NotSupportedOr(_)) = + self.forbid_let_reason => { let forbid_let_reason = self.forbid_let_reason; - self.forbid_let_reason = Some(NotSupportedOr(or_span)); + self.forbid_let_reason = Some(errors::ForbiddenLetReason::NotSupportedOr(or_span)); mut_visit::walk_expr(self, e); self.forbid_let_reason = forbid_let_reason; } ExprKind::Paren(ref inner) - if let None | Some(NotSupportedParentheses(_)) = self.forbid_let_reason => + if let None | Some(errors::ForbiddenLetReason::NotSupportedParentheses(_)) = + self.forbid_let_reason => { let forbid_let_reason = self.forbid_let_reason; - self.forbid_let_reason = Some(NotSupportedParentheses(inner.span)); + self.forbid_let_reason = + Some(errors::ForbiddenLetReason::NotSupportedParentheses(inner.span)); mut_visit::walk_expr(self, e); self.forbid_let_reason = forbid_let_reason; } - ExprKind::Assign(ref lhs, _, span) => { + ExprKind::Assign(ref lhs, ref rhs, span) => { + if let ExprKind::Call(_, _) = &lhs.kind { + fn get_path_from_rhs(e: &Expr) -> Option<(u32, &Path)> { + fn inner(e: &Expr, depth: u32) -> Option<(u32, &Path)> { + match &e.kind { + ExprKind::Binary(_, lhs, _) => inner(lhs, depth + 1), + ExprKind::Path(_, path) => Some((depth, path)), + _ => None, + } + } + + inner(e, 0) + } + + if let Some((depth, path)) = get_path_from_rhs(rhs) { + // For cases like if Some(_) = x && let Some(_) = y && let Some(_) = z + // This return let Some(_) = y expression + fn find_let_some(expr: &Expr) -> Option<&Expr> { + match &expr.kind { + ExprKind::Let(..) => Some(expr), + + ExprKind::Binary(op, lhs, rhs) if op.node == BinOpKind::And => { + find_let_some(lhs).or_else(|| find_let_some(rhs)) + } + + _ => None, + } + } + + let expr_span = lhs.span.to(path.span); + + if let Some(later_rhs) = find_let_some(rhs) + && depth > 0 + { + let guar = self.parser.dcx().emit_err(errors::LetChainMissingLet { + span: lhs.span, + label_span: expr_span, + rhs_span: later_rhs.span, + sug_span: lhs.span.shrink_to_lo(), + }); + + self.found_incorrect_let_chain = Some(guar); + } + } + } + let forbid_let_reason = self.forbid_let_reason; - self.forbid_let_reason = Some(OtherForbidden); + self.forbid_let_reason = Some(errors::ForbiddenLetReason::OtherForbidden); let missing_let = self.missing_let; if let ExprKind::Binary(_, _, rhs) = &lhs.kind && let ExprKind::Path(_, _) @@ -4339,7 +4362,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::Tup(_) | ExprKind::Paren(_) => { let forbid_let_reason = self.forbid_let_reason; - self.forbid_let_reason = Some(OtherForbidden); + self.forbid_let_reason = Some(errors::ForbiddenLetReason::OtherForbidden); mut_visit::walk_expr(self, e); self.forbid_let_reason = forbid_let_reason; } @@ -4347,7 +4370,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::Type(ref mut op, _) | ExprKind::UnsafeBinderCast(_, ref mut op, _) => { let forbid_let_reason = self.forbid_let_reason; - self.forbid_let_reason = Some(OtherForbidden); + self.forbid_let_reason = Some(errors::ForbiddenLetReason::OtherForbidden); self.visit_expr(op); self.forbid_let_reason = forbid_let_reason; } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 6b61504f2327..b7ba92bac524 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -6,12 +6,10 @@ use rustc_ast::ast::*; use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::util::case::Case; -use rustc_ast::{ - attr, {self as ast}, -}; +use rustc_ast::{self as ast}; use rustc_ast_pretty::pprust; use rustc_errors::codes::*; -use rustc_errors::{Applicability, PResult, StashKey, struct_span_code_err}; +use rustc_errors::{Applicability, PResult, StashKey, inline_fluent, struct_span_code_err}; use rustc_session::lint::builtin::VARARGS_WITHOUT_PATTERN; use rustc_span::edit_distance::edit_distance; use rustc_span::edition::Edition; @@ -26,7 +24,7 @@ use super::{ Parser, PathStyle, Recovered, Trailing, UsePreAttrPos, }; use crate::errors::{self, FnPointerCannotBeAsync, FnPointerCannotBeConst, MacroExpandsToAdtField}; -use crate::{exp, fluent_generated as fluent}; +use crate::exp; impl<'a> Parser<'a> { /// Parses a source module as a crate. This is the main entry point for the parser. @@ -286,13 +284,13 @@ impl<'a> Parser<'a> { // CONST ITEM self.recover_const_mut(const_span); self.recover_missing_kw_before_item()?; - let (ident, generics, ty, rhs) = self.parse_const_item(attrs)?; + let (ident, generics, ty, rhs_kind) = self.parse_const_item(false)?; ItemKind::Const(Box::new(ConstItem { defaultness: def_(), ident, generics, ty, - rhs, + rhs_kind, define_opaque: None, })) } else if let Some(kind) = self.is_reuse_item() { @@ -303,8 +301,26 @@ impl<'a> Parser<'a> { // MODULE ITEM self.parse_item_mod(attrs)? } else if self.eat_keyword_case(exp!(Type), case) { - // TYPE ITEM - self.parse_type_alias(def_())? + if let Const::Yes(const_span) = self.parse_constness(case) { + // TYPE CONST (mgca) + self.recover_const_mut(const_span); + self.recover_missing_kw_before_item()?; + let (ident, generics, ty, rhs_kind) = self.parse_const_item(true)?; + // Make sure this is only allowed if the feature gate is enabled. + // #![feature(mgca_type_const_syntax)] + self.psess.gated_spans.gate(sym::mgca_type_const_syntax, lo.to(const_span)); + ItemKind::Const(Box::new(ConstItem { + defaultness: def_(), + ident, + generics, + ty, + rhs_kind, + define_opaque: None, + })) + } else { + // TYPE ITEM + self.parse_type_alias(def_())? + } } else if self.eat_keyword_case(exp!(Enum), case) { // ENUM ITEM self.parse_item_enum()? @@ -518,7 +534,7 @@ impl<'a> Parser<'a> { match self.parse_delim_args() { // `( .. )` or `[ .. ]` (followed by `;`), or `{ .. }`. Ok(args) => { - self.eat_semi_for_macro_if_needed(&args); + self.eat_semi_for_macro_if_needed(&args, Some(&path)); self.complain_if_pub_macro(vis, false); Ok(MacCall { path, args }) } @@ -1113,13 +1129,12 @@ impl<'a> Parser<'a> { define_opaque, }) => { self.dcx().emit_err(errors::AssociatedStaticItemNotAllowed { span }); - let rhs = expr.map(ConstItemRhs::Body); AssocItemKind::Const(Box::new(ConstItem { defaultness: Defaultness::Final, ident, generics: Generics::default(), ty, - rhs, + rhs_kind: ConstItemRhsKind::Body { rhs: expr }, define_opaque, })) } @@ -1360,7 +1375,7 @@ impl<'a> Parser<'a> { let kind = match ForeignItemKind::try_from(kind) { Ok(kind) => kind, Err(kind) => match kind { - ItemKind::Const(box ConstItem { ident, ty, rhs, .. }) => { + ItemKind::Const(box ConstItem { ident, ty, rhs_kind, .. }) => { let const_span = Some(span.with_hi(ident.span.lo())) .filter(|span| span.can_be_used_for_suggestions()); self.dcx().emit_err(errors::ExternItemCannotBeConst { @@ -1371,10 +1386,13 @@ impl<'a> Parser<'a> { ident, ty, mutability: Mutability::Not, - expr: rhs.map(|b| match b { - ConstItemRhs::TypeConst(anon_const) => anon_const.value, - ConstItemRhs::Body(expr) => expr, - }), + expr: match rhs_kind { + ConstItemRhsKind::Body { rhs } => rhs, + ConstItemRhsKind::TypeConst { rhs: Some(anon) } => { + Some(anon.value) + } + ConstItemRhsKind::TypeConst { rhs: None } => None, + }, safety: Safety::Default, define_opaque: None, })) @@ -1516,13 +1534,16 @@ impl<'a> Parser<'a> { /// Parse a constant item with the prefix `"const"` already parsed. /// + /// If `const_arg` is true, any expression assigned to the const will be parsed + /// as a const_arg instead of a body expression. + /// /// ```ebnf /// Const = "const" ($ident | "_") Generics ":" $ty (= $expr)? WhereClause ";" ; /// ``` fn parse_const_item( &mut self, - attrs: &[Attribute], - ) -> PResult<'a, (Ident, Generics, Box, Option)> { + const_arg: bool, + ) -> PResult<'a, (Ident, Generics, Box, ConstItemRhsKind)> { let ident = self.parse_ident_or_underscore()?; let mut generics = self.parse_generics()?; @@ -1549,14 +1570,15 @@ impl<'a> Parser<'a> { let before_where_clause = if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() }; - let rhs = if self.eat(exp!(Eq)) { - if attr::contains_name(attrs, sym::type_const) { - Some(ConstItemRhs::TypeConst(self.parse_const_arg()?)) - } else { - Some(ConstItemRhs::Body(self.parse_expr()?)) - } - } else { - None + let rhs = match (self.eat(exp!(Eq)), const_arg) { + (true, true) => ConstItemRhsKind::TypeConst { + rhs: Some( + self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?, + ), + }, + (true, false) => ConstItemRhsKind::Body { rhs: Some(self.parse_expr()?) }, + (false, true) => ConstItemRhsKind::TypeConst { rhs: None }, + (false, false) => ConstItemRhsKind::Body { rhs: None }, }; let after_where_clause = self.parse_where_clause()?; @@ -1565,18 +1587,18 @@ impl<'a> Parser<'a> { // Users may be tempted to write such code if they are still used to the deprecated // where-clause location on type aliases and associated types. See also #89122. if before_where_clause.has_where_token - && let Some(rhs) = &rhs + && let Some(rhs_span) = rhs.span() { self.dcx().emit_err(errors::WhereClauseBeforeConstBody { span: before_where_clause.span, name: ident.span, - body: rhs.span(), + body: rhs_span, sugg: if !after_where_clause.has_where_token { - self.psess.source_map().span_to_snippet(rhs.span()).ok().map(|body_s| { + self.psess.source_map().span_to_snippet(rhs_span).ok().map(|body_s| { errors::WhereClauseBeforeConstBodySugg { left: before_where_clause.span.shrink_to_lo(), snippet: body_s, - right: before_where_clause.span.shrink_to_hi().to(rhs.span()), + right: before_where_clause.span.shrink_to_hi().to(rhs_span), } }) } else { @@ -1721,7 +1743,7 @@ impl<'a> Parser<'a> { if this.token == token::Bang { if let Err(err) = this.unexpected() { - err.with_note(fluent::parse_macro_expands_to_enum_variant).emit(); + err.with_note(inline_fluent!("macros cannot expand to enum variants")).emit(); } this.bump(); @@ -2370,7 +2392,7 @@ impl<'a> Parser<'a> { } let body = self.parse_delim_args()?; - self.eat_semi_for_macro_if_needed(&body); + self.eat_semi_for_macro_if_needed(&body, None); self.complain_if_pub_macro(vis, true); Ok(ItemKind::MacroDef( @@ -2395,13 +2417,13 @@ impl<'a> Parser<'a> { } } - fn eat_semi_for_macro_if_needed(&mut self, args: &DelimArgs) { + fn eat_semi_for_macro_if_needed(&mut self, args: &DelimArgs, path: Option<&Path>) { if args.need_semicolon() && !self.eat(exp!(Semi)) { - self.report_invalid_macro_expansion_item(args); + self.report_invalid_macro_expansion_item(args, path); } } - fn report_invalid_macro_expansion_item(&self, args: &DelimArgs) { + fn report_invalid_macro_expansion_item(&self, args: &DelimArgs, path: Option<&Path>) { let span = args.dspan.entire(); let mut err = self.dcx().struct_span_err( span, @@ -2411,17 +2433,32 @@ impl<'a> Parser<'a> { // macros within the same crate (that we can fix), which is sad. if !span.from_expansion() { let DelimSpan { open, close } = args.dspan; - err.multipart_suggestion( - "change the delimiters to curly braces", - vec![(open, "{".to_string()), (close, '}'.to_string())], - Applicability::MaybeIncorrect, - ); - err.span_suggestion( - span.with_neighbor(self.token.span).shrink_to_hi(), - "add a semicolon", - ';', - Applicability::MaybeIncorrect, - ); + // Check if this looks like `macro_rules!(name) { ... }` + // a common mistake when trying to define a macro. + if let Some(path) = path + && path.segments.first().is_some_and(|seg| seg.ident.name == sym::macro_rules) + && args.delim == Delimiter::Parenthesis + { + let replace = + if path.span.hi() + rustc_span::BytePos(1) < open.lo() { "" } else { " " }; + err.multipart_suggestion( + "to define a macro, remove the parentheses around the macro name", + vec![(open, replace.to_string()), (close, String::new())], + Applicability::MachineApplicable, + ); + } else { + err.multipart_suggestion( + "change the delimiters to curly braces", + vec![(open, "{".to_string()), (close, '}'.to_string())], + Applicability::MaybeIncorrect, + ); + err.span_suggestion( + span.with_neighbor(self.token.span).shrink_to_hi(), + "add a semicolon", + ';', + Applicability::MaybeIncorrect, + ); + } } err.emit(); } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 3ef73a55d47d..f95fe61b0abd 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -20,7 +20,6 @@ use std::{fmt, mem, slice}; use attr_wrapper::{AttrWrapper, UsePreAttrPos}; pub use diagnostics::AttemptLocalParseRecovery; -pub(crate) use expr::ForbiddenLetReason; // Public to use it for custom `if` expressions in rustfmt forks like https://github.com/tucant/rustfmt pub use expr::LetChainsPolicy; pub(crate) use item::{FnContext, FnParseMode}; @@ -34,9 +33,9 @@ use rustc_ast::tokenstream::{ }; use rustc_ast::util::case::Case; use rustc_ast::{ - self as ast, AnonConst, AttrArgs, AttrId, BlockCheckMode, ByRef, Const, CoroutineKind, - DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, MgcaDisambiguation, - Mutability, Recovered, Safety, StrLit, Visibility, VisibilityKind, + self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, + DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, MgcaDisambiguation, Mutability, + Recovered, Safety, StrLit, Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::debug_assert_matches; @@ -50,7 +49,7 @@ use token_type::TokenTypeSet; pub use token_type::{ExpKeywordPair, ExpTokenPair, TokenType}; use tracing::debug; -use crate::errors::{self, IncorrectVisibilityRestriction, NonStringAbiLiteral}; +use crate::errors::{self, IncorrectVisibilityRestriction, NonStringAbiLiteral, TokenDescription}; use crate::exp; #[cfg(test)] @@ -308,35 +307,6 @@ impl From for Trailing { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub(super) enum TokenDescription { - ReservedIdentifier, - Keyword, - ReservedKeyword, - DocComment, - - // Expanded metavariables are wrapped in invisible delimiters which aren't - // pretty-printed. In error messages we must handle these specially - // otherwise we get confusing things in messages like "expected `(`, found - // ``". It's better to say e.g. "expected `(`, found type metavariable". - MetaVar(MetaVarKind), -} - -impl TokenDescription { - pub(super) fn from_token(token: &Token) -> Option { - match token.kind { - _ if token.is_special_ident() => Some(TokenDescription::ReservedIdentifier), - _ if token.is_used_keyword() => Some(TokenDescription::Keyword), - _ if token.is_unused_keyword() => Some(TokenDescription::ReservedKeyword), - token::DocComment(..) => Some(TokenDescription::DocComment), - token::OpenInvisible(InvisibleOrigin::MetaVar(kind)) => { - Some(TokenDescription::MetaVar(kind)) - } - _ => None, - } - } -} - pub fn token_descr(token: &Token) -> String { let s = pprust::token_to_string(token).to_string(); @@ -1299,21 +1269,8 @@ impl<'a> Parser<'a> { } } - fn parse_mgca_const_block(&mut self, gate_syntax: bool) -> PResult<'a, AnonConst> { - let kw_span = self.prev_token.span; - let value = self.parse_expr_block(None, kw_span, BlockCheckMode::Default)?; - if gate_syntax { - self.psess.gated_spans.gate(sym::min_generic_const_args, kw_span.to(value.span)); - } - Ok(AnonConst { - id: ast::DUMMY_NODE_ID, - value, - mgca_disambiguation: MgcaDisambiguation::AnonConst, - }) - } - /// Parses inline const expressions. - fn parse_const_block(&mut self, span: Span) -> PResult<'a, Box> { + fn parse_const_block(&mut self, span: Span, pat: bool) -> PResult<'a, Box> { self.expect_keyword(exp!(Const))?; let (attrs, blk) = self.parse_inner_attrs_and_block(None)?; let anon_const = AnonConst { @@ -1322,7 +1279,18 @@ impl<'a> Parser<'a> { mgca_disambiguation: MgcaDisambiguation::AnonConst, }; let blk_span = anon_const.value.span; - let kind = ExprKind::ConstBlock(anon_const); + let kind = if pat { + let guar = self + .dcx() + .struct_span_err(blk_span, "const blocks cannot be used as patterns") + .with_help( + "use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead", + ) + .emit(); + ExprKind::Err(guar) + } else { + ExprKind::ConstBlock(anon_const) + }; Ok(self.mk_expr_with_attrs(span.to(blk_span), kind, attrs)) } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index c1e864985e19..bc73c3a2007a 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -785,10 +785,8 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(exp!(Box)) { self.parse_pat_box()? } else if self.check_inline_const(0) { - // Parse `const pat`. - // NOTE: This will always error later during AST lowering because - // inline const cannot be used as patterns. - let const_expr = self.parse_const_block(lo.to(self.token.span))?; + // Parse `const pat` + let const_expr = self.parse_const_block(lo.to(self.token.span), true)?; if let Some(re) = self.parse_range_end() { self.parse_pat_range_begin_with(const_expr, re)? @@ -1283,7 +1281,7 @@ impl<'a> Parser<'a> { .then_some(self.prev_token.span); let bound = if self.check_inline_const(0) { - self.parse_const_block(self.token.span) + self.parse_const_block(self.token.span, true) } else if self.check_path() { let lo = self.token.span; let (qself, path) = if self.eat_lt() { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index dd190707c42b..9196d8d156d8 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -847,6 +847,7 @@ impl<'a> Parser<'a> { /// - A literal. /// - A numeric literal prefixed by `-`. /// - A single-segment path. + /// - A const block (under mGCA) pub(super) fn expr_is_valid_const_arg(&self, expr: &Box) -> bool { match &expr.kind { ast::ExprKind::Block(_, _) @@ -863,6 +864,10 @@ impl<'a> Parser<'a> { { true } + ast::ExprKind::ConstBlock(_) => { + self.psess.gated_spans.gate(sym::min_generic_const_args, expr.span); + true + } _ => false, } } @@ -874,14 +879,6 @@ impl<'a> Parser<'a> { let (value, mgca_disambiguation) = if self.token.kind == token::OpenBrace { let value = self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)?; (value, MgcaDisambiguation::Direct) - } else if self.eat_keyword(exp!(Const)) { - // While we could just disambiguate `Direct` from `AnonConst` by - // treating all const block exprs as `AnonConst`, that would - // complicate the DefCollector and likely all other visitors. - // So we strip the const blockiness and just store it as a block - // in the AST with the extra disambiguator on the AnonConst - let value = self.parse_mgca_const_block(true)?; - (value.value, MgcaDisambiguation::AnonConst) } else { self.parse_unambiguous_unbraced_const_arg()? }; diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 61b498431b04..ac9c3e63e853 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -25,10 +25,6 @@ use crate::lexer::StripTokens; use crate::parser::{AllowConstBlockItems, ForceCollect, Parser}; use crate::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; -fn psess() -> ParseSess { - ParseSess::new(vec![crate::DEFAULT_LOCALE_RESOURCE]) -} - fn filename(sm: &SourceMap, path: &str) -> FileName { FileName::Real(sm.path_mapping().to_real_filename(sm.working_dir(), PathBuf::from(path))) } @@ -46,7 +42,7 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> { fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Arc, Arc>>) { let output = Arc::new(Mutex::new(Vec::new())); let source_map = Arc::new(SourceMap::new(FilePathMapping::empty())); - let translator = Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false); + let translator = Translator::new(); let shared: Box = Box::new(Shared { data: output.clone() }); let auto_stream = AutoStream::never(shared); let dcx = DiagCtxt::new(Box::new( @@ -93,7 +89,7 @@ where /// Maps a string to tts, using a made-up filename. pub(crate) fn string_to_stream(source_str: String) -> TokenStream { - let psess = psess(); + let psess = ParseSess::new(); unwrap_or_emit_fatal(source_str_to_stream( &psess, filename(psess.source_map(), "bogofile"), @@ -2243,12 +2239,12 @@ fn sp(a: u32, b: u32) -> Span { /// Parses a string, return an expression. fn string_to_expr(source_str: String) -> Box { - with_error_checking_parse(source_str, &psess(), |p| p.parse_expr()) + with_error_checking_parse(source_str, &ParseSess::new(), |p| p.parse_expr()) } /// Parses a string, returns an item. fn string_to_item(source_str: String) -> Option> { - with_error_checking_parse(source_str, &psess(), |p| { + with_error_checking_parse(source_str, &ParseSess::new(), |p| { p.parse_item(ForceCollect::No, AllowConstBlockItems::Yes) }) } @@ -2484,7 +2480,7 @@ let mut fflags: c_int = wb(); #[test] fn crlf_doc_comments() { create_default_session_globals_then(|| { - let psess = psess(); + let psess = ParseSess::new(); let name_1 = FileName::Custom("crlf_source_1".to_string()); let source = "/// doc comment\r\nfn foo() {}".to_string(); @@ -2519,7 +2515,7 @@ fn ttdelim_span() { } create_default_session_globals_then(|| { - let psess = psess(); + let psess = ParseSess::new(); let expr = parse_expr_from_source_str( filename(psess.source_map(), "foo"), "foo!( fn main() { body } )".to_string(), @@ -2555,7 +2551,7 @@ fn look_ahead() { let sym_S = Symbol::intern("S"); let raw_no = IdentIsRaw::No; - let psess = psess(); + let psess = ParseSess::new(); let mut p = string_to_parser(&psess, "fn f(x: u32) { x } struct S;".to_string()); // Current position is the `fn`. @@ -2630,7 +2626,7 @@ fn look_ahead_non_outermost_stream() { let sym_S = Symbol::intern("S"); let raw_no = IdentIsRaw::No; - let psess = psess(); + let psess = ParseSess::new(); let mut p = string_to_parser(&psess, "mod m { fn f(x: u32) { x } struct S; }".to_string()); // Move forward to the `fn`, which is not within the outermost token @@ -2662,7 +2658,7 @@ fn look_ahead_non_outermost_stream() { #[test] fn debug_lookahead() { create_default_session_globals_then(|| { - let psess = psess(); + let psess = ParseSess::new(); let mut p = string_to_parser(&psess, "fn f(x: u32) { x } struct S;".to_string()); // Current position is the `fn`. @@ -2883,7 +2879,7 @@ fn debug_lookahead() { #[test] fn out_of_line_mod() { create_default_session_globals_then(|| { - let psess = psess(); + let psess = ParseSess::new(); let item = parse_item_from_source_str( filename(psess.source_map(), "foo"), "mod foo { struct S; mod this_does_not_exist; }".to_owned(), diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 380b6a214846..6ff165eb22b7 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -658,16 +658,8 @@ impl<'a> Parser<'a> { }; let ty = if self.eat(exp!(Semi)) { - let mut length = if self.eat_keyword(exp!(Const)) { - // While we could just disambiguate `Direct` from `AnonConst` by - // treating all const block exprs as `AnonConst`, that would - // complicate the DefCollector and likely all other visitors. - // So we strip the const blockiness and just store it as a block - // in the AST with the extra disambiguator on the AnonConst - self.parse_mgca_const_block(false)? - } else { - self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))? - }; + let mut length = + self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?; if let Err(e) = self.expect(exp!(CloseBracket)) { // Try to recover from `X` when `X::` works diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 3d8b97b2fde3..2338268a874f 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -187,6 +187,9 @@ pub enum Suggestion { /// Add missing colon: /// `format!("{foo?}")` -> `format!("{foo:?}")` AddMissingColon(Range), + /// Use Rust format string: + /// `format!("{x=}")` -> `dbg!(x)` + UseRustDebugPrintingMacro, } /// The parser structure for interpreting the input format string. This is @@ -462,6 +465,7 @@ impl<'input> Parser<'input> { ('?', _) => self.suggest_format_debug(), ('<' | '^' | '>', _) => self.suggest_format_align(c), (',', _) => self.suggest_unsupported_python_numeric_grouping(), + ('=', '}') => self.suggest_rust_debug_printing_macro(), _ => self.suggest_positional_arg_instead_of_captured_arg(arg), } } @@ -871,6 +875,27 @@ impl<'input> Parser<'input> { } } + fn suggest_rust_debug_printing_macro(&mut self) { + if let Some((range, _)) = self.consume_pos('=') { + self.errors.insert( + 0, + ParseError { + description: + "python's f-string debug `=` is not supported in rust, use `dbg(x)` instead" + .to_owned(), + note: Some(format!("to print `{{`, you can escape it using `{{{{`",)), + label: "expected `}`".to_owned(), + span: range, + secondary_label: self + .last_open_brace + .clone() + .map(|sp| ("because of this opening brace".to_owned(), sp)), + suggestion: Suggestion::UseRustDebugPrintingMacro, + }, + ); + } + } + fn suggest_format_align(&mut self, alignment: char) { if let Some((range, _)) = self.consume_pos(alignment) { self.errors.insert( diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index 10da57f56ecf..b65a0ad404a5 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -13,7 +13,6 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl deleted file mode 100644 index fae0052ab7a4..000000000000 --- a/compiler/rustc_passes/messages.ftl +++ /dev/null @@ -1,576 +0,0 @@ --passes_previously_accepted = - this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - --passes_see_issue = - see issue #{$issue} for more information - -passes_abi_invalid_attribute = - `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions -passes_abi_ne = - ABIs are not compatible - left ABI = {$left} - right ABI = {$right} -passes_abi_of = - fn_abi_of({$fn_name}) = {$fn_abi} - -passes_attr_application_enum = - attribute should be applied to an enum - .label = not an enum - -passes_attr_application_struct = - attribute should be applied to a struct - .label = not a struct - -passes_attr_application_struct_enum_union = - attribute should be applied to a struct, enum, or union - .label = not a struct, enum, or union - -passes_attr_application_struct_union = - attribute should be applied to a struct or union - .label = not a struct or union - -passes_autodiff_attr = - `#[autodiff]` should be applied to a function - .label = not a function - -passes_both_ffi_const_and_pure = - `#[ffi_const]` function cannot be `#[ffi_pure]` - -passes_cannot_stabilize_deprecated = - an API can't be stabilized after it is deprecated - .label = invalid version - .item = the stability attribute annotates this item - -passes_change_fields_to_be_of_unit_type = - consider changing the { $num -> - [one] field - *[other] fields - } to be of unit type to suppress this warning while preserving the field numbering, or remove the { $num -> - [one] field - *[other] fields - } - -passes_const_continue_attr = - `#[const_continue]` should be applied to a break expression - .label = not a break expression - -passes_const_stable_not_stable = - attribute `#[rustc_const_stable]` can only be applied to functions that are declared `#[stable]` - .label = attribute specified here - -passes_custom_mir_incompatible_dialect_and_phase = - the {$dialect} dialect is not compatible with the {$phase} phase - .dialect_span = this dialect... - .phase_span = ... is not compatible with this phase - -passes_custom_mir_phase_requires_dialect = - `dialect` key required - .phase_span = `phase` argument requires a `dialect` argument - - -passes_dead_codes = - { $multiple -> - *[true] multiple {$descr}s are - [false] { $num -> - [one] {$descr} {$name_list} is - *[other] {$descr}s {$name_list} are - } - } never {$participle} - -passes_debug_visualizer_unreadable = - couldn't read {$file}: {$error} - -passes_deprecated_annotation_has_no_effect = - this `#[deprecated]` annotation has no effect - .suggestion = remove the unnecessary deprecation attribute - -passes_deprecated_attribute = - deprecated attribute must be paired with either stable or unstable attribute - -passes_diagnostic_diagnostic_on_const_only_for_non_const_trait_impls = - `#[diagnostic::on_const]` can only be applied to non-const trait impls - .label = this is a const trait impl - -passes_diagnostic_diagnostic_on_const_only_for_trait_impls = - `#[diagnostic::on_const]` can only be applied to trait impls - .label = not a trait impl - -passes_diagnostic_diagnostic_on_unimplemented_only_for_traits = - `#[diagnostic::on_unimplemented]` can only be applied to trait definitions - -passes_diagnostic_item_first_defined = - the diagnostic item is first defined here - -passes_doc_alias_bad_location = - `#[doc(alias = "...")]` isn't allowed on {$location} - -passes_doc_alias_not_an_alias = - `#[doc(alias = "{$attr_str}"]` is the same as the item's name - -passes_doc_fake_variadic_not_valid = - `#[doc(fake_variadic)]` must be used on the first of a set of tuple or fn pointer trait impls with varying arity - -passes_doc_inline_conflict = - conflicting doc inlining attributes - .help = remove one of the conflicting attributes - -passes_doc_inline_conflict_first = - this attribute... - -passes_doc_inline_conflict_second = - {"."}..conflicts with this attribute - -passes_doc_inline_only_use = - this attribute can only be applied to a `use` item - .label = only applicable on `use` items - .not_a_use_item_label = not a `use` item - .note = read for more information - -passes_doc_keyword_attribute_empty_mod = - `#[doc({$attr_name} = "...")]` should be used on empty modules - -passes_doc_keyword_attribute_not_mod = - `#[doc({$attr_name} = "...")]` should be used on modules - -passes_doc_keyword_only_impl = - `#[doc(keyword = "...")]` should be used on impl blocks - -passes_doc_masked_not_extern_crate_self = - this attribute cannot be applied to an `extern crate self` item - .label = not applicable on `extern crate self` items - .extern_crate_self_label = `extern crate self` defined here - -passes_doc_masked_only_extern_crate = - this attribute can only be applied to an `extern crate` item - .label = only applicable on `extern crate` items - .not_an_extern_crate_label = not an `extern crate` item - .note = read for more information - -passes_doc_rust_logo = - the `#[doc(rust_logo)]` attribute is used for Rust branding - -passes_doc_search_unbox_invalid = - `#[doc(search_unbox)]` should be used on generic structs and enums - -passes_duplicate_diagnostic_item_in_crate = - duplicate diagnostic item in crate `{$crate_name}`: `{$name}` - .note = the diagnostic item is first defined in crate `{$orig_crate_name}` - -passes_duplicate_eii_impls = - multiple implementations of `#[{$name}]` - .first = first implemented here in crate `{$first_crate}` - .second = also implemented here in crate `{$second_crate}` - .note = in addition to these two, { $num_additional_crates -> - [one] another implementation was found in crate {$additional_crate_names} - *[other] more implementations were also found in the following crates: {$additional_crate_names} - } - - .help = an "externally implementable item" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict - -passes_duplicate_feature_err = - the feature `{$feature}` has already been enabled - -passes_duplicate_lang_item = - found duplicate lang item `{$lang_item_name}` - .first_defined_span = the lang item is first defined here - .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on) - .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}` - .first_definition_local = first definition in the local crate (`{$orig_crate_name}`) - .second_definition_local = second definition in the local crate (`{$crate_name}`) - .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path} - .second_definition_path = second definition in `{$crate_name}` loaded from {$path} - -passes_duplicate_lang_item_crate = - duplicate lang item in crate `{$crate_name}`: `{$lang_item_name}` - .first_defined_span = the lang item is first defined here - .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on) - .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}` - .first_definition_local = first definition in the local crate (`{$orig_crate_name}`) - .second_definition_local = second definition in the local crate (`{$crate_name}`) - .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path} - .second_definition_path = second definition in `{$crate_name}` loaded from {$path} - -passes_duplicate_lang_item_crate_depends = - duplicate lang item in crate `{$crate_name}` (which `{$dependency_of}` depends on): `{$lang_item_name}` - .first_defined_span = the lang item is first defined here - .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on) - .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}` - .first_definition_local = first definition in the local crate (`{$orig_crate_name}`) - .second_definition_local = second definition in the local crate (`{$crate_name}`) - .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path} - .second_definition_path = second definition in `{$crate_name}` loaded from {$path} - -passes_eii_fn_with_track_caller = - `#[{$name}]` is not allowed to have `#[track_caller]` - .label = `#[{$name}]` is not allowed to have `#[track_caller]` - -passes_eii_impl_not_function = - `eii_macro_for` is only valid on functions - -passes_eii_impl_requires_unsafe = - `#[{$name}]` is unsafe to implement -passes_eii_impl_requires_unsafe_suggestion = wrap the attribute in `unsafe(...)` - -passes_eii_without_impl = - `#[{$name}]` required, but not found - .label = expected because `#[{$name}]` was declared here in crate `{$decl_crate_name}` - .help = expected at least one implementation in crate `{$current_crate_name}` or any of its dependencies - -passes_enum_variant_same_name = - it is impossible to refer to the {$dead_descr} `{$dead_name}` because it is shadowed by this enum variant with the same name - -passes_extern_main = - the `main` function cannot be declared in an `extern` block - -passes_feature_previously_declared = - feature `{$feature}` is declared {$declared}, but was previously declared {$prev_declared} - -passes_feature_stable_twice = - feature `{$feature}` is declared stable since {$since}, but was previously declared stable since {$prev_since} - -passes_function_not_found_in_trait = function not found in this trait - -passes_function_not_have_default_implementation = function doesn't have a default implementation - .note = required by this annotation - -passes_functions_names_duplicated = functions names are duplicated - .note = all `#[rustc_must_implement_one_of]` arguments must be unique - -passes_ignored_derived_impls = - `{$name}` has {$trait_list_len -> - [one] a derived impl - *[other] derived impls - } for the {$trait_list_len -> - [one] trait {$trait_list}, but this is - *[other] traits {$trait_list}, but these are - } intentionally ignored during dead code analysis - -passes_implied_feature_not_exist = - feature `{$implied_by}` implying `{$feature}` does not exist - -passes_incorrect_crate_type = lang items are not allowed in stable dylibs - -passes_incorrect_do_not_recommend_location = - `#[diagnostic::do_not_recommend]` can only be placed on trait implementations - -passes_incorrect_target = - `{$name}` lang item must be applied to a {$kind} with {$at_least -> - [true] at least {$num} - *[false] {$num} - } generic {$num -> - [one] argument - *[other] arguments - } - .label = this {$kind} has {$actual_num} generic {$actual_num -> - [one] argument - *[other] arguments - } - -passes_ineffective_unstable_impl = an `#[unstable]` annotation here has no effect - .note = see issue #55436 for more information - -passes_inline_ignored_for_exported = - `#[inline]` is ignored on externally exported functions - .help = externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]` - -passes_inner_crate_level_attr = - crate-level attribute should be in the root module - -passes_invalid_attr_at_crate_level = - `{$name}` attribute cannot be used at crate level - .suggestion = perhaps you meant to use an outer attribute - -passes_invalid_attr_at_crate_level_item = - the inner attribute doesn't annotate this {$kind} - -passes_lang_item_fn = {$name -> - [panic_impl] `#[panic_handler]` - *[other] `{$name}` lang item -} function - -passes_lang_item_fn_with_target_feature = - {passes_lang_item_fn} is not allowed to have `#[target_feature]` - .label = {passes_lang_item_fn} is not allowed to have `#[target_feature]` - -passes_lang_item_fn_with_track_caller = - {passes_lang_item_fn} is not allowed to have `#[track_caller]` - .label = {passes_lang_item_fn} is not allowed to have `#[track_caller]` - -passes_lang_item_on_incorrect_target = - `{$name}` lang item must be applied to a {$expected_target} - .label = attribute should be applied to a {$expected_target}, not a {$actual_target} - -passes_layout_abi = - abi: {$abi} -passes_layout_align = - align: {$align} -passes_layout_homogeneous_aggregate = - homogeneous_aggregate: {$homogeneous_aggregate} -passes_layout_of = - layout_of({$normalized_ty}) = {$ty_layout} -passes_layout_size = - size: {$size} - -passes_link = - attribute should be applied to an `extern` block with non-Rust ABI - .warn = {-passes_previously_accepted} - .label = not an `extern` block - -passes_loop_match_attr = - `#[loop_match]` should be applied to a loop - .label = not a loop - -passes_macro_export_on_decl_macro = - `#[macro_export]` has no effect on declarative macro definitions - .note = declarative macros follow the same exporting rules as regular items - -passes_macro_only_attribute = - attribute should be applied to a macro - .label = not a macro - -passes_may_dangle = - `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl - -passes_missing_const_err = - attributes `#[rustc_const_unstable]`, `#[rustc_const_stable]` and `#[rustc_const_stable_indirect]` require the function or method to be `const` - .help = make the function or method const - -passes_missing_const_stab_attr = - {$descr} has missing const stability attribute - -passes_missing_lang_item = - lang item required, but not found: `{$name}` - .note = this can occur when a binary crate with `#![no_std]` is compiled for a target where `{$name}` is defined in the standard library - .help = you may be able to compile for a target that doesn't need `{$name}`, specify a target with `--target` or in `.cargo/config` - -passes_missing_panic_handler = - `#[panic_handler]` function required, but not found - -passes_missing_stability_attr = - {$descr} has missing stability attribute - -passes_misspelled_feature = there is a feature with a similar name: `{$actual_name}` - -passes_mixed_export_name_and_no_mangle = `{$no_mangle_attr}` attribute may not be used in combination with `{$export_name_attr}` - .label = `{$no_mangle_attr}` is ignored - .note = `{$export_name_attr}` takes precedence - .suggestion = remove the `{$no_mangle_attr}` attribute - -passes_multiple_rustc_main = - multiple functions with a `#[rustc_main]` attribute - .first = first `#[rustc_main]` function - .additional = additional `#[rustc_main]` function - -passes_must_implement_not_function = not a function - -passes_must_implement_not_function_note = all `#[rustc_must_implement_one_of]` arguments must be associated function names - -passes_must_implement_not_function_span_note = required by this annotation - -passes_no_main_function = - `main` function not found in crate `{$crate_name}` - .here_is_main = here is a function named `main` - .one_or_more_possible_main = you have one or more functions named `main` not defined at the crate level - .consider_moving_main = consider moving the `main` function definitions - .main_must_be_defined_at_crate = the main function must be defined at the crate level{$has_filename -> - [true] {" "}(in `{$filename}`) - *[false] {""} - } - .consider_adding_main_to_file = consider adding a `main` function to `{$filename}` - .consider_adding_main_at_crate = consider adding a `main` function at the crate level - .teach_note = if you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/ - .non_function_main = non-function item at `crate::main` is found - -passes_non_exhaustive_with_default_field_values = - `#[non_exhaustive]` can't be used to annotate items with default field values - .label = this struct has default field values - -passes_non_exported_macro_invalid_attrs = - attribute should be applied to function or closure - .label = not a function or closure - -passes_object_lifetime_err = - {$repr} - -passes_outer_crate_level_attr = - crate-level attribute should be an inner attribute - -passes_outer_crate_level_attr_suggestion = - add a `!` - -passes_panic_unwind_without_std = - unwinding panics are not supported without std - .note = since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem - .help = using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding - -passes_parent_info = - {$num -> - [one] {$descr} - *[other] {$descr}s - } in this {$parent_descr} - -passes_proc_macro_bad_sig = {$kind} has incorrect signature - -passes_remove_fields = - consider removing { $num -> - [one] this - *[other] these - } { $num -> - [one] field - *[other] fields - } - -passes_repr_align_greater_than_target_max = - alignment must not be greater than `isize::MAX` bytes - .note = `isize::MAX` is {$size} for the current target - -passes_repr_align_should_be_align = - `#[repr(align(...))]` is not supported on {$item} - .help = use `#[rustc_align(...)]` instead - -passes_repr_align_should_be_align_static = - `#[repr(align(...))]` is not supported on {$item} - .help = use `#[rustc_align_static(...)]` instead - -passes_repr_conflicting = - conflicting representation hints - -passes_rustc_allow_const_fn_unstable = - attribute should be applied to `const fn` - .label = not a `const fn` - -passes_rustc_const_stable_indirect_pairing = - `const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied - -passes_rustc_force_inline_coro = - attribute cannot be applied to a `async`, `gen` or `async gen` function - .label = `async`, `gen` or `async gen` function - -passes_rustc_legacy_const_generics_index = - #[rustc_legacy_const_generics] must have one index for each generic parameter - .label = generic parameters - -passes_rustc_legacy_const_generics_index_exceed = - index exceeds number of arguments - .label = there {$arg_count -> - [one] is - *[other] are - } only {$arg_count} {$arg_count -> - [one] argument - *[other] arguments - } - -passes_rustc_legacy_const_generics_only = - #[rustc_legacy_const_generics] functions must only have const generics - .label = non-const generic parameter - - -passes_rustc_pub_transparent = - attribute should be applied to `#[repr(transparent)]` types - .label = not a `#[repr(transparent)]` type - -passes_sanitize_attribute_not_allowed = - sanitize attribute not allowed here - .not_fn_impl_mod = not a function, impl block, or module - .no_body = function has no body - .help = sanitize attribute can be applied to a function (with body), impl block, or module - -passes_trait_impl_const_stability_mismatch = const stability on the impl does not match the const stability on the trait -passes_trait_impl_const_stability_mismatch_impl_stable = this impl is (implicitly) stable... -passes_trait_impl_const_stability_mismatch_impl_unstable = this impl is unstable... -passes_trait_impl_const_stability_mismatch_trait_stable = ...but the trait is stable -passes_trait_impl_const_stability_mismatch_trait_unstable = ...but the trait is unstable - -passes_trait_impl_const_stable = - trait implementations cannot be const stable yet - .note = see issue #143874 for more information - -passes_transparent_incompatible = - transparent {$target} cannot have other repr hints - -passes_unexportable_adt_with_private_fields = ADT types with private fields are not exportable - .note = `{$field_name}` is private - -passes_unexportable_fn_abi = only functions with "C" ABI are exportable - -passes_unexportable_generic_fn = generic functions are not exportable - -passes_unexportable_item = {$descr}'s are not exportable - -passes_unexportable_priv_item = private items are not exportable - .note = is only usable at visibility `{$vis_descr}` - -passes_unexportable_type_in_interface = {$desc} with `#[export_stable]` attribute uses type `{$ty}`, which is not exportable - .label = not exportable - -passes_unexportable_type_repr = types with unstable layout are not exportable - -passes_unknown_external_lang_item = - unknown external lang item: `{$lang_item}` - -passes_unknown_feature = - unknown feature `{$feature}` - -passes_unknown_feature_alias = - feature `{$alias}` has been renamed to `{$feature}` - -passes_unknown_lang_item = - definition of an unknown lang item: `{$name}` - .label = definition of unknown lang item `{$name}` - -passes_unnecessary_partial_stable_feature = the feature `{$feature}` has been partially stabilized since {$since} and is succeeded by the feature `{$implies}` - .suggestion = if you are using features which are still unstable, change to using `{$implies}` - .suggestion_remove = if you are using features which are now stable, remove this line - -passes_unnecessary_stable_feature = the feature `{$feature}` has been stable since {$since} and no longer requires an attribute to enable - -passes_unrecognized_argument = - unrecognized argument - -passes_unstable_attr_for_already_stable_feature = - can't mark as unstable using an already stable feature - .label = this feature is already stable - .item = the stability attribute annotates this item - .help = consider removing the attribute - -passes_unsupported_attributes_in_where = - most attributes are not supported in `where` clauses - .help = only `#[cfg]` and `#[cfg_attr]` are supported - -passes_unused = - unused attribute - .suggestion = remove this attribute - -passes_unused_default_method_body_const_note = - `default_method_body_is_const` has been replaced with `const` on traits - -passes_unused_duplicate = - unused attribute - .suggestion = remove this attribute - .note = attribute also specified here - .warn = {-passes_previously_accepted} - -passes_unused_empty_lints_note = - attribute `{$name}` with an empty list has no effect - -passes_unused_linker_messages_note = - the `linker_messages` lint can only be controlled at the root of a crate that needs to be linked - -passes_unused_multiple = - multiple `{$name}` attributes - .suggestion = remove this attribute - .note = attribute also specified here - -passes_unused_no_lints_note = - attribute `{$name}` without any lints has no effect - -passes_useless_assignment = - useless assignment of {$is_field_assign -> - [true] field - *[false] variable - } of type `{$ty}` to itself - -passes_useless_stability = - this stability annotation is useless - .label = useless stability annotation - .item = the stability attribute annotates this item diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index 0ac42f03eb2f..c3e80208e2d5 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -1,11 +1,12 @@ -use rustc_hir::Attribute; +use rustc_hir::attrs::{AttributeKind, RustcAbiAttrKind}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_hir::find_attr; use rustc_middle::span_bug; use rustc_middle::ty::layout::{FnAbiError, LayoutError}; use rustc_middle::ty::{self, GenericArgs, Instance, Ty, TyCtxt}; +use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::sym; use rustc_target::callconv::FnAbi; use super::layout_test::ensure_wf; @@ -17,17 +18,19 @@ pub fn test_abi(tcx: TyCtxt<'_>) { return; } for id in tcx.hir_crate_items(()).definitions() { - for attr in tcx.get_attrs(id, sym::rustc_abi) { - match tcx.def_kind(id) { - DefKind::Fn | DefKind::AssocFn => { - dump_abi_of_fn_item(tcx, id, attr); - } - DefKind::TyAlias => { - dump_abi_of_fn_type(tcx, id, attr); - } - _ => { - tcx.dcx().emit_err(AbiInvalidAttribute { span: tcx.def_span(id) }); - } + let Some((attr_span, attr_kind)) = find_attr!(tcx.get_all_attrs(id), AttributeKind::RustcAbi{ attr_span, kind } => (*attr_span, *kind)) + else { + continue; + }; + match tcx.def_kind(id) { + DefKind::Fn | DefKind::AssocFn => { + dump_abi_of_fn_item(tcx, id, attr_span, attr_kind); + } + DefKind::TyAlias => { + dump_abi_of_fn_type(tcx, id, attr_span, attr_kind); + } + _ => { + tcx.dcx().emit_err(AbiInvalidAttribute { span: tcx.def_span(id) }); } } } @@ -49,7 +52,12 @@ fn unwrap_fn_abi<'tcx>( } } -fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) { +fn dump_abi_of_fn_item( + tcx: TyCtxt<'_>, + item_def_id: LocalDefId, + attr_span: Span, + attr_kind: RustcAbiAttrKind, +) { let typing_env = ty::TypingEnv::post_analysis(tcx, item_def_id); let args = GenericArgs::identity_for_item(tcx, item_def_id); let instance = match Instance::try_resolve(tcx, typing_env, item_def_id.into(), args) { @@ -75,22 +83,18 @@ fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut // Check out the `#[rustc_abi(..)]` attribute to tell what to dump. // The `..` are the names of fields to dump. - let meta_items = attr.meta_item_list().unwrap_or_default(); - for meta_item in meta_items { - match meta_item.name() { - Some(sym::debug) => { - let fn_name = tcx.item_name(item_def_id); - tcx.dcx().emit_err(AbiOf { - span: tcx.def_span(item_def_id), - fn_name, - // FIXME: using the `Debug` impl here isn't ideal. - fn_abi: format!("{:#?}", abi), - }); - } - - _ => { - tcx.dcx().emit_err(UnrecognizedArgument { span: meta_item.span() }); - } + match attr_kind { + RustcAbiAttrKind::Debug => { + let fn_name = tcx.item_name(item_def_id); + tcx.dcx().emit_err(AbiOf { + span: tcx.def_span(item_def_id), + fn_name, + // FIXME: using the `Debug` impl here isn't ideal. + fn_abi: format!("{:#?}", abi), + }); + } + _ => { + tcx.dcx().emit_err(UnrecognizedArgument { span: attr_span }); } } } @@ -109,24 +113,29 @@ fn test_abi_eq<'tcx>(abi1: &'tcx FnAbi<'tcx, Ty<'tcx>>, abi2: &'tcx FnAbi<'tcx, && abi1.args.iter().zip(abi2.args.iter()).all(|(arg1, arg2)| arg1.eq_abi(arg2)) } -fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) { +fn dump_abi_of_fn_type( + tcx: TyCtxt<'_>, + item_def_id: LocalDefId, + attr_span: Span, + attr_kind: RustcAbiAttrKind, +) { let typing_env = ty::TypingEnv::post_analysis(tcx, item_def_id); let ty = tcx.type_of(item_def_id).instantiate_identity(); let span = tcx.def_span(item_def_id); if !ensure_wf(tcx, typing_env, ty, item_def_id, span) { return; } - let meta_items = attr.meta_item_list().unwrap_or_default(); - for meta_item in meta_items { - match meta_item.name() { - Some(sym::debug) => { - let ty::FnPtr(sig_tys, hdr) = ty.kind() else { - span_bug!( - meta_item.span(), - "`#[rustc_abi(debug)]` on a type alias requires function pointer type" - ); - }; - let abi = unwrap_fn_abi( + + match attr_kind { + RustcAbiAttrKind::Debug => { + let ty::FnPtr(sig_tys, hdr) = ty.kind() else { + span_bug!( + attr_span, + "`#[rustc_abi(debug)]` on a type alias requires function pointer type" + ); + }; + let abi = + unwrap_fn_abi( tcx.fn_abi_of_fn_ptr(typing_env.as_query_input(( sig_tys.with(*hdr), /* extra_args */ ty::List::empty(), @@ -135,61 +144,57 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut item_def_id, ); - let fn_name = tcx.item_name(item_def_id); - tcx.dcx().emit_err(AbiOf { span, fn_name, fn_abi: format!("{:#?}", abi) }); - } - Some(sym::assert_eq) => { - let ty::Tuple(fields) = ty.kind() else { - span_bug!( - meta_item.span(), - "`#[rustc_abi(assert_eq)]` on a type alias requires pair type" - ); - }; - let [field1, field2] = ***fields else { - span_bug!( - meta_item.span(), - "`#[rustc_abi(assert_eq)]` on a type alias requires pair type" - ); - }; - let ty::FnPtr(sig_tys1, hdr1) = field1.kind() else { - span_bug!( - meta_item.span(), - "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types" - ); - }; - let abi1 = unwrap_fn_abi( - tcx.fn_abi_of_fn_ptr(typing_env.as_query_input(( - sig_tys1.with(*hdr1), - /* extra_args */ ty::List::empty(), - ))), - tcx, - item_def_id, + let fn_name = tcx.item_name(item_def_id); + tcx.dcx().emit_err(AbiOf { span, fn_name, fn_abi: format!("{:#?}", abi) }); + } + RustcAbiAttrKind::AssertEq => { + let ty::Tuple(fields) = ty.kind() else { + span_bug!( + attr_span, + "`#[rustc_abi(assert_eq)]` on a type alias requires pair type" ); - let ty::FnPtr(sig_tys2, hdr2) = field2.kind() else { - span_bug!( - meta_item.span(), - "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types" - ); - }; - let abi2 = unwrap_fn_abi( - tcx.fn_abi_of_fn_ptr(typing_env.as_query_input(( - sig_tys2.with(*hdr2), - /* extra_args */ ty::List::empty(), - ))), - tcx, - item_def_id, + }; + let [field1, field2] = ***fields else { + span_bug!( + attr_span, + "`#[rustc_abi(assert_eq)]` on a type alias requires pair type" ); + }; + let ty::FnPtr(sig_tys1, hdr1) = field1.kind() else { + span_bug!( + attr_span, + "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types" + ); + }; + let abi1 = unwrap_fn_abi( + tcx.fn_abi_of_fn_ptr(typing_env.as_query_input(( + sig_tys1.with(*hdr1), + /* extra_args */ ty::List::empty(), + ))), + tcx, + item_def_id, + ); + let ty::FnPtr(sig_tys2, hdr2) = field2.kind() else { + span_bug!( + attr_span, + "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types" + ); + }; + let abi2 = unwrap_fn_abi( + tcx.fn_abi_of_fn_ptr(typing_env.as_query_input(( + sig_tys2.with(*hdr2), + /* extra_args */ ty::List::empty(), + ))), + tcx, + item_def_id, + ); - if !test_abi_eq(abi1, abi2) { - tcx.dcx().emit_err(AbiNe { - span, - left: format!("{:#?}", abi1), - right: format!("{:#?}", abi2), - }); - } - } - _ => { - tcx.dcx().emit_err(UnrecognizedArgument { span: meta_item.span() }); + if !test_abi_eq(abi1, abi2) { + tcx.dcx().emit_err(AbiNe { + span, + left: format!("{:#?}", abi1), + right: format!("{:#?}", abi2), + }); } } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 0d64e30d9c79..08f2597e874d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -15,7 +15,7 @@ use rustc_attr_parsing::{AttributeParser, Late}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; +use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey, inline_fluent}; use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, @@ -54,23 +54,23 @@ use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; use rustc_trait_selection::traits::ObligationCtxt; use tracing::debug; -use crate::{errors, fluent_generated as fluent}; +use crate::errors; #[derive(LintDiagnostic)] -#[diag(passes_diagnostic_diagnostic_on_unimplemented_only_for_traits)] +#[diag("`#[diagnostic::on_unimplemented]` can only be applied to trait definitions")] struct DiagnosticOnUnimplementedOnlyForTraits; #[derive(LintDiagnostic)] -#[diag(passes_diagnostic_diagnostic_on_const_only_for_trait_impls)] +#[diag("`#[diagnostic::on_const]` can only be applied to trait impls")] struct DiagnosticOnConstOnlyForTraitImpls { - #[label] + #[label("not a trait impl")] item_span: Span, } #[derive(LintDiagnostic)] -#[diag(passes_diagnostic_diagnostic_on_const_only_for_non_const_trait_impls)] +#[diag("`#[diagnostic::on_const]` can only be applied to non-const trait impls")] struct DiagnosticOnConstOnlyForNonConstTraitImpls { - #[label] + #[label("this is a const trait impl")] item_span: Span, } @@ -282,24 +282,32 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::PatternComplexityLimit { .. } | AttributeKind::PinV2(..) | AttributeKind::Pointee(..) + | AttributeKind::PreludeImport | AttributeKind::ProfilerRuntime | AttributeKind::RecursionLimit { .. } | AttributeKind::ReexportTestHarnessMain(..) // handled below this loop and elsewhere | AttributeKind::Repr { .. } + | AttributeKind::RustcAbi { .. } | AttributeKind::RustcAllocator | AttributeKind::RustcAllocatorZeroed | AttributeKind::RustcAllocatorZeroedVariant { .. } | AttributeKind::RustcAsPtr(..) | AttributeKind::RustcBodyStability { .. } | AttributeKind::RustcBuiltinMacro { .. } + | AttributeKind::RustcCaptureAnalysis + | AttributeKind::RustcCguTestAttr(..) | AttributeKind::RustcClean(..) | AttributeKind::RustcCoherenceIsCore(..) | AttributeKind::RustcCoinductive(..) | AttributeKind::RustcConfusables { .. } | AttributeKind::RustcConstStabilityIndirect + | AttributeKind::RustcConversionSuggestion | AttributeKind::RustcDeallocator + | AttributeKind::RustcDefPath(..) + | AttributeKind::RustcDelayedBugFromInsideQuery | AttributeKind::RustcDenyExplicitImpl(..) + | AttributeKind::RustcDeprecatedSafe2024 {..} | AttributeKind::RustcDummy | AttributeKind::RustcDumpDefParents | AttributeKind::RustcDumpItemBounds @@ -307,9 +315,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcDumpUserArgs | AttributeKind::RustcDumpVtable(..) | AttributeKind::RustcDynIncompatibleTrait(..) + | AttributeKind::RustcEffectiveVisibility + | AttributeKind::RustcEvaluateWhereClauses | AttributeKind::RustcHasIncoherentInherentImpls | AttributeKind::RustcHiddenTypeOfOpaques | AttributeKind::RustcIfThisChanged(..) + | AttributeKind::RustcInsignificantDtor + | AttributeKind::RustcIntrinsic + | AttributeKind::RustcIntrinsicConstStableIndirect | AttributeKind::RustcLayout(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) | AttributeKind::RustcLayoutScalarValidRangeStart(..) @@ -321,30 +334,39 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcMain | AttributeKind::RustcMir(_) | AttributeKind::RustcNeverReturnsNullPointer + | AttributeKind::RustcNeverTypeOptions {..} | AttributeKind::RustcNoImplicitAutorefs + | AttributeKind::RustcNoImplicitBounds + | AttributeKind::RustcNoMirInline | AttributeKind::RustcNonConstTraitMethod | AttributeKind::RustcNounwind | AttributeKind::RustcObjcClass { .. } | AttributeKind::RustcObjcSelector { .. } | AttributeKind::RustcOffloadKernel + | AttributeKind::RustcOutlives | AttributeKind::RustcParenSugar(..) | AttributeKind::RustcPassByValue (..) | AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) | AttributeKind::RustcPreserveUbChecks | AttributeKind::RustcReallocator + | AttributeKind::RustcRegions + | AttributeKind::RustcReservationImpl(..) | AttributeKind::RustcScalableVector { .. } | AttributeKind::RustcShouldNotBeCalledOnConstItems(..) | AttributeKind::RustcSimdMonomorphizeLaneLimit(..) | AttributeKind::RustcSkipDuringMethodDispatch { .. } | AttributeKind::RustcSpecializationTrait(..) | AttributeKind::RustcStdInternalSymbol (..) + | AttributeKind::RustcStrictCoherence(..) + | AttributeKind::RustcSymbolName(..) | AttributeKind::RustcThenThisWouldNeed(..) + | AttributeKind::RustcTrivialFieldReads | AttributeKind::RustcUnsafeSpecializationMarker(..) | AttributeKind::RustcVariance | AttributeKind::RustcVarianceOfOpaques | AttributeKind::ShouldPanic { .. } + | AttributeKind::TestRunner(..) | AttributeKind::ThreadLocal - | AttributeKind::TypeConst{..} | AttributeKind::TypeLengthLimit { .. } | AttributeKind::UnstableFeatureBound(..) | AttributeKind::Used { .. } @@ -373,49 +395,24 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // need to be fixed | sym::deprecated_safe // FIXME(deprecated_safe) // internal - | sym::prelude_import | sym::panic_handler | sym::lang | sym::default_lib_allocator | sym::rustc_diagnostic_item - | sym::rustc_no_mir_inline - | sym::rustc_insignificant_dtor | sym::rustc_nonnull_optimization_guaranteed - | sym::rustc_intrinsic | sym::rustc_inherit_overflow_checks - | sym::rustc_intrinsic_const_stable_indirect - | sym::rustc_trivial_field_reads | sym::rustc_on_unimplemented | sym::rustc_do_not_const_check - | sym::rustc_reservation_impl | sym::rustc_doc_primitive - | sym::rustc_conversion_suggestion - | sym::rustc_deprecated_safe_2024 | sym::rustc_test_marker - | sym::rustc_abi | sym::rustc_layout | sym::rustc_proc_macro_decls - | sym::rustc_never_type_options | sym::rustc_autodiff | sym::rustc_capture_analysis - | sym::rustc_regions - | sym::rustc_strict_coherence | sym::rustc_mir - | sym::rustc_effective_visibility - | sym::rustc_outlives - | sym::rustc_symbol_name - | sym::rustc_evaluate_where_clauses - | sym::rustc_delayed_bug_from_inside_query - | sym::rustc_def_path - | sym::rustc_partition_reused - | sym::rustc_partition_codegened - | sym::rustc_expected_cgu_reuse // crate-level attrs, are checked below | sym::feature - | sym::register_tool - | sym::rustc_no_implicit_bounds - | sym::test_runner, - .. + | sym::register_tool, .. ] => {} [name, rest@..] => { match BUILTIN_ATTRIBUTE_MAP.get(name) { @@ -1010,8 +1007,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { for (inline2, span2) in rest { if inline2 != inline { let mut spans = MultiSpan::from_spans(vec![*span, *span2]); - spans.push_span_label(*span, fluent::passes_doc_inline_conflict_first); - spans.push_span_label(*span2, fluent::passes_doc_inline_conflict_second); + spans.push_span_label(*span, inline_fluent!("this attribute...")); + spans.push_span_label( + *span2, + inline_fluent!("{\".\"}..conflicts with this attribute"), + ); self.dcx().emit_err(errors::DocInlineConflict { spans }); return; } @@ -1155,7 +1155,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &self.tcx.sess, sym::rustdoc_internals, *span, - fluent::passes_doc_rust_logo, + inline_fluent!("the `#[doc(rust_logo)]` attribute is used for Rust branding"), ) .emit(); } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 3294b6802a71..75bac9eff2c7 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -10,10 +10,11 @@ use hir::def_id::{LocalDefIdMap, LocalDefIdSet}; use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{ErrorGuaranteed, MultiSpan}; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Node, PatKind, QPath}; +use rustc_hir::{self as hir, Node, PatKind, QPath, find_attr}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; @@ -380,7 +381,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && let impl_of = self.tcx.parent(impl_item.owner_id.to_def_id()) && self.tcx.is_automatically_derived(impl_of) && let trait_ref = self.tcx.impl_trait_ref(impl_of).instantiate_identity() - && self.tcx.has_attr(trait_ref.def_id, sym::rustc_trivial_field_reads) + && find_attr!( + self.tcx.get_all_attrs(trait_ref.def_id), + AttributeKind::RustcTrivialFieldReads + ) { if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() && let Some(adt_def_id) = adt_def.did().as_local() diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index d0fa1c4f0e03..f420bba9b4e9 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, - MultiSpan, + MultiSpan, inline_fluent, }; use rustc_hir::Target; use rustc_hir::attrs::{MirDialect, MirPhase}; @@ -13,80 +13,84 @@ use rustc_middle::ty::{MainDefinition, Ty}; use rustc_span::{DUMMY_SP, Span, Symbol}; use crate::check_attr::ProcMacroKind; -use crate::fluent_generated as fluent; use crate::lang_items::Duplicate; #[derive(LintDiagnostic)] -#[diag(passes_incorrect_do_not_recommend_location)] +#[diag("`#[diagnostic::do_not_recommend]` can only be placed on trait implementations")] pub(crate) struct IncorrectDoNotRecommendLocation; #[derive(Diagnostic)] -#[diag(passes_autodiff_attr)] +#[diag("`#[autodiff]` should be applied to a function")] pub(crate) struct AutoDiffAttr { #[primary_span] - #[label] + #[label("not a function")] pub attr_span: Span, } #[derive(Diagnostic)] -#[diag(passes_loop_match_attr)] +#[diag("`#[loop_match]` should be applied to a loop")] pub(crate) struct LoopMatchAttr { #[primary_span] pub attr_span: Span, - #[label] + #[label("not a loop")] pub node_span: Span, } #[derive(Diagnostic)] -#[diag(passes_const_continue_attr)] +#[diag("`#[const_continue]` should be applied to a break expression")] pub(crate) struct ConstContinueAttr { #[primary_span] pub attr_span: Span, - #[label] + #[label("not a break expression")] pub node_span: Span, } #[derive(LintDiagnostic)] -#[diag(passes_mixed_export_name_and_no_mangle)] +#[diag("`{$no_mangle_attr}` attribute may not be used in combination with `{$export_name_attr}`")] pub(crate) struct MixedExportNameAndNoMangle { - #[label] - #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] + #[label("`{$no_mangle_attr}` is ignored")] + #[suggestion( + "remove the `{$no_mangle_attr}` attribute", + style = "verbose", + code = "", + applicability = "machine-applicable" + )] pub no_mangle_span: Span, - #[note] + #[note("`{$export_name_attr}` takes precedence")] pub export_name_span: Span, pub no_mangle_attr: &'static str, pub export_name_attr: &'static str, } #[derive(LintDiagnostic)] -#[diag(passes_outer_crate_level_attr)] +#[diag("crate-level attribute should be an inner attribute")] pub(crate) struct OuterCrateLevelAttr { #[subdiagnostic] pub suggestion: OuterCrateLevelAttrSuggestion, } #[derive(Subdiagnostic)] -#[multipart_suggestion(passes_outer_crate_level_attr_suggestion, style = "verbose")] +#[multipart_suggestion("add a `!`", style = "verbose")] pub(crate) struct OuterCrateLevelAttrSuggestion { #[suggestion_part(code = "!")] pub bang_position: Span, } #[derive(LintDiagnostic)] -#[diag(passes_inner_crate_level_attr)] +#[diag("crate-level attribute should be in the root module")] pub(crate) struct InnerCrateLevelAttr; #[derive(Diagnostic)] -#[diag(passes_non_exhaustive_with_default_field_values)] +#[diag("`#[non_exhaustive]` can't be used to annotate items with default field values")] pub(crate) struct NonExhaustiveWithDefaultFieldValues { #[primary_span] pub attr_span: Span, - #[label] + #[label("this struct has default field values")] pub defn_span: Span, } #[derive(Diagnostic)] -#[diag(passes_doc_alias_bad_location)] +#[diag("`#[doc(alias = \"...\")]` isn't allowed on {$location}")] pub(crate) struct DocAliasBadLocation<'a> { #[primary_span] pub span: Span, @@ -94,7 +98,7 @@ pub(crate) struct DocAliasBadLocation<'a> { } #[derive(Diagnostic)] -#[diag(passes_doc_alias_not_an_alias)] +#[diag("`#[doc(alias = \"{$attr_str}\"]` is the same as the item's name")] pub(crate) struct DocAliasNotAnAlias { #[primary_span] pub span: Span, @@ -102,7 +106,7 @@ pub(crate) struct DocAliasNotAnAlias { } #[derive(Diagnostic)] -#[diag(passes_doc_keyword_attribute_empty_mod)] +#[diag("`#[doc({$attr_name} = \"...\")]` should be used on empty modules")] pub(crate) struct DocKeywordAttributeEmptyMod { #[primary_span] pub span: Span, @@ -110,7 +114,7 @@ pub(crate) struct DocKeywordAttributeEmptyMod { } #[derive(Diagnostic)] -#[diag(passes_doc_keyword_attribute_not_mod)] +#[diag("`#[doc({$attr_name} = \"...\")]` should be used on modules")] pub(crate) struct DocKeywordAttributeNotMod { #[primary_span] pub span: Span, @@ -118,115 +122,131 @@ pub(crate) struct DocKeywordAttributeNotMod { } #[derive(Diagnostic)] -#[diag(passes_doc_fake_variadic_not_valid)] +#[diag( + "`#[doc(fake_variadic)]` must be used on the first of a set of tuple or fn pointer trait impls with varying arity" +)] pub(crate) struct DocFakeVariadicNotValid { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_doc_keyword_only_impl)] +#[diag("`#[doc(keyword = \"...\")]` should be used on impl blocks")] pub(crate) struct DocKeywordOnlyImpl { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_doc_search_unbox_invalid)] +#[diag("`#[doc(search_unbox)]` should be used on generic structs and enums")] pub(crate) struct DocSearchUnboxInvalid { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_doc_inline_conflict)] -#[help] +#[diag("conflicting doc inlining attributes")] +#[help("remove one of the conflicting attributes")] pub(crate) struct DocInlineConflict { #[primary_span] pub spans: MultiSpan, } #[derive(LintDiagnostic)] -#[diag(passes_doc_inline_only_use)] -#[note] +#[diag("this attribute can only be applied to a `use` item")] +#[note( + "read for more information" +)] pub(crate) struct DocInlineOnlyUse { - #[label] + #[label("only applicable on `use` items")] pub attr_span: Span, - #[label(passes_not_a_use_item_label)] + #[label("not a `use` item")] pub item_span: Span, } #[derive(LintDiagnostic)] -#[diag(passes_doc_masked_only_extern_crate)] -#[note] +#[diag("this attribute can only be applied to an `extern crate` item")] +#[note( + "read for more information" +)] pub(crate) struct DocMaskedOnlyExternCrate { - #[label] + #[label("only applicable on `extern crate` items")] pub attr_span: Span, - #[label(passes_not_an_extern_crate_label)] + #[label("not an `extern crate` item")] pub item_span: Span, } #[derive(LintDiagnostic)] -#[diag(passes_doc_masked_not_extern_crate_self)] +#[diag("this attribute cannot be applied to an `extern crate self` item")] pub(crate) struct DocMaskedNotExternCrateSelf { - #[label] + #[label("not applicable on `extern crate self` items")] pub attr_span: Span, - #[label(passes_extern_crate_self_label)] + #[label("`extern crate self` defined here")] pub item_span: Span, } #[derive(Diagnostic)] -#[diag(passes_both_ffi_const_and_pure, code = E0757)] +#[diag("`#[ffi_const]` function cannot be `#[ffi_pure]`", code = E0757)] pub(crate) struct BothFfiConstAndPure { #[primary_span] pub attr_span: Span, } #[derive(LintDiagnostic)] -#[diag(passes_link)] -#[warning] +#[diag("attribute should be applied to an `extern` block with non-Rust ABI")] +#[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] pub(crate) struct Link { - #[label] + #[label("not an `extern` block")] pub span: Option, } #[derive(Diagnostic)] -#[diag(passes_rustc_legacy_const_generics_only)] +#[diag("#[rustc_legacy_const_generics] functions must only have const generics")] pub(crate) struct RustcLegacyConstGenericsOnly { #[primary_span] pub attr_span: Span, - #[label] + #[label("non-const generic parameter")] pub param_span: Span, } #[derive(Diagnostic)] -#[diag(passes_rustc_legacy_const_generics_index)] +#[diag("#[rustc_legacy_const_generics] must have one index for each generic parameter")] pub(crate) struct RustcLegacyConstGenericsIndex { #[primary_span] pub attr_span: Span, - #[label] + #[label("generic parameters")] pub generics_span: Span, } #[derive(Diagnostic)] -#[diag(passes_rustc_legacy_const_generics_index_exceed)] +#[diag("index exceeds number of arguments")] pub(crate) struct RustcLegacyConstGenericsIndexExceed { #[primary_span] - #[label] + #[label( + "there {$arg_count -> + [one] is + *[other] are + } only {$arg_count} {$arg_count -> + [one] argument + *[other] arguments + }" + )] pub span: Span, pub arg_count: usize, } #[derive(Diagnostic)] -#[diag(passes_repr_conflicting, code = E0566)] +#[diag("conflicting representation hints", code = E0566)] pub(crate) struct ReprConflicting { #[primary_span] pub hint_spans: Vec, } #[derive(Diagnostic)] -#[diag(passes_repr_align_greater_than_target_max, code = E0589)] -#[note] +#[diag("alignment must not be greater than `isize::MAX` bytes", code = E0589)] +#[note("`isize::MAX` is {$size} for the current target")] pub(crate) struct InvalidReprAlignForTarget { #[primary_span] pub span: Span, @@ -234,20 +254,20 @@ pub(crate) struct InvalidReprAlignForTarget { } #[derive(LintDiagnostic)] -#[diag(passes_repr_conflicting, code = E0566)] +#[diag("conflicting representation hints", code = E0566)] pub(crate) struct ReprConflictingLint; #[derive(Diagnostic)] -#[diag(passes_macro_only_attribute)] +#[diag("attribute should be applied to a macro")] pub(crate) struct MacroOnlyAttribute { #[primary_span] pub attr_span: Span, - #[label] + #[label("not a macro")] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_debug_visualizer_unreadable)] +#[diag("couldn't read {$file}: {$error}")] pub(crate) struct DebugVisualizerUnreadable<'a> { #[primary_span] pub span: Span, @@ -256,106 +276,114 @@ pub(crate) struct DebugVisualizerUnreadable<'a> { } #[derive(Diagnostic)] -#[diag(passes_rustc_allow_const_fn_unstable)] +#[diag("attribute should be applied to `const fn`")] pub(crate) struct RustcAllowConstFnUnstable { #[primary_span] pub attr_span: Span, - #[label] + #[label("not a `const fn`")] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_rustc_pub_transparent)] +#[diag("attribute should be applied to `#[repr(transparent)]` types")] pub(crate) struct RustcPubTransparent { #[primary_span] pub attr_span: Span, - #[label] + #[label("not a `#[repr(transparent)]` type")] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_rustc_force_inline_coro)] +#[diag("attribute cannot be applied to a `async`, `gen` or `async gen` function")] pub(crate) struct RustcForceInlineCoro { #[primary_span] pub attr_span: Span, - #[label] + #[label("`async`, `gen` or `async gen` function")] pub span: Span, } #[derive(LintDiagnostic)] pub(crate) enum MacroExport { - #[diag(passes_macro_export_on_decl_macro)] - #[note] + #[diag("`#[macro_export]` has no effect on declarative macro definitions")] + #[note("declarative macros follow the same exporting rules as regular items")] OnDeclMacro, } #[derive(Subdiagnostic)] pub(crate) enum UnusedNote { - #[note(passes_unused_empty_lints_note)] + #[note("attribute `{$name}` with an empty list has no effect")] EmptyList { name: Symbol }, - #[note(passes_unused_no_lints_note)] + #[note("attribute `{$name}` without any lints has no effect")] NoLints { name: Symbol }, - #[note(passes_unused_default_method_body_const_note)] + #[note("`default_method_body_is_const` has been replaced with `const` on traits")] DefaultMethodBodyConst, - #[note(passes_unused_linker_messages_note)] + #[note( + "the `linker_messages` lint can only be controlled at the root of a crate that needs to be linked" + )] LinkerMessagesBinaryCrateOnly, } #[derive(LintDiagnostic)] -#[diag(passes_unused)] +#[diag("unused attribute")] pub(crate) struct Unused { - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] pub attr_span: Span, #[subdiagnostic] pub note: UnusedNote, } #[derive(Diagnostic)] -#[diag(passes_non_exported_macro_invalid_attrs, code = E0518)] +#[diag("attribute should be applied to function or closure", code = E0518)] pub(crate) struct NonExportedMacroInvalidAttrs { #[primary_span] - #[label] + #[label("not a function or closure")] pub attr_span: Span, } #[derive(Diagnostic)] -#[diag(passes_may_dangle)] +#[diag("`#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl")] pub(crate) struct InvalidMayDangle { #[primary_span] pub attr_span: Span, } #[derive(LintDiagnostic)] -#[diag(passes_unused_duplicate)] +#[diag("unused attribute")] pub(crate) struct UnusedDuplicate { - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] pub this: Span, - #[note] + #[note("attribute also specified here")] pub other: Span, - #[warning] + #[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" + )] pub warning: bool, } #[derive(Diagnostic)] -#[diag(passes_unused_multiple)] +#[diag("multiple `{$name}` attributes")] pub(crate) struct UnusedMultiple { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] pub this: Span, - #[note] + #[note("attribute also specified here")] pub other: Span, pub name: Symbol, } #[derive(LintDiagnostic)] -#[diag(passes_deprecated_annotation_has_no_effect)] +#[diag("this `#[deprecated]` annotation has no effect")] pub(crate) struct DeprecatedAnnotationHasNoEffect { - #[suggestion(applicability = "machine-applicable", code = "")] + #[suggestion( + "remove the unnecessary deprecation attribute", + applicability = "machine-applicable", + code = "" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_unknown_external_lang_item, code = E0264)] +#[diag("unknown external lang item: `{$lang_item}`", code = E0264)] pub(crate) struct UnknownExternLangItem { #[primary_span] pub span: Span, @@ -363,48 +391,74 @@ pub(crate) struct UnknownExternLangItem { } #[derive(Diagnostic)] -#[diag(passes_missing_panic_handler)] +#[diag("`#[panic_handler]` function required, but not found")] pub(crate) struct MissingPanicHandler; #[derive(Diagnostic)] -#[diag(passes_panic_unwind_without_std)] -#[help] -#[note] +#[diag("unwinding panics are not supported without std")] +#[help("using nightly cargo, use -Zbuild-std with panic=\"abort\" to avoid unwinding")] +#[note( + "since the core library is usually precompiled with panic=\"unwind\", rebuilding your crate with panic=\"abort\" may not be enough to fix the problem" +)] pub(crate) struct PanicUnwindWithoutStd; #[derive(Diagnostic)] -#[diag(passes_missing_lang_item)] -#[note] -#[help] +#[diag("lang item required, but not found: `{$name}`")] +#[note( + "this can occur when a binary crate with `#![no_std]` is compiled for a target where `{$name}` is defined in the standard library" +)] +#[help( + "you may be able to compile for a target that doesn't need `{$name}`, specify a target with `--target` or in `.cargo/config`" +)] pub(crate) struct MissingLangItem { pub name: Symbol, } #[derive(Diagnostic)] -#[diag(passes_lang_item_fn_with_track_caller)] +#[diag( + "{$name -> + [panic_impl] `#[panic_handler]` + *[other] `{$name}` lang item +} function is not allowed to have `#[track_caller]`" +)] pub(crate) struct LangItemWithTrackCaller { #[primary_span] pub attr_span: Span, pub name: Symbol, - #[label] + #[label( + "{$name -> + [panic_impl] `#[panic_handler]` + *[other] `{$name}` lang item + } function is not allowed to have `#[track_caller]`" + )] pub sig_span: Span, } #[derive(Diagnostic)] -#[diag(passes_lang_item_fn_with_target_feature)] +#[diag( + "{$name -> + [panic_impl] `#[panic_handler]` + *[other] `{$name}` lang item + } function is not allowed to have `#[target_feature]`" +)] pub(crate) struct LangItemWithTargetFeature { #[primary_span] pub attr_span: Span, pub name: Symbol, - #[label] + #[label( + "{$name -> + [panic_impl] `#[panic_handler]` + *[other] `{$name}` lang item + } function is not allowed to have `#[target_feature]`" + )] pub sig_span: Span, } #[derive(Diagnostic)] -#[diag(passes_lang_item_on_incorrect_target, code = E0718)] +#[diag("`{$name}` lang item must be applied to a {$expected_target}", code = E0718)] pub(crate) struct LangItemOnIncorrectTarget { #[primary_span] - #[label] + #[label("attribute should be applied to a {$expected_target}, not a {$actual_target}")] pub span: Span, pub name: Symbol, pub expected_target: Target, @@ -412,10 +466,10 @@ pub(crate) struct LangItemOnIncorrectTarget { } #[derive(Diagnostic)] -#[diag(passes_unknown_lang_item, code = E0522)] +#[diag("definition of an unknown lang item: `{$name}`", code = E0522)] pub(crate) struct UnknownLangItem { #[primary_span] - #[label] + #[label("definition of unknown lang item `{$name}`")] pub span: Span, pub name: Symbol, } @@ -436,7 +490,11 @@ pub(crate) struct ItemFollowingInnerAttr { impl Diagnostic<'_, G> for InvalidAttrAtCrateLevel { #[track_caller] fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { - let mut diag = Diag::new(dcx, level, fluent::passes_invalid_attr_at_crate_level); + let mut diag = Diag::new( + dcx, + level, + inline_fluent!("`{$name}` attribute cannot be used at crate level"), + ); diag.span(self.span); diag.arg("name", self.name); // Only emit an error with a suggestion if we can create a string out @@ -444,27 +502,30 @@ impl Diagnostic<'_, G> for InvalidAttrAtCrateLevel { if let Some(span) = self.sugg_span { diag.span_suggestion_verbose( span, - fluent::passes_suggestion, + inline_fluent!("perhaps you meant to use an outer attribute"), String::new(), Applicability::MachineApplicable, ); } if let Some(item) = self.item { diag.arg("kind", item.kind); - diag.span_label(item.span, fluent::passes_invalid_attr_at_crate_level_item); + diag.span_label( + item.span, + inline_fluent!("the inner attribute doesn't annotate this {$kind}"), + ); } diag } } #[derive(Diagnostic)] -#[diag(passes_duplicate_diagnostic_item_in_crate)] +#[diag("duplicate diagnostic item in crate `{$crate_name}`: `{$name}`")] pub(crate) struct DuplicateDiagnosticItemInCrate { #[primary_span] pub duplicate_span: Option, - #[note(passes_diagnostic_item_first_defined)] + #[note("the diagnostic item is first defined here")] pub orig_span: Option, - #[note] + #[note("the diagnostic item is first defined in crate `{$orig_crate_name}`")] pub different_crates: bool, pub crate_name: Symbol, pub orig_crate_name: Symbol, @@ -472,7 +533,7 @@ pub(crate) struct DuplicateDiagnosticItemInCrate { } #[derive(Diagnostic)] -#[diag(passes_layout_abi)] +#[diag("abi: {$abi}")] pub(crate) struct LayoutAbi { #[primary_span] pub span: Span, @@ -480,7 +541,7 @@ pub(crate) struct LayoutAbi { } #[derive(Diagnostic)] -#[diag(passes_layout_align)] +#[diag("align: {$align}")] pub(crate) struct LayoutAlign { #[primary_span] pub span: Span, @@ -488,7 +549,7 @@ pub(crate) struct LayoutAlign { } #[derive(Diagnostic)] -#[diag(passes_layout_size)] +#[diag("size: {$size}")] pub(crate) struct LayoutSize { #[primary_span] pub span: Span, @@ -496,7 +557,7 @@ pub(crate) struct LayoutSize { } #[derive(Diagnostic)] -#[diag(passes_layout_homogeneous_aggregate)] +#[diag("homogeneous_aggregate: {$homogeneous_aggregate}")] pub(crate) struct LayoutHomogeneousAggregate { #[primary_span] pub span: Span, @@ -504,7 +565,7 @@ pub(crate) struct LayoutHomogeneousAggregate { } #[derive(Diagnostic)] -#[diag(passes_layout_of)] +#[diag("layout_of({$normalized_ty}) = {$ty_layout}")] pub(crate) struct LayoutOf<'tcx> { #[primary_span] pub span: Span, @@ -513,7 +574,7 @@ pub(crate) struct LayoutOf<'tcx> { } #[derive(Diagnostic)] -#[diag(passes_abi_of)] +#[diag("fn_abi_of({$fn_name}) = {$fn_abi}")] pub(crate) struct AbiOf { #[primary_span] pub span: Span, @@ -522,7 +583,11 @@ pub(crate) struct AbiOf { } #[derive(Diagnostic)] -#[diag(passes_abi_ne)] +#[diag( + "ABIs are not compatible + left ABI = {$left} + right ABI = {$right}" +)] pub(crate) struct AbiNe { #[primary_span] pub span: Span, @@ -531,21 +596,23 @@ pub(crate) struct AbiNe { } #[derive(Diagnostic)] -#[diag(passes_abi_invalid_attribute)] +#[diag( + "`#[rustc_abi]` can only be applied to function items, type aliases, and associated functions" +)] pub(crate) struct AbiInvalidAttribute { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_unrecognized_argument)] +#[diag("unrecognized argument")] pub(crate) struct UnrecognizedArgument { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_feature_stable_twice, code = E0711)] +#[diag("feature `{$feature}` is declared stable since {$since}, but was previously declared stable since {$prev_since}", code = E0711)] pub(crate) struct FeatureStableTwice { #[primary_span] pub span: Span, @@ -555,7 +622,7 @@ pub(crate) struct FeatureStableTwice { } #[derive(Diagnostic)] -#[diag(passes_feature_previously_declared, code = E0711)] +#[diag("feature `{$feature}` is declared {$declared}, but was previously declared {$prev_declared}", code = E0711)] pub(crate) struct FeaturePreviouslyDeclared<'a> { #[primary_span] pub span: Span, @@ -565,18 +632,18 @@ pub(crate) struct FeaturePreviouslyDeclared<'a> { } #[derive(Diagnostic)] -#[diag(passes_multiple_rustc_main, code = E0137)] +#[diag("multiple functions with a `#[rustc_main]` attribute", code = E0137)] pub(crate) struct MultipleRustcMain { #[primary_span] pub span: Span, - #[label(passes_first)] + #[label("first `#[rustc_main]` function")] pub first: Span, - #[label(passes_additional)] + #[label("additional `#[rustc_main]` function")] pub additional: Span, } #[derive(Diagnostic)] -#[diag(passes_extern_main)] +#[diag("the `main` function cannot be declared in an `extern` block")] pub(crate) struct ExternMain { #[primary_span] pub span: Span, @@ -596,7 +663,11 @@ pub(crate) struct NoMainErr { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NoMainErr { #[track_caller] fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - let mut diag = Diag::new(dcx, level, fluent::passes_no_main_function); + let mut diag = Diag::new( + dcx, + level, + inline_fluent!("`main` function not found in crate `{$crate_name}`"), + ); diag.span(DUMMY_SP); diag.code(E0601); diag.arg("crate_name", self.crate_name); @@ -604,16 +675,23 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NoMainErr { diag.arg("has_filename", self.has_filename); let note = if !self.non_main_fns.is_empty() { for &span in &self.non_main_fns { - diag.span_note(span, fluent::passes_here_is_main); + diag.span_note(span, inline_fluent!("here is a function named `main`")); } - diag.note(fluent::passes_one_or_more_possible_main); - diag.help(fluent::passes_consider_moving_main); + diag.note(inline_fluent!( + "you have one or more functions named `main` not defined at the crate level" + )); + diag.help(inline_fluent!("consider moving the `main` function definitions")); // There were some functions named `main` though. Try to give the user a hint. - fluent::passes_main_must_be_defined_at_crate + inline_fluent!( + "the main function must be defined at the crate level{$has_filename -> + [true] {\" \"}(in `{$filename}`) + *[false] {\"\"} + }" + ) } else if self.has_filename { - fluent::passes_consider_adding_main_to_file + inline_fluent!("consider adding a `main` function to `{$filename}`") } else { - fluent::passes_consider_adding_main_at_crate + inline_fluent!("consider adding a `main` function at the crate level") }; if self.file_empty { diag.note(note); @@ -626,11 +704,14 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NoMainErr { && main_def.opt_fn_def_id().is_none() { // There is something at `crate::main`, but it is not a function definition. - diag.span_label(main_def.span, fluent::passes_non_function_main); + diag.span_label( + main_def.span, + inline_fluent!("non-function item at `crate::main` is found"), + ); } if self.add_teach_note { - diag.note(fluent::passes_teach_note); + diag.note(inline_fluent!("if you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/")); } diag } @@ -658,9 +739,13 @@ impl Diagnostic<'_, G> for DuplicateLangItem { dcx, level, match self.duplicate { - Duplicate::Plain => fluent::passes_duplicate_lang_item, - Duplicate::Crate => fluent::passes_duplicate_lang_item_crate, - Duplicate::CrateDepends => fluent::passes_duplicate_lang_item_crate_depends, + Duplicate::Plain => inline_fluent!("found duplicate lang item `{$lang_item_name}`"), + Duplicate::Crate => inline_fluent!( + "duplicate lang item in crate `{$crate_name}`: `{$lang_item_name}`" + ), + Duplicate::CrateDepends => inline_fluent!( + "duplicate lang item in crate `{$crate_name}` (which `{$dependency_of}` depends on): `{$lang_item_name}`" + ), }, ); diag.code(E0152); @@ -681,24 +766,32 @@ impl Diagnostic<'_, G> for DuplicateLangItem { diag.span(span); } if let Some(span) = self.first_defined_span { - diag.span_note(span, fluent::passes_first_defined_span); + diag.span_note(span, inline_fluent!("the lang item is first defined here")); } else { if self.orig_dependency_of.is_none() { - diag.note(fluent::passes_first_defined_crate); + diag.note(inline_fluent!( + "the lang item is first defined in crate `{$orig_crate_name}`" + )); } else { - diag.note(fluent::passes_first_defined_crate_depends); + diag.note(inline_fluent!("the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on)")); } if self.orig_is_local { - diag.note(fluent::passes_first_definition_local); + diag.note(inline_fluent!( + "first definition in the local crate (`{$orig_crate_name}`)" + )); } else { - diag.note(fluent::passes_first_definition_path); + diag.note(inline_fluent!( + "first definition in `{$orig_crate_name}` loaded from {$orig_path}" + )); } if self.is_local { - diag.note(fluent::passes_second_definition_local); + diag.note(inline_fluent!("second definition in the local crate (`{$crate_name}`)")); } else { - diag.note(fluent::passes_second_definition_path); + diag.note(inline_fluent!( + "second definition in `{$crate_name}` loaded from {$path}" + )); } } diag @@ -706,11 +799,22 @@ impl Diagnostic<'_, G> for DuplicateLangItem { } #[derive(Diagnostic)] -#[diag(passes_incorrect_target, code = E0718)] +#[diag("`{$name}` lang item must be applied to a {$kind} with {$at_least -> + [true] at least {$num} + *[false] {$num} + } generic {$num -> + [one] argument + *[other] arguments + }", code = E0718)] pub(crate) struct IncorrectTarget<'a> { #[primary_span] pub span: Span, - #[label] + #[label( + "this {$kind} has {$actual_num} generic {$actual_num -> + [one] argument + *[other] arguments + }" + )] pub generics_span: Span, pub name: &'a str, // cannot be symbol because it renders e.g. `r#fn` instead of `fn` pub kind: &'static str, @@ -720,26 +824,33 @@ pub(crate) struct IncorrectTarget<'a> { } #[derive(Diagnostic)] -#[diag(passes_incorrect_crate_type)] +#[diag("lang items are not allowed in stable dylibs")] pub(crate) struct IncorrectCrateType { #[primary_span] pub span: Span, } #[derive(LintDiagnostic)] -#[diag(passes_useless_assignment)] +#[diag( + "useless assignment of {$is_field_assign -> + [true] field + *[false] variable + } of type `{$ty}` to itself" +)] pub(crate) struct UselessAssignment<'a> { pub is_field_assign: bool, pub ty: Ty<'a>, } #[derive(LintDiagnostic)] -#[diag(passes_inline_ignored_for_exported)] -#[help] +#[diag("`#[inline]` is ignored on externally exported functions")] +#[help( + "externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]`" +)] pub(crate) struct InlineIgnoredForExported {} #[derive(Diagnostic)] -#[diag(passes_object_lifetime_err)] +#[diag("{$repr}")] pub(crate) struct ObjectLifetimeErr { #[primary_span] pub span: Span, @@ -748,38 +859,38 @@ pub(crate) struct ObjectLifetimeErr { #[derive(Diagnostic)] pub(crate) enum AttrApplication { - #[diag(passes_attr_application_enum, code = E0517)] + #[diag("attribute should be applied to an enum", code = E0517)] Enum { #[primary_span] hint_span: Span, - #[label] + #[label("not an enum")] span: Span, }, - #[diag(passes_attr_application_struct, code = E0517)] + #[diag("attribute should be applied to a struct", code = E0517)] Struct { #[primary_span] hint_span: Span, - #[label] + #[label("not a struct")] span: Span, }, - #[diag(passes_attr_application_struct_union, code = E0517)] + #[diag("attribute should be applied to a struct or union", code = E0517)] StructUnion { #[primary_span] hint_span: Span, - #[label] + #[label("not a struct or union")] span: Span, }, - #[diag(passes_attr_application_struct_enum_union, code = E0517)] + #[diag("attribute should be applied to a struct, enum, or union", code = E0517)] StructEnumUnion { #[primary_span] hint_span: Span, - #[label] + #[label("not a struct, enum, or union")] span: Span, }, } #[derive(Diagnostic)] -#[diag(passes_transparent_incompatible, code = E0692)] +#[diag("transparent {$target} cannot have other repr hints", code = E0692)] pub(crate) struct TransparentIncompatible { #[primary_span] pub hint_spans: Vec, @@ -787,45 +898,45 @@ pub(crate) struct TransparentIncompatible { } #[derive(Diagnostic)] -#[diag(passes_deprecated_attribute, code = E0549)] +#[diag("deprecated attribute must be paired with either stable or unstable attribute", code = E0549)] pub(crate) struct DeprecatedAttribute { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_useless_stability)] +#[diag("this stability annotation is useless")] pub(crate) struct UselessStability { #[primary_span] - #[label] + #[label("useless stability annotation")] pub span: Span, - #[label(passes_item)] + #[label("the stability attribute annotates this item")] pub item_sp: Span, } #[derive(Diagnostic)] -#[diag(passes_cannot_stabilize_deprecated)] +#[diag("an API can't be stabilized after it is deprecated")] pub(crate) struct CannotStabilizeDeprecated { #[primary_span] - #[label] + #[label("invalid version")] pub span: Span, - #[label(passes_item)] + #[label("the stability attribute annotates this item")] pub item_sp: Span, } #[derive(Diagnostic)] -#[diag(passes_unstable_attr_for_already_stable_feature)] +#[diag("can't mark as unstable using an already stable feature")] pub(crate) struct UnstableAttrForAlreadyStableFeature { #[primary_span] - #[label] - #[help] + #[label("this feature is already stable")] + #[help("consider removing the attribute")] pub attr_span: Span, - #[label(passes_item)] + #[label("the stability attribute annotates this item")] pub item_span: Span, } #[derive(Diagnostic)] -#[diag(passes_missing_stability_attr)] +#[diag("{$descr} has missing stability attribute")] pub(crate) struct MissingStabilityAttr<'a> { #[primary_span] pub span: Span, @@ -833,7 +944,7 @@ pub(crate) struct MissingStabilityAttr<'a> { } #[derive(Diagnostic)] -#[diag(passes_missing_const_stab_attr)] +#[diag("{$descr} has missing const stability attribute")] pub(crate) struct MissingConstStabAttr<'a> { #[primary_span] pub span: Span, @@ -841,15 +952,15 @@ pub(crate) struct MissingConstStabAttr<'a> { } #[derive(Diagnostic)] -#[diag(passes_trait_impl_const_stable)] -#[note] +#[diag("trait implementations cannot be const stable yet")] +#[note("see issue #143874 for more information")] pub(crate) struct TraitImplConstStable { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_trait_impl_const_stability_mismatch)] +#[diag("const stability on the impl does not match the const stability on the trait")] pub(crate) struct TraitImplConstStabilityMismatch { #[primary_span] pub span: Span, @@ -861,12 +972,12 @@ pub(crate) struct TraitImplConstStabilityMismatch { #[derive(Subdiagnostic)] pub(crate) enum TraitConstStability { - #[note(passes_trait_impl_const_stability_mismatch_trait_stable)] + #[note("...but the trait is stable")] Stable { #[primary_span] span: Span, }, - #[note(passes_trait_impl_const_stability_mismatch_trait_unstable)] + #[note("...but the trait is unstable")] Unstable { #[primary_span] span: Span, @@ -875,12 +986,12 @@ pub(crate) enum TraitConstStability { #[derive(Subdiagnostic)] pub(crate) enum ImplConstStability { - #[note(passes_trait_impl_const_stability_mismatch_impl_stable)] + #[note("this impl is (implicitly) stable...")] Stable { #[primary_span] span: Span, }, - #[note(passes_trait_impl_const_stability_mismatch_impl_unstable)] + #[note("this impl is unstable...")] Unstable { #[primary_span] span: Span, @@ -888,7 +999,7 @@ pub(crate) enum ImplConstStability { } #[derive(Diagnostic)] -#[diag(passes_unknown_feature, code = E0635)] +#[diag("unknown feature `{$feature}`", code = E0635)] pub(crate) struct UnknownFeature { #[primary_span] pub span: Span, @@ -899,7 +1010,7 @@ pub(crate) struct UnknownFeature { #[derive(Subdiagnostic)] #[suggestion( - passes_misspelled_feature, + "there is a feature with a similar name: `{$actual_name}`", style = "verbose", code = "{actual_name}", applicability = "maybe-incorrect" @@ -911,7 +1022,7 @@ pub(crate) struct MisspelledFeature { } #[derive(Diagnostic)] -#[diag(passes_unknown_feature_alias, code = E0635)] +#[diag("feature `{$alias}` has been renamed to `{$feature}`", code = E0635)] pub(crate) struct RenamedFeature { #[primary_span] pub span: Span, @@ -920,7 +1031,7 @@ pub(crate) struct RenamedFeature { } #[derive(Diagnostic)] -#[diag(passes_implied_feature_not_exist)] +#[diag("feature `{$implied_by}` implying `{$feature}` does not exist")] pub(crate) struct ImpliedFeatureNotExist { #[primary_span] pub span: Span, @@ -929,7 +1040,7 @@ pub(crate) struct ImpliedFeatureNotExist { } #[derive(Diagnostic)] -#[diag(passes_duplicate_feature_err, code = E0636)] +#[diag("the feature `{$feature}` has already been enabled", code = E0636)] pub(crate) struct DuplicateFeatureErr { #[primary_span] pub span: Span, @@ -937,25 +1048,37 @@ pub(crate) struct DuplicateFeatureErr { } #[derive(Diagnostic)] -#[diag(passes_missing_const_err)] +#[diag( + "attributes `#[rustc_const_unstable]`, `#[rustc_const_stable]` and `#[rustc_const_stable_indirect]` require the function or method to be `const`" +)] pub(crate) struct MissingConstErr { #[primary_span] - #[help] + #[help("make the function or method const")] pub fn_sig_span: Span, } #[derive(Diagnostic)] -#[diag(passes_const_stable_not_stable)] +#[diag( + "attribute `#[rustc_const_stable]` can only be applied to functions that are declared `#[stable]`" +)] pub(crate) struct ConstStableNotStable { #[primary_span] pub fn_sig_span: Span, - #[label] + #[label("attribute specified here")] pub const_span: Span, } #[derive(LintDiagnostic)] pub(crate) enum MultipleDeadCodes<'tcx> { - #[diag(passes_dead_codes)] + #[diag( + "{ $multiple -> + *[true] multiple {$descr}s are + [false] { $num -> + [one] {$descr} {$name_list} is + *[other] {$descr}s {$name_list} are + } + } never {$participle}" + )] DeadCodes { multiple: bool, num: usize, @@ -970,7 +1093,15 @@ pub(crate) enum MultipleDeadCodes<'tcx> { #[subdiagnostic] ignored_derived_impls: Option, }, - #[diag(passes_dead_codes)] + #[diag( + "{ $multiple -> + *[true] multiple {$descr}s are + [false] { $num -> + [one] {$descr} {$name_list} is + *[other] {$descr}s {$name_list} are + } + } never {$participle}" + )] UnusedTupleStructFields { multiple: bool, num: usize, @@ -987,7 +1118,9 @@ pub(crate) enum MultipleDeadCodes<'tcx> { } #[derive(Subdiagnostic)] -#[note(passes_enum_variant_same_name)] +#[note( + "it is impossible to refer to the {$dead_descr} `{$dead_name}` because it is shadowed by this enum variant with the same name" +)] pub(crate) struct EnumVariantSameName<'tcx> { #[primary_span] pub variant_span: Span, @@ -996,7 +1129,12 @@ pub(crate) struct EnumVariantSameName<'tcx> { } #[derive(Subdiagnostic)] -#[label(passes_parent_info)] +#[label( + "{$num -> + [one] {$descr} + *[other] {$descr}s + } in this {$parent_descr}" +)] pub(crate) struct ParentInfo<'tcx> { pub num: usize, pub descr: &'tcx str, @@ -1006,7 +1144,15 @@ pub(crate) struct ParentInfo<'tcx> { } #[derive(Subdiagnostic)] -#[note(passes_ignored_derived_impls)] +#[note( + "`{$name}` has {$trait_list_len -> + [one] a derived impl + *[other] derived impls + } for the {$trait_list_len -> + [one] trait {$trait_list}, but this is + *[other] traits {$trait_list}, but these are + } intentionally ignored during dead code analysis" +)] pub(crate) struct IgnoredDerivedImpls { pub name: Symbol, pub trait_list: DiagSymbolList, @@ -1016,7 +1162,13 @@ pub(crate) struct IgnoredDerivedImpls { #[derive(Subdiagnostic)] pub(crate) enum ChangeFields { #[multipart_suggestion( - passes_change_fields_to_be_of_unit_type, + "consider changing the { $num -> + [one] field + *[other] fields + } to be of unit type to suppress this warning while preserving the field numbering, or remove the { $num -> + [one] field + *[other] fields + }", applicability = "has-placeholders" )] ChangeToUnitTypeOrRemove { @@ -1024,12 +1176,20 @@ pub(crate) enum ChangeFields { #[suggestion_part(code = "()")] spans: Vec, }, - #[help(passes_remove_fields)] + #[help( + "consider removing { $num -> + [one] this + *[other] these + } { $num -> + [one] field + *[other] fields + }" + )] Remove { num: usize }, } #[derive(Diagnostic)] -#[diag(passes_proc_macro_bad_sig)] +#[diag("{$kind} has incorrect signature")] pub(crate) struct ProcMacroBadSig { #[primary_span] pub span: Span, @@ -1037,18 +1197,30 @@ pub(crate) struct ProcMacroBadSig { } #[derive(LintDiagnostic)] -#[diag(passes_unnecessary_stable_feature)] +#[diag( + "the feature `{$feature}` has been stable since {$since} and no longer requires an attribute to enable" +)] pub(crate) struct UnnecessaryStableFeature { pub feature: Symbol, pub since: Symbol, } #[derive(LintDiagnostic)] -#[diag(passes_unnecessary_partial_stable_feature)] +#[diag( + "the feature `{$feature}` has been partially stabilized since {$since} and is succeeded by the feature `{$implies}`" +)] pub(crate) struct UnnecessaryPartialStableFeature { - #[suggestion(code = "{implies}", applicability = "maybe-incorrect")] + #[suggestion( + "if you are using features which are still unstable, change to using `{$implies}`", + code = "{implies}", + applicability = "maybe-incorrect" + )] pub span: Span, - #[suggestion(passes_suggestion_remove, code = "", applicability = "maybe-incorrect")] + #[suggestion( + "if you are using features which are now stable, remove this line", + code = "", + applicability = "maybe-incorrect" + )] pub line: Span, pub feature: Symbol, pub since: Symbol, @@ -1056,38 +1228,36 @@ pub(crate) struct UnnecessaryPartialStableFeature { } #[derive(LintDiagnostic)] -#[diag(passes_ineffective_unstable_impl)] -#[note] +#[diag("an `#[unstable]` annotation here has no effect")] +#[note("see issue #55436 for more information")] pub(crate) struct IneffectiveUnstableImpl; -/// "sanitize attribute not allowed here" #[derive(Diagnostic)] -#[diag(passes_sanitize_attribute_not_allowed)] +#[diag("sanitize attribute not allowed here")] pub(crate) struct SanitizeAttributeNotAllowed { #[primary_span] pub attr_span: Span, - /// "not a function, impl block, or module" - #[label(passes_not_fn_impl_mod)] + #[label("not a function, impl block, or module")] pub not_fn_impl_mod: Option, - /// "function has no body" - #[label(passes_no_body)] + #[label("function has no body")] pub no_body: Option, - /// "sanitize attribute can be applied to a function (with body), impl block, or module" - #[help] + #[help("sanitize attribute can be applied to a function (with body), impl block, or module")] pub help: (), } // FIXME(jdonszelmann): move back to rustc_attr #[derive(Diagnostic)] -#[diag(passes_rustc_const_stable_indirect_pairing)] +#[diag( + "`const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied" +)] pub(crate) struct RustcConstStableIndirectPairing { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_unsupported_attributes_in_where)] -#[help] +#[diag("most attributes are not supported in `where` clauses")] +#[help("only `#[cfg]` and `#[cfg_attr]` are supported")] pub(crate) struct UnsupportedAttributesInWhere { #[primary_span] pub span: MultiSpan, @@ -1095,100 +1265,100 @@ pub(crate) struct UnsupportedAttributesInWhere { #[derive(Diagnostic)] pub(crate) enum UnexportableItem<'a> { - #[diag(passes_unexportable_item)] + #[diag("{$descr}'s are not exportable")] Item { #[primary_span] span: Span, descr: &'a str, }, - #[diag(passes_unexportable_generic_fn)] + #[diag("generic functions are not exportable")] GenericFn(#[primary_span] Span), - #[diag(passes_unexportable_fn_abi)] + #[diag("only functions with \"C\" ABI are exportable")] FnAbi(#[primary_span] Span), - #[diag(passes_unexportable_type_repr)] + #[diag("types with unstable layout are not exportable")] TypeRepr(#[primary_span] Span), - #[diag(passes_unexportable_type_in_interface)] + #[diag("{$desc} with `#[export_stable]` attribute uses type `{$ty}`, which is not exportable")] TypeInInterface { #[primary_span] span: Span, desc: &'a str, ty: &'a str, - #[label] + #[label("not exportable")] ty_span: Span, }, - #[diag(passes_unexportable_priv_item)] + #[diag("private items are not exportable")] PrivItem { #[primary_span] span: Span, - #[note] + #[note("is only usable at visibility `{$vis_descr}`")] vis_note: Span, vis_descr: &'a str, }, - #[diag(passes_unexportable_adt_with_private_fields)] + #[diag("ADT types with private fields are not exportable")] AdtWithPrivFields { #[primary_span] span: Span, - #[note] + #[note("`{$field_name}` is private")] vis_note: Span, field_name: &'a str, }, } #[derive(Diagnostic)] -#[diag(passes_repr_align_should_be_align)] +#[diag("`#[repr(align(...))]` is not supported on {$item}")] pub(crate) struct ReprAlignShouldBeAlign { #[primary_span] - #[help] + #[help("use `#[rustc_align(...)]` instead")] pub span: Span, pub item: &'static str, } #[derive(Diagnostic)] -#[diag(passes_repr_align_should_be_align_static)] +#[diag("`#[repr(align(...))]` is not supported on {$item}")] pub(crate) struct ReprAlignShouldBeAlignStatic { #[primary_span] - #[help] + #[help("use `#[rustc_align_static(...)]` instead")] pub span: Span, pub item: &'static str, } #[derive(Diagnostic)] -#[diag(passes_custom_mir_phase_requires_dialect)] +#[diag("`dialect` key required")] pub(crate) struct CustomMirPhaseRequiresDialect { #[primary_span] pub attr_span: Span, - #[label] + #[label("`phase` argument requires a `dialect` argument")] pub phase_span: Span, } #[derive(Diagnostic)] -#[diag(passes_custom_mir_incompatible_dialect_and_phase)] +#[diag("the {$dialect} dialect is not compatible with the {$phase} phase")] pub(crate) struct CustomMirIncompatibleDialectAndPhase { pub dialect: MirDialect, pub phase: MirPhase, #[primary_span] pub attr_span: Span, - #[label] + #[label("this dialect...")] pub dialect_span: Span, - #[label] + #[label("... is not compatible with this phase")] pub phase_span: Span, } #[derive(Diagnostic)] -#[diag(passes_eii_impl_not_function)] +#[diag("`eii_macro_for` is only valid on functions")] pub(crate) struct EiiImplNotFunction { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_eii_impl_requires_unsafe)] +#[diag("`#[{$name}]` is unsafe to implement")] pub(crate) struct EiiImplRequiresUnsafe { #[primary_span] pub span: Span, @@ -1198,10 +1368,7 @@ pub(crate) struct EiiImplRequiresUnsafe { } #[derive(Subdiagnostic)] -#[multipart_suggestion( - passes_eii_impl_requires_unsafe_suggestion, - applicability = "machine-applicable" -)] +#[multipart_suggestion("wrap the attribute in `unsafe(...)`", applicability = "machine-applicable")] pub(crate) struct EiiImplRequiresUnsafeSuggestion { #[suggestion_part(code = "unsafe(")] pub left: Span, @@ -1210,64 +1377,71 @@ pub(crate) struct EiiImplRequiresUnsafeSuggestion { } #[derive(Diagnostic)] -#[diag(passes_eii_fn_with_track_caller)] +#[diag("`#[{$name}]` is not allowed to have `#[track_caller]`")] pub(crate) struct EiiWithTrackCaller { #[primary_span] pub attr_span: Span, pub name: Symbol, - #[label] + #[label("`#[{$name}]` is not allowed to have `#[track_caller]`")] pub sig_span: Span, } #[derive(Diagnostic)] -#[diag(passes_eii_without_impl)] +#[diag("`#[{$name}]` required, but not found")] pub(crate) struct EiiWithoutImpl { #[primary_span] - #[label] + #[label("expected because `#[{$name}]` was declared here in crate `{$decl_crate_name}`")] pub span: Span, pub name: Symbol, pub current_crate_name: Symbol, pub decl_crate_name: Symbol, - #[help] + #[help( + "expected at least one implementation in crate `{$current_crate_name}` or any of its dependencies" + )] pub help: (), } #[derive(Diagnostic)] -#[diag(passes_duplicate_eii_impls)] +#[diag("multiple implementations of `#[{$name}]`")] pub(crate) struct DuplicateEiiImpls { pub name: Symbol, #[primary_span] - #[label(passes_first)] + #[label("first implemented here in crate `{$first_crate}`")] pub first_span: Span, pub first_crate: Symbol, - #[label(passes_second)] + #[label("also implemented here in crate `{$second_crate}`")] pub second_span: Span, pub second_crate: Symbol, - #[note] + #[note("in addition to these two, { $num_additional_crates -> + [one] another implementation was found in crate {$additional_crate_names} + *[other] more implementations were also found in the following crates: {$additional_crate_names} + }")] pub additional_crates: Option<()>, pub num_additional_crates: usize, pub additional_crate_names: String, - #[help] + #[help( + "an \"externally implementable item\" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict" + )] pub help: (), } #[derive(Diagnostic)] -#[diag(passes_function_not_have_default_implementation)] +#[diag("function doesn't have a default implementation")] pub(crate) struct FunctionNotHaveDefaultImplementation { #[primary_span] pub span: Span, - #[note] + #[note("required by this annotation")] pub note_span: Span, } #[derive(Diagnostic)] -#[diag(passes_must_implement_not_function)] +#[diag("not a function")] pub(crate) struct MustImplementNotFunction { #[primary_span] pub span: Span, @@ -1278,26 +1452,26 @@ pub(crate) struct MustImplementNotFunction { } #[derive(Subdiagnostic)] -#[note(passes_must_implement_not_function_span_note)] +#[note("required by this annotation")] pub(crate) struct MustImplementNotFunctionSpanNote { #[primary_span] pub span: Span, } #[derive(Subdiagnostic)] -#[note(passes_must_implement_not_function_note)] +#[note("all `#[rustc_must_implement_one_of]` arguments must be associated function names")] pub(crate) struct MustImplementNotFunctionNote {} #[derive(Diagnostic)] -#[diag(passes_function_not_found_in_trait)] +#[diag("function not found in this trait")] pub(crate) struct FunctionNotFoundInTrait { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(passes_functions_names_duplicated)] -#[note] +#[diag("functions names are duplicated")] +#[note("all `#[rustc_must_implement_one_of]` arguments must be unique")] pub(crate) struct FunctionNamesDuplicated { #[primary_span] pub spans: Vec, diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index d371589cba6c..ef6a8e80c72f 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -30,8 +30,6 @@ pub mod stability; mod upvars; mod weak_lang_items; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub fn provide(providers: &mut Providers) { check_attr::provide(providers); dead::provide(providers); diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index d9565e2dae0e..225487cc989f 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -145,7 +145,7 @@ impl<'tcx> ReachableContext<'tcx> { _ => false, }, Node::TraitItem(trait_method) => match trait_method.kind { - hir::TraitItemKind::Const(_, ref default) => default.is_some(), + hir::TraitItemKind::Const(_, ref default, _) => default.is_some(), hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => true, hir::TraitItemKind::Fn(_, hir::TraitFn::Required(_)) | hir::TraitItemKind::Type(..) => false, @@ -209,7 +209,7 @@ impl<'tcx> ReachableContext<'tcx> { self.visit_nested_body(body); } } - // For #[type_const] we want to evaluate the RHS. + // For `type const` we want to evaluate the RHS. hir::ItemKind::Const(_, _, _, init @ hir::ConstItemRhs::TypeConst(_)) => { self.visit_const_item_rhs(init); } @@ -258,11 +258,11 @@ impl<'tcx> ReachableContext<'tcx> { } Node::TraitItem(trait_method) => { match trait_method.kind { - hir::TraitItemKind::Const(_, None) + hir::TraitItemKind::Const(_, None, _) | hir::TraitItemKind::Fn(_, hir::TraitFn::Required(_)) => { // Keep going, nothing to get exported } - hir::TraitItemKind::Const(_, Some(rhs)) => self.visit_const_item_rhs(rhs), + hir::TraitItemKind::Const(_, Some(rhs), _) => self.visit_const_item_rhs(rhs), hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)) => { self.visit_nested_body(body_id); } diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index 64d4ff422043..e7448613cc14 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -56,22 +56,14 @@ pub struct OverlappingRangeEndpoints { pub overlap: Vec, } +#[derive(Subdiagnostic)] +#[label("this range overlaps on `{$range}`...")] pub struct Overlap { + #[primary_span] pub span: Span, pub range: String, // a printed pattern } -impl Subdiagnostic for Overlap { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { - let Overlap { span, range } = self; - - // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]` - // does not support `#[subdiagnostic(eager)]`... - let message = format!("this range overlaps on `{range}`..."); - diag.span_label(span, message); - } -} - #[derive(LintDiagnostic)] #[diag("exclusive range missing `{$max}`")] pub struct ExclusiveRangeMissingMax { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 4a88ea0cc445..9a952bb72195 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -876,7 +876,7 @@ pub struct TestReachabilityVisitor<'a, 'tcx> { impl<'a, 'tcx> TestReachabilityVisitor<'a, 'tcx> { fn effective_visibility_diagnostic(&self, def_id: LocalDefId) { - if self.tcx.has_attr(def_id, sym::rustc_effective_visibility) { + if find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::RustcEffectiveVisibility) { let mut error_msg = String::new(); let span = self.tcx.def_span(def_id.to_def_id()); if let Some(effective_vis) = self.effective_visibilities.effective_vis(def_id) { @@ -1157,14 +1157,14 @@ impl<'tcx> TypePrivacyVisitor<'tcx> { let typeck_results = self .maybe_typeck_results .unwrap_or_else(|| span_bug!(span, "`hir::Expr` or `hir::Pat` outside of a body")); - let result: ControlFlow<()> = try { + try { self.visit(typeck_results.node_type(id))?; self.visit(typeck_results.node_args(id))?; if let Some(adjustments) = typeck_results.adjustments().get(id) { adjustments.iter().try_for_each(|adjustment| self.visit(adjustment.target))?; } - }; - result.is_break() + } + .is_break() } fn check_def_id(&self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index d611629671a0..a8edc1129481 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start measureme = "12.0.1" +rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hashes = { path = "../rustc_hashes" } @@ -15,6 +16,8 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } +rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_thread_pool = { path = "../rustc_thread_pool" } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_query_impl/src/error.rs b/compiler/rustc_query_impl/src/error.rs index b109172d8e49..6d3eb2950982 100644 --- a/compiler/rustc_query_impl/src/error.rs +++ b/compiler/rustc_query_impl/src/error.rs @@ -1,3 +1,4 @@ +use rustc_errors::codes::*; use rustc_hir::limit::Limit; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -22,3 +23,59 @@ pub(crate) struct QueryOverflowNote { pub desc: String, pub depth: usize, } + +#[derive(Subdiagnostic)] +#[note("...which requires {$desc}...")] +pub(crate) struct CycleStack { + #[primary_span] + pub span: Span, + pub desc: String, +} + +#[derive(Subdiagnostic)] +pub(crate) enum StackCount { + #[note("...which immediately requires {$stack_bottom} again")] + Single, + #[note("...which again requires {$stack_bottom}, completing the cycle")] + Multiple, +} + +#[derive(Subdiagnostic)] +pub(crate) enum Alias { + #[note("type aliases cannot be recursive")] + #[help("consider using a struct, enum, or union instead to break the cycle")] + #[help( + "see for more information" + )] + Ty, + #[note("trait aliases cannot be recursive")] + Trait, +} + +#[derive(Subdiagnostic)] +#[note("cycle used when {$usage}")] +pub(crate) struct CycleUsage { + #[primary_span] + pub span: Span, + pub usage: String, +} + +#[derive(Diagnostic)] +#[diag("cycle detected when {$stack_bottom}", code = E0391)] +pub(crate) struct Cycle { + #[primary_span] + pub span: Span, + pub stack_bottom: String, + #[subdiagnostic] + pub cycle_stack: Vec, + #[subdiagnostic] + pub stack_count: StackCount, + #[subdiagnostic] + pub alias: Option, + #[subdiagnostic] + pub cycle_usage: Option, + #[note( + "see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information" + )] + pub note_span: (), +} diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs new file mode 100644 index 000000000000..50fb4f29ed71 --- /dev/null +++ b/compiler/rustc_query_impl/src/execution.rs @@ -0,0 +1,690 @@ +use std::hash::Hash; +use std::mem; + +use rustc_data_structures::hash_table::{Entry, HashTable}; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_data_structures::{outline, sharded, sync}; +use rustc_errors::{Diag, FatalError, StashKey}; +use rustc_middle::dep_graph::DepsType; +use rustc_middle::ty::TyCtxt; +use rustc_query_system::dep_graph::{DepGraphData, DepNodeKey, HasDepContext}; +use rustc_query_system::query::{ + ActiveKeyStatus, CycleError, CycleErrorHandling, QueryCache, QueryJob, QueryJobId, QueryLatch, + QueryMode, QueryStackDeferred, QueryStackFrame, QueryState, incremental_verify_ich, +}; +use rustc_span::{DUMMY_SP, Span}; + +use crate::dep_graph::{DepContext, DepNode, DepNodeIndex}; +use crate::job::{QueryJobInfo, QueryJobMap, find_cycle_in_stack, report_cycle}; +use crate::{QueryCtxt, QueryFlags, SemiDynamicQueryDispatcher}; + +#[inline] +fn equivalent_key(k: &K) -> impl Fn(&(K, V)) -> bool + '_ { + move |x| x.0 == *k +} + +/// Obtains the enclosed [`QueryJob`], or panics if this query evaluation +/// was poisoned by a panic. +fn expect_job<'tcx>(status: ActiveKeyStatus<'tcx>) -> QueryJob<'tcx> { + match status { + ActiveKeyStatus::Started(job) => job, + ActiveKeyStatus::Poisoned => { + panic!("job for query failed to start and was poisoned") + } + } +} + +pub(crate) fn all_inactive<'tcx, K>(state: &QueryState<'tcx, K>) -> bool { + state.active.lock_shards().all(|shard| shard.is_empty()) +} + +/// Internal plumbing for collecting the set of active jobs for this query. +/// +/// Should only be called from `gather_active_jobs`. +pub(crate) fn gather_active_jobs_inner<'tcx, K: Copy>( + state: &QueryState<'tcx, K>, + tcx: TyCtxt<'tcx>, + make_frame: fn(TyCtxt<'tcx>, K) -> QueryStackFrame>, + require_complete: bool, + job_map_out: &mut QueryJobMap<'tcx>, // Out-param; job info is gathered into this map +) -> Option<()> { + let mut active = Vec::new(); + + // Helper to gather active jobs from a single shard. + let mut gather_shard_jobs = |shard: &HashTable<(K, ActiveKeyStatus<'tcx>)>| { + for (k, v) in shard.iter() { + if let ActiveKeyStatus::Started(ref job) = *v { + active.push((*k, job.clone())); + } + } + }; + + // Lock shards and gather jobs from each shard. + if require_complete { + for shard in state.active.lock_shards() { + gather_shard_jobs(&shard); + } + } else { + // We use try_lock_shards here since we are called from the + // deadlock handler, and this shouldn't be locked. + for shard in state.active.try_lock_shards() { + let shard = shard?; + gather_shard_jobs(&shard); + } + } + + // Call `make_frame` while we're not holding a `state.active` lock as `make_frame` may call + // queries leading to a deadlock. + for (key, job) in active { + let frame = make_frame(tcx, key); + job_map_out.insert(job.id, QueryJobInfo { frame, job }); + } + + Some(()) +} + +/// Guard object representing the responsibility to execute a query job and +/// mark it as completed. +/// +/// This will poison the relevant query key if it is dropped without calling +/// [`Self::complete`]. +struct ActiveJobGuard<'tcx, K> +where + K: Eq + Hash + Copy, +{ + state: &'tcx QueryState<'tcx, K>, + key: K, + key_hash: u64, +} + +#[cold] +#[inline(never)] +fn mk_cycle<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + qcx: QueryCtxt<'tcx>, + cycle_error: CycleError, +) -> C::Value { + let error = report_cycle(qcx.tcx.sess, &cycle_error); + handle_cycle_error(query, qcx, &cycle_error, error) +} + +fn handle_cycle_error<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + qcx: QueryCtxt<'tcx>, + cycle_error: &CycleError, + error: Diag<'_>, +) -> C::Value { + match query.cycle_error_handling() { + CycleErrorHandling::Error => { + let guar = error.emit(); + query.value_from_cycle_error(qcx.tcx, cycle_error, guar) + } + CycleErrorHandling::Fatal => { + error.emit(); + qcx.tcx.dcx().abort_if_errors(); + unreachable!() + } + CycleErrorHandling::DelayBug => { + let guar = error.delay_as_bug(); + query.value_from_cycle_error(qcx.tcx, cycle_error, guar) + } + CycleErrorHandling::Stash => { + let guar = if let Some(root) = cycle_error.cycle.first() + && let Some(span) = root.frame.info.span + { + error.stash(span, StashKey::Cycle).unwrap() + } else { + error.emit() + }; + query.value_from_cycle_error(qcx.tcx, cycle_error, guar) + } + } +} + +impl<'tcx, K> ActiveJobGuard<'tcx, K> +where + K: Eq + Hash + Copy, +{ + /// Completes the query by updating the query cache with the `result`, + /// signals the waiter, and forgets the guard so it won't poison the query. + fn complete(self, cache: &C, result: C::Value, dep_node_index: DepNodeIndex) + where + C: QueryCache, + { + // Forget ourself so our destructor won't poison the query. + // (Extract fields by value first to make sure we don't leak anything.) + let Self { state, key, key_hash }: Self = self; + mem::forget(self); + + // Mark as complete before we remove the job from the active state + // so no other thread can re-execute this query. + cache.complete(key, result, dep_node_index); + + let job = { + // don't keep the lock during the `unwrap()` of the retrieved value, or we taint the + // underlying shard. + // since unwinding also wants to look at this map, this can also prevent a double + // panic. + let mut shard = state.active.lock_shard_by_hash(key_hash); + match shard.find_entry(key_hash, equivalent_key(&key)) { + Err(_) => None, + Ok(occupied) => Some(occupied.remove().0.1), + } + }; + let job = expect_job(job.expect("active query job entry")); + + job.signal_complete(); + } +} + +impl<'tcx, K> Drop for ActiveJobGuard<'tcx, K> +where + K: Eq + Hash + Copy, +{ + #[inline(never)] + #[cold] + fn drop(&mut self) { + // Poison the query so jobs waiting on it panic. + let Self { state, key, key_hash } = *self; + let job = { + let mut shard = state.active.lock_shard_by_hash(key_hash); + match shard.find_entry(key_hash, equivalent_key(&key)) { + Err(_) => panic!(), + Ok(occupied) => { + let ((key, value), vacant) = occupied.remove(); + vacant.insert((key, ActiveKeyStatus::Poisoned)); + expect_job(value) + } + } + }; + // Also signal the completion of the job, so waiters + // will continue execution. + job.signal_complete(); + } +} + +#[cold] +#[inline(never)] +fn cycle_error<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + qcx: QueryCtxt<'tcx>, + try_execute: QueryJobId, + span: Span, +) -> (C::Value, Option) { + // Ensure there was no errors collecting all active jobs. + // We need the complete map to ensure we find a cycle to break. + let job_map = qcx + .collect_active_jobs_from_all_queries(false) + .ok() + .expect("failed to collect active queries"); + + let error = find_cycle_in_stack(try_execute, job_map, &qcx.current_query_job(), span); + (mk_cycle(query, qcx, error.lift()), None) +} + +#[inline(always)] +fn wait_for_query<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + qcx: QueryCtxt<'tcx>, + span: Span, + key: C::Key, + latch: QueryLatch<'tcx>, + current: Option, +) -> (C::Value, Option) { + // For parallel queries, we'll block and wait until the query running + // in another thread has completed. Record how long we wait in the + // self-profiler. + let query_blocked_prof_timer = qcx.tcx.prof.query_blocked(); + + // With parallel queries we might just have to wait on some other + // thread. + let result = latch.wait_on(qcx, current, span); + + match result { + Ok(()) => { + let Some((v, index)) = query.query_cache(qcx).lookup(&key) else { + outline(|| { + // We didn't find the query result in the query cache. Check if it was + // poisoned due to a panic instead. + let key_hash = sharded::make_hash(&key); + let shard = query.query_state(qcx).active.lock_shard_by_hash(key_hash); + match shard.find(key_hash, equivalent_key(&key)) { + // The query we waited on panicked. Continue unwinding here. + Some((_, ActiveKeyStatus::Poisoned)) => FatalError.raise(), + _ => panic!( + "query '{}' result must be in the cache or the query must be poisoned after a wait", + query.name() + ), + } + }) + }; + + qcx.tcx.prof.query_cache_hit(index.into()); + query_blocked_prof_timer.finish_with_query_invocation_id(index.into()); + + (v, Some(index)) + } + Err(cycle) => (mk_cycle(query, qcx, cycle.lift()), None), + } +} + +#[inline(never)] +fn try_execute_query<'tcx, C: QueryCache, const FLAGS: QueryFlags, const INCR: bool>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + qcx: QueryCtxt<'tcx>, + span: Span, + key: C::Key, + dep_node: Option, +) -> (C::Value, Option) { + let state = query.query_state(qcx); + let key_hash = sharded::make_hash(&key); + let mut state_lock = state.active.lock_shard_by_hash(key_hash); + + // For the parallel compiler we need to check both the query cache and query state structures + // while holding the state lock to ensure that 1) the query has not yet completed and 2) the + // query is not still executing. Without checking the query cache here, we can end up + // re-executing the query since `try_start` only checks that the query is not currently + // executing, but another thread may have already completed the query and stores it result + // in the query cache. + if qcx.tcx.sess.threads() > 1 { + if let Some((value, index)) = query.query_cache(qcx).lookup(&key) { + qcx.tcx.prof.query_cache_hit(index.into()); + return (value, Some(index)); + } + } + + let current_job_id = qcx.current_query_job(); + + match state_lock.entry(key_hash, equivalent_key(&key), |(k, _)| sharded::make_hash(k)) { + Entry::Vacant(entry) => { + // Nothing has computed or is computing the query, so we start a new job and insert it in the + // state map. + let id = qcx.next_job_id(); + let job = QueryJob::new(id, span, current_job_id); + entry.insert((key, ActiveKeyStatus::Started(job))); + + // Drop the lock before we start executing the query + drop(state_lock); + + execute_job::(query, qcx, state, key, key_hash, id, dep_node) + } + Entry::Occupied(mut entry) => { + match &mut entry.get_mut().1 { + ActiveKeyStatus::Started(job) => { + if sync::is_dyn_thread_safe() { + // Get the latch out + let latch = job.latch(); + drop(state_lock); + + // Only call `wait_for_query` if we're using a Rayon thread pool + // as it will attempt to mark the worker thread as blocked. + return wait_for_query(query, qcx, span, key, latch, current_job_id); + } + + let id = job.id; + drop(state_lock); + + // If we are single-threaded we know that we have cycle error, + // so we just return the error. + cycle_error(query, qcx, id, span) + } + ActiveKeyStatus::Poisoned => FatalError.raise(), + } + } + } +} + +#[inline(always)] +fn execute_job<'tcx, C: QueryCache, const FLAGS: QueryFlags, const INCR: bool>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + qcx: QueryCtxt<'tcx>, + state: &'tcx QueryState<'tcx, C::Key>, + key: C::Key, + key_hash: u64, + id: QueryJobId, + dep_node: Option, +) -> (C::Value, Option) { + // Set up a guard object that will automatically poison the query if a + // panic occurs while executing the query (or any intermediate plumbing). + let job_guard = ActiveJobGuard { state, key, key_hash }; + + debug_assert_eq!(qcx.tcx.dep_graph.is_fully_enabled(), INCR); + + // Delegate to another function to actually execute the query job. + let (result, dep_node_index) = if INCR { + execute_job_incr(query, qcx, qcx.tcx.dep_graph.data().unwrap(), key, dep_node, id) + } else { + execute_job_non_incr(query, qcx, key, id) + }; + + let cache = query.query_cache(qcx); + if query.feedable() { + // We should not compute queries that also got a value via feeding. + // This can't happen, as query feeding adds the very dependencies to the fed query + // as its feeding query had. So if the fed query is red, so is its feeder, which will + // get evaluated first, and re-feed the query. + if let Some((cached_result, _)) = cache.lookup(&key) { + let Some(hasher) = query.hash_result() else { + panic!( + "no_hash fed query later has its value computed.\n\ + Remove `no_hash` modifier to allow recomputation.\n\ + The already cached value: {}", + (query.format_value())(&cached_result) + ); + }; + + let (old_hash, new_hash) = qcx.dep_context().with_stable_hashing_context(|mut hcx| { + (hasher(&mut hcx, &cached_result), hasher(&mut hcx, &result)) + }); + let formatter = query.format_value(); + if old_hash != new_hash { + // We have an inconsistency. This can happen if one of the two + // results is tainted by errors. + assert!( + qcx.tcx.dcx().has_errors().is_some(), + "Computed query value for {:?}({:?}) is inconsistent with fed value,\n\ + computed={:#?}\nfed={:#?}", + query.dep_kind(), + key, + formatter(&result), + formatter(&cached_result), + ); + } + } + } + + // Tell the guard to perform completion bookkeeping, and also to not poison the query. + job_guard.complete(cache, result, dep_node_index); + + (result, Some(dep_node_index)) +} + +// Fast path for when incr. comp. is off. +#[inline(always)] +fn execute_job_non_incr<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + qcx: QueryCtxt<'tcx>, + key: C::Key, + job_id: QueryJobId, +) -> (C::Value, DepNodeIndex) { + debug_assert!(!qcx.tcx.dep_graph.is_fully_enabled()); + + // Fingerprint the key, just to assert that it doesn't + // have anything we don't consider hashable + if cfg!(debug_assertions) { + let _ = key.to_fingerprint(qcx.tcx); + } + + let prof_timer = qcx.tcx.prof.query_provider(); + // Call the query provider. + let result = qcx.start_query(job_id, query.depth_limit(), || query.invoke_provider(qcx, key)); + let dep_node_index = qcx.tcx.dep_graph.next_virtual_depnode_index(); + prof_timer.finish_with_query_invocation_id(dep_node_index.into()); + + // Similarly, fingerprint the result to assert that + // it doesn't have anything not considered hashable. + if cfg!(debug_assertions) + && let Some(hash_result) = query.hash_result() + { + qcx.dep_context().with_stable_hashing_context(|mut hcx| { + hash_result(&mut hcx, &result); + }); + } + + (result, dep_node_index) +} + +#[inline(always)] +fn execute_job_incr<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + qcx: QueryCtxt<'tcx>, + dep_graph_data: &DepGraphData, + key: C::Key, + mut dep_node_opt: Option, + job_id: QueryJobId, +) -> (C::Value, DepNodeIndex) { + if !query.anon() && !query.eval_always() { + // `to_dep_node` is expensive for some `DepKind`s. + let dep_node = dep_node_opt.get_or_insert_with(|| query.construct_dep_node(qcx.tcx, &key)); + + // The diagnostics for this query will be promoted to the current session during + // `try_mark_green()`, so we can ignore them here. + if let Some(ret) = qcx.start_query(job_id, false, || { + try_load_from_disk_and_cache_in_memory(query, dep_graph_data, qcx, &key, dep_node) + }) { + return ret; + } + } + + let prof_timer = qcx.tcx.prof.query_provider(); + + let (result, dep_node_index) = qcx.start_query(job_id, query.depth_limit(), || { + if query.anon() { + // Call the query provider inside an anon task. + return dep_graph_data.with_anon_task_inner(qcx.tcx, query.dep_kind(), || { + query.invoke_provider(qcx, key) + }); + } + + // `to_dep_node` is expensive for some `DepKind`s. + let dep_node = dep_node_opt.unwrap_or_else(|| query.construct_dep_node(qcx.tcx, &key)); + + // Call the query provider. + dep_graph_data.with_task( + dep_node, + (qcx, query), + key, + |(qcx, query), key| query.invoke_provider(qcx, key), + query.hash_result(), + ) + }); + + prof_timer.finish_with_query_invocation_id(dep_node_index.into()); + + (result, dep_node_index) +} + +#[inline(always)] +fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + dep_graph_data: &DepGraphData, + qcx: QueryCtxt<'tcx>, + key: &C::Key, + dep_node: &DepNode, +) -> Option<(C::Value, DepNodeIndex)> { + // Note this function can be called concurrently from the same query + // We must ensure that this is handled correctly. + + let (prev_dep_node_index, dep_node_index) = dep_graph_data.try_mark_green(qcx, dep_node)?; + + debug_assert!(dep_graph_data.is_index_green(prev_dep_node_index)); + + // First we try to load the result from the on-disk cache. + // Some things are never cached on disk. + if let Some(result) = query.try_load_from_disk(qcx, key, prev_dep_node_index, dep_node_index) { + if std::intrinsics::unlikely(qcx.tcx.sess.opts.unstable_opts.query_dep_graph) { + dep_graph_data.mark_debug_loaded_from_disk(*dep_node) + } + + let prev_fingerprint = dep_graph_data.prev_fingerprint_of(prev_dep_node_index); + // If `-Zincremental-verify-ich` is specified, re-hash results from + // the cache and make sure that they have the expected fingerprint. + // + // If not, we still seek to verify a subset of fingerprints loaded + // from disk. Re-hashing results is fairly expensive, so we can't + // currently afford to verify every hash. This subset should still + // give us some coverage of potential bugs though. + let try_verify = prev_fingerprint.split().1.as_u64().is_multiple_of(32); + if std::intrinsics::unlikely( + try_verify || qcx.tcx.sess.opts.unstable_opts.incremental_verify_ich, + ) { + incremental_verify_ich( + qcx.tcx, + dep_graph_data, + &result, + prev_dep_node_index, + query.hash_result(), + query.format_value(), + ); + } + + return Some((result, dep_node_index)); + } + + // We always expect to find a cached result for things that + // can be forced from `DepNode`. + debug_assert!( + !query.will_cache_on_disk_for_key(qcx.tcx, key) + || !qcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(), + "missing on-disk cache entry for {dep_node:?}" + ); + + // Sanity check for the logic in `ensure`: if the node is green and the result loadable, + // we should actually be able to load it. + debug_assert!( + !query.is_loadable_from_disk(qcx, key, prev_dep_node_index), + "missing on-disk cache entry for loadable {dep_node:?}" + ); + + // We could not load a result from the on-disk cache, so + // recompute. + let prof_timer = qcx.tcx.prof.query_provider(); + + // The dep-graph for this computation is already in-place. + // Call the query provider. + let result = qcx.tcx.dep_graph.with_ignore(|| query.invoke_provider(qcx, *key)); + + prof_timer.finish_with_query_invocation_id(dep_node_index.into()); + + // Verify that re-running the query produced a result with the expected hash + // This catches bugs in query implementations, turning them into ICEs. + // For example, a query might sort its result by `DefId` - since `DefId`s are + // not stable across compilation sessions, the result could get up getting sorted + // in a different order when the query is re-run, even though all of the inputs + // (e.g. `DefPathHash` values) were green. + // + // See issue #82920 for an example of a miscompilation that would get turned into + // an ICE by this check + incremental_verify_ich( + qcx.tcx, + dep_graph_data, + &result, + prev_dep_node_index, + query.hash_result(), + query.format_value(), + ); + + Some((result, dep_node_index)) +} + +/// Ensure that either this query has all green inputs or been executed. +/// Executing `query::ensure(D)` is considered a read of the dep-node `D`. +/// Returns true if the query should still run. +/// +/// This function is particularly useful when executing passes for their +/// side-effects -- e.g., in order to report errors for erroneous programs. +/// +/// Note: The optimization is only available during incr. comp. +#[inline(never)] +fn ensure_must_run<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + qcx: QueryCtxt<'tcx>, + key: &C::Key, + check_cache: bool, +) -> (bool, Option) { + if query.eval_always() { + return (true, None); + } + + // Ensuring an anonymous query makes no sense + assert!(!query.anon()); + + let dep_node = query.construct_dep_node(qcx.tcx, key); + + let dep_graph = &qcx.tcx.dep_graph; + let serialized_dep_node_index = match dep_graph.try_mark_green(qcx, &dep_node) { + None => { + // A None return from `try_mark_green` means that this is either + // a new dep node or that the dep node has already been marked red. + // Either way, we can't call `dep_graph.read()` as we don't have the + // DepNodeIndex. We must invoke the query itself. The performance cost + // this introduces should be negligible as we'll immediately hit the + // in-memory cache, or another query down the line will. + return (true, Some(dep_node)); + } + Some((serialized_dep_node_index, dep_node_index)) => { + dep_graph.read_index(dep_node_index); + qcx.tcx.prof.query_cache_hit(dep_node_index.into()); + serialized_dep_node_index + } + }; + + // We do not need the value at all, so do not check the cache. + if !check_cache { + return (false, None); + } + + let loadable = query.is_loadable_from_disk(qcx, key, serialized_dep_node_index); + (!loadable, Some(dep_node)) +} + +#[inline(always)] +pub(super) fn get_query_non_incr<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + qcx: QueryCtxt<'tcx>, + span: Span, + key: C::Key, +) -> C::Value { + debug_assert!(!qcx.tcx.dep_graph.is_fully_enabled()); + + ensure_sufficient_stack(|| try_execute_query::(query, qcx, span, key, None).0) +} + +#[inline(always)] +pub(super) fn get_query_incr<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + qcx: QueryCtxt<'tcx>, + span: Span, + key: C::Key, + mode: QueryMode, +) -> Option { + debug_assert!(qcx.tcx.dep_graph.is_fully_enabled()); + + let dep_node = if let QueryMode::Ensure { check_cache } = mode { + let (must_run, dep_node) = ensure_must_run(query, qcx, &key, check_cache); + if !must_run { + return None; + } + dep_node + } else { + None + }; + + let (result, dep_node_index) = ensure_sufficient_stack(|| { + try_execute_query::(query, qcx, span, key, dep_node) + }); + if let Some(dep_node_index) = dep_node_index { + qcx.tcx.dep_graph.read_index(dep_node_index) + } + Some(result) +} + +pub(crate) fn force_query<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + qcx: QueryCtxt<'tcx>, + key: C::Key, + dep_node: DepNode, +) { + // We may be concurrently trying both execute and force a query. + // Ensure that only one of them runs the query. + if let Some((_, index)) = query.query_cache(qcx).lookup(&key) { + qcx.tcx.prof.query_cache_hit(index.into()); + return; + } + + debug_assert!(!query.anon()); + + ensure_sufficient_stack(|| { + try_execute_query::(query, qcx, DUMMY_SP, key, Some(dep_node)) + }); +} diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs new file mode 100644 index 000000000000..7fa5882bc8e9 --- /dev/null +++ b/compiler/rustc_query_impl/src/job.rs @@ -0,0 +1,494 @@ +use std::io::Write; +use std::iter; +use std::ops::ControlFlow; +use std::sync::Arc; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{Diag, DiagCtxtHandle}; +use rustc_hir::def::DefKind; +use rustc_query_system::query::{ + CycleError, QueryInfo, QueryJob, QueryJobId, QueryLatch, QueryStackDeferred, QueryStackFrame, + QueryWaiter, +}; +use rustc_session::Session; +use rustc_span::{DUMMY_SP, Span}; + +use crate::QueryCtxt; +use crate::dep_graph::DepContext; + +/// Map from query job IDs to job information collected by +/// `collect_active_jobs_from_all_queries`. +#[derive(Debug, Default)] +pub struct QueryJobMap<'tcx> { + map: FxHashMap>, +} + +impl<'tcx> QueryJobMap<'tcx> { + /// Adds information about a job ID to the job map. + /// + /// Should only be called by `gather_active_jobs_inner`. + pub(crate) fn insert(&mut self, id: QueryJobId, info: QueryJobInfo<'tcx>) { + self.map.insert(id, info); + } + + fn frame_of(&self, id: QueryJobId) -> &QueryStackFrame> { + &self.map[&id].frame + } + + fn span_of(&self, id: QueryJobId) -> Span { + self.map[&id].job.span + } + + fn parent_of(&self, id: QueryJobId) -> Option { + self.map[&id].job.parent + } + + fn latch_of(&self, id: QueryJobId) -> Option<&QueryLatch<'tcx>> { + self.map[&id].job.latch.as_ref() + } +} + +#[derive(Clone, Debug)] +pub(crate) struct QueryJobInfo<'tcx> { + pub(crate) frame: QueryStackFrame>, + pub(crate) job: QueryJob<'tcx>, +} + +pub(crate) fn find_cycle_in_stack<'tcx>( + id: QueryJobId, + job_map: QueryJobMap<'tcx>, + current_job: &Option, + span: Span, +) -> CycleError> { + // Find the waitee amongst `current_job` parents + let mut cycle = Vec::new(); + let mut current_job = Option::clone(current_job); + + while let Some(job) = current_job { + let info = &job_map.map[&job]; + cycle.push(QueryInfo { span: info.job.span, frame: info.frame.clone() }); + + if job == id { + cycle.reverse(); + + // This is the end of the cycle + // The span entry we included was for the usage + // of the cycle itself, and not part of the cycle + // Replace it with the span which caused the cycle to form + cycle[0].span = span; + // Find out why the cycle itself was used + let usage = try { + let parent = info.job.parent?; + (info.job.span, job_map.frame_of(parent).clone()) + }; + return CycleError { usage, cycle }; + } + + current_job = info.job.parent; + } + + panic!("did not find a cycle") +} + +#[cold] +#[inline(never)] +pub(crate) fn find_dep_kind_root<'tcx>( + id: QueryJobId, + job_map: QueryJobMap<'tcx>, +) -> (QueryJobInfo<'tcx>, usize) { + let mut depth = 1; + let info = &job_map.map[&id]; + let dep_kind = info.frame.dep_kind; + let mut current_id = info.job.parent; + let mut last_layout = (info.clone(), depth); + + while let Some(id) = current_id { + let info = &job_map.map[&id]; + if info.frame.dep_kind == dep_kind { + depth += 1; + last_layout = (info.clone(), depth); + } + current_id = info.job.parent; + } + last_layout +} + +/// A resumable waiter of a query. The usize is the index into waiters in the query's latch +type Waiter = (QueryJobId, usize); + +/// Visits all the non-resumable and resumable waiters of a query. +/// Only waiters in a query are visited. +/// `visit` is called for every waiter and is passed a query waiting on `query` +/// and a span indicating the reason the query waited on `query`. +/// If `visit` returns `Break`, this function also returns `Break`, +/// and if all `visit` calls returns `Continue` it also returns `Continue`. +/// For visits of non-resumable waiters it returns the return value of `visit`. +/// For visits of resumable waiters it returns information required to resume that waiter. +fn visit_waiters<'tcx>( + job_map: &QueryJobMap<'tcx>, + query: QueryJobId, + mut visit: impl FnMut(Span, QueryJobId) -> ControlFlow>, +) -> ControlFlow> { + // Visit the parent query which is a non-resumable waiter since it's on the same stack + if let Some(parent) = job_map.parent_of(query) { + visit(job_map.span_of(query), parent)?; + } + + // Visit the explicit waiters which use condvars and are resumable + if let Some(latch) = job_map.latch_of(query) { + for (i, waiter) in latch.info.lock().waiters.iter().enumerate() { + if let Some(waiter_query) = waiter.query { + // Return a value which indicates that this waiter can be resumed + visit(waiter.span, waiter_query).map_break(|_| Some((query, i)))?; + } + } + } + + ControlFlow::Continue(()) +} + +/// Look for query cycles by doing a depth first search starting at `query`. +/// `span` is the reason for the `query` to execute. This is initially DUMMY_SP. +/// If a cycle is detected, this initial value is replaced with the span causing +/// the cycle. +fn cycle_check<'tcx>( + job_map: &QueryJobMap<'tcx>, + query: QueryJobId, + span: Span, + stack: &mut Vec<(Span, QueryJobId)>, + visited: &mut FxHashSet, +) -> ControlFlow> { + if !visited.insert(query) { + return if let Some(p) = stack.iter().position(|q| q.1 == query) { + // We detected a query cycle, fix up the initial span and return Some + + // Remove previous stack entries + stack.drain(0..p); + // Replace the span for the first query with the cycle cause + stack[0].0 = span; + ControlFlow::Break(None) + } else { + ControlFlow::Continue(()) + }; + } + + // Query marked as visited is added it to the stack + stack.push((span, query)); + + // Visit all the waiters + let r = visit_waiters(job_map, query, |span, successor| { + cycle_check(job_map, successor, span, stack, visited) + }); + + // Remove the entry in our stack if we didn't find a cycle + if r.is_continue() { + stack.pop(); + } + + r +} + +/// Finds out if there's a path to the compiler root (aka. code which isn't in a query) +/// from `query` without going through any of the queries in `visited`. +/// This is achieved with a depth first search. +fn connected_to_root<'tcx>( + job_map: &QueryJobMap<'tcx>, + query: QueryJobId, + visited: &mut FxHashSet, +) -> ControlFlow> { + // We already visited this or we're deliberately ignoring it + if !visited.insert(query) { + return ControlFlow::Continue(()); + } + + // This query is connected to the root (it has no query parent), return true + if job_map.parent_of(query).is_none() { + return ControlFlow::Break(None); + } + + visit_waiters(job_map, query, |_, successor| connected_to_root(job_map, successor, visited)) +} + +// Deterministically pick an query from a list +fn pick_query<'a, 'tcx, T, F>(job_map: &QueryJobMap<'tcx>, queries: &'a [T], f: F) -> &'a T +where + F: Fn(&T) -> (Span, QueryJobId), +{ + // Deterministically pick an entry point + // FIXME: Sort this instead + queries + .iter() + .min_by_key(|v| { + let (span, query) = f(v); + let hash = job_map.frame_of(query).hash; + // Prefer entry points which have valid spans for nicer error messages + // We add an integer to the tuple ensuring that entry points + // with valid spans are picked first + let span_cmp = if span == DUMMY_SP { 1 } else { 0 }; + (span_cmp, hash) + }) + .unwrap() +} + +/// Looks for query cycles starting from the last query in `jobs`. +/// If a cycle is found, all queries in the cycle is removed from `jobs` and +/// the function return true. +/// If a cycle was not found, the starting query is removed from `jobs` and +/// the function returns false. +fn remove_cycle<'tcx>( + job_map: &QueryJobMap<'tcx>, + jobs: &mut Vec, + wakelist: &mut Vec>>, +) -> bool { + let mut visited = FxHashSet::default(); + let mut stack = Vec::new(); + // Look for a cycle starting with the last query in `jobs` + if let ControlFlow::Break(waiter) = + cycle_check(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) + { + // The stack is a vector of pairs of spans and queries; reverse it so that + // the earlier entries require later entries + let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip(); + + // Shift the spans so that queries are matched with the span for their waitee + spans.rotate_right(1); + + // Zip them back together + let mut stack: Vec<_> = iter::zip(spans, queries).collect(); + + // Remove the queries in our cycle from the list of jobs to look at + for r in &stack { + if let Some(pos) = jobs.iter().position(|j| j == &r.1) { + jobs.remove(pos); + } + } + + // Find the queries in the cycle which are + // connected to queries outside the cycle + let entry_points = stack + .iter() + .filter_map(|&(span, query)| { + if job_map.parent_of(query).is_none() { + // This query is connected to the root (it has no query parent) + Some((span, query, None)) + } else { + let mut waiters = Vec::new(); + // Find all the direct waiters who lead to the root + let _ = visit_waiters(job_map, query, |span, waiter| { + // Mark all the other queries in the cycle as already visited + let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); + + if connected_to_root(job_map, waiter, &mut visited).is_break() { + waiters.push((span, waiter)); + } + + ControlFlow::Continue(()) + }); + if waiters.is_empty() { + None + } else { + // Deterministically pick one of the waiters to show to the user + let waiter = *pick_query(job_map, &waiters, |s| *s); + Some((span, query, Some(waiter))) + } + } + }) + .collect::)>>(); + + // Deterministically pick an entry point + let (_, entry_point, usage) = pick_query(job_map, &entry_points, |e| (e.0, e.1)); + + // Shift the stack so that our entry point is first + let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point); + if let Some(pos) = entry_point_pos { + stack.rotate_left(pos); + } + + let usage = usage.map(|(span, job)| (span, job_map.frame_of(job).clone())); + + // Create the cycle error + let error = CycleError { + usage, + cycle: stack + .iter() + .map(|&(span, job)| QueryInfo { span, frame: job_map.frame_of(job).clone() }) + .collect(), + }; + + // We unwrap `waiter` here since there must always be one + // edge which is resumable / waited using a query latch + let (waitee_query, waiter_idx) = waiter.unwrap(); + + // Extract the waiter we want to resume + let waiter = job_map.latch_of(waitee_query).unwrap().extract_waiter(waiter_idx); + + // Set the cycle error so it will be picked up when resumed + *waiter.cycle.lock() = Some(error); + + // Put the waiter on the list of things to resume + wakelist.push(waiter); + + true + } else { + false + } +} + +/// Detects query cycles by using depth first search over all active query jobs. +/// If a query cycle is found it will break the cycle by finding an edge which +/// uses a query latch and then resuming that waiter. +/// There may be multiple cycles involved in a deadlock, so this searches +/// all active queries for cycles before finally resuming all the waiters at once. +pub fn break_query_cycles<'tcx>( + job_map: QueryJobMap<'tcx>, + registry: &rustc_thread_pool::Registry, +) { + let mut wakelist = Vec::new(); + // It is OK per the comments: + // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932 + // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798866392 + #[allow(rustc::potential_query_instability)] + let mut jobs: Vec = job_map.map.keys().copied().collect(); + + let mut found_cycle = false; + + while jobs.len() > 0 { + if remove_cycle(&job_map, &mut jobs, &mut wakelist) { + found_cycle = true; + } + } + + // Check that a cycle was found. It is possible for a deadlock to occur without + // a query cycle if a query which can be waited on uses Rayon to do multithreading + // internally. Such a query (X) may be executing on 2 threads (A and B) and A may + // wait using Rayon on B. Rayon may then switch to executing another query (Y) + // which in turn will wait on X causing a deadlock. We have a false dependency from + // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here + // only considers the true dependency and won't detect a cycle. + if !found_cycle { + panic!( + "deadlock detected as we're unable to find a query cycle to break\n\ + current query map:\n{job_map:#?}", + ); + } + + // Mark all the thread we're about to wake up as unblocked. This needs to be done before + // we wake the threads up as otherwise Rayon could detect a deadlock if a thread we + // resumed fell asleep and this thread had yet to mark the remaining threads as unblocked. + for _ in 0..wakelist.len() { + rustc_thread_pool::mark_unblocked(registry); + } + + for waiter in wakelist.into_iter() { + waiter.condvar.notify_one(); + } +} + +pub fn print_query_stack<'tcx>( + qcx: QueryCtxt<'tcx>, + mut current_query: Option, + dcx: DiagCtxtHandle<'_>, + limit_frames: Option, + mut file: Option, +) -> usize { + // Be careful relying on global state here: this code is called from + // a panic hook, which means that the global `DiagCtxt` may be in a weird + // state if it was responsible for triggering the panic. + let mut count_printed = 0; + let mut count_total = 0; + + // Make use of a partial query job map if we fail to take locks collecting active queries. + let job_map: QueryJobMap<'_> = qcx + .collect_active_jobs_from_all_queries(false) + .unwrap_or_else(|partial_job_map| partial_job_map); + + if let Some(ref mut file) = file { + let _ = writeln!(file, "\n\nquery stack during panic:"); + } + while let Some(query) = current_query { + let Some(query_info) = job_map.map.get(&query) else { + break; + }; + let query_extra = query_info.frame.info.extract(); + if Some(count_printed) < limit_frames || limit_frames.is_none() { + // Only print to stderr as many stack frames as `num_frames` when present. + dcx.struct_failure_note(format!( + "#{} [{:?}] {}", + count_printed, query_info.frame.dep_kind, query_extra.description + )) + .with_span(query_info.job.span) + .emit(); + count_printed += 1; + } + + if let Some(ref mut file) = file { + let _ = writeln!( + file, + "#{} [{}] {}", + count_total, + qcx.tcx.dep_kind_vtable(query_info.frame.dep_kind).name, + query_extra.description + ); + } + + current_query = query_info.job.parent; + count_total += 1; + } + + if let Some(ref mut file) = file { + let _ = writeln!(file, "end of query stack"); + } + count_total +} + +#[inline(never)] +#[cold] +pub(crate) fn report_cycle<'a>( + sess: &'a Session, + CycleError { usage, cycle: stack }: &CycleError, +) -> Diag<'a> { + assert!(!stack.is_empty()); + + let span = stack[0].frame.info.default_span(stack[1 % stack.len()].span); + + let mut cycle_stack = Vec::new(); + + use crate::error::StackCount; + let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple }; + + for i in 1..stack.len() { + let frame = &stack[i].frame; + let span = frame.info.default_span(stack[(i + 1) % stack.len()].span); + cycle_stack + .push(crate::error::CycleStack { span, desc: frame.info.description.to_owned() }); + } + + let mut cycle_usage = None; + if let Some((span, ref query)) = *usage { + cycle_usage = Some(crate::error::CycleUsage { + span: query.info.default_span(span), + usage: query.info.description.to_string(), + }); + } + + let alias = + if stack.iter().all(|entry| matches!(entry.frame.info.def_kind, Some(DefKind::TyAlias))) { + Some(crate::error::Alias::Ty) + } else if stack.iter().all(|entry| entry.frame.info.def_kind == Some(DefKind::TraitAlias)) { + Some(crate::error::Alias::Trait) + } else { + None + }; + + let cycle_diag = crate::error::Cycle { + span, + cycle_stack, + stack_bottom: stack[0].frame.info.description.to_owned(), + alias, + cycle_usage, + stack_count, + note_span: (), + }; + + sess.dcx().create_err(cycle_diag) +} diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 19d43e9bb93d..9b2078275aae 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -3,43 +3,44 @@ // tidy-alphabetical-start #![allow(internal_features)] #![feature(adt_const_params)] +#![feature(core_intrinsics)] #![feature(min_specialization)] #![feature(rustc_attrs)] +#![feature(try_blocks)] // tidy-alphabetical-end use std::marker::ConstParamTy; -use rustc_data_structures::stable_hasher::HashStable; use rustc_data_structures::sync::AtomicU64; use rustc_middle::arena::Arena; -use rustc_middle::dep_graph::{self, DepKind, DepKindVTable, DepNodeIndex}; +use rustc_middle::dep_graph::{self, DepKind, DepKindVTable, DepNode, DepNodeIndex}; use rustc_middle::queries::{ self, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, }; use rustc_middle::query::AsLocalKey; use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; -use rustc_middle::query::plumbing::{QuerySystem, QuerySystemFns, QueryVTable}; -use rustc_middle::query::values::Value; +use rustc_middle::query::plumbing::{HashResult, QuerySystem, QuerySystemFns, QueryVTable}; use rustc_middle::ty::TyCtxt; use rustc_query_system::dep_graph::SerializedDepNodeIndex; -use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ - CycleError, CycleErrorHandling, HashResult, QueryCache, QueryDispatcher, QueryMap, QueryMode, - QueryState, get_query_incr, get_query_non_incr, + CycleError, CycleErrorHandling, QueryCache, QueryMode, QueryState, }; use rustc_span::{ErrorGuaranteed, Span}; +pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack}; +pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all}; use crate::plumbing::{encode_all_query_results, try_mark_green}; use crate::profiling_support::QueryKeyStringCache; - -#[macro_use] -mod plumbing; -pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all}; - -mod profiling_support; -pub use self::profiling_support::alloc_self_profile_query_strings; +pub use crate::profiling_support::alloc_self_profile_query_strings; +use crate::values::Value; mod error; +mod execution; +mod job; +#[macro_use] +mod plumbing; +mod profiling_support; +mod values; #[derive(ConstParamTy)] // Allow this struct to be used for const-generic values. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -52,8 +53,8 @@ struct QueryFlags { is_feedable: bool, } -/// Combines a [`QueryVTable`] with some additional compile-time booleans -/// to implement [`QueryDispatcher`], for use by code in [`rustc_query_system`]. +/// Combines a [`QueryVTable`] with some additional compile-time booleans. +/// "Dispatcher" should be understood as a near-synonym of "vtable". /// /// Baking these boolean flags into the type gives a modest but measurable /// improvement to compiler perf and compiler code size; see @@ -75,67 +76,63 @@ impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Clone } } -// This is `impl QueryDispatcher for SemiDynamicQueryDispatcher`. -impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> QueryDispatcher<'tcx> - for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> -where - for<'a> C::Key: HashStable>, -{ - type Qcx = QueryCtxt<'tcx>; - type Key = C::Key; - type Value = C::Value; - type Cache = C; - +impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> SemiDynamicQueryDispatcher<'tcx, C, FLAGS> { #[inline(always)] fn name(self) -> &'static str { self.vtable.name } #[inline(always)] - fn will_cache_on_disk_for_key(self, tcx: TyCtxt<'tcx>, key: &Self::Key) -> bool { + fn will_cache_on_disk_for_key(self, tcx: TyCtxt<'tcx>, key: &C::Key) -> bool { self.vtable.will_cache_on_disk_for_key_fn.map_or(false, |f| f(tcx, key)) } + // Don't use this method to access query results, instead use the methods on TyCtxt. #[inline(always)] - fn query_state(self, qcx: QueryCtxt<'tcx>) -> &'tcx QueryState<'tcx, Self::Key> { + fn query_state(self, qcx: QueryCtxt<'tcx>) -> &'tcx QueryState<'tcx, C::Key> { // Safety: // This is just manually doing the subfield referencing through pointer math. unsafe { &*(&qcx.tcx.query_system.states as *const QueryStates<'tcx>) .byte_add(self.vtable.query_state) - .cast::>() + .cast::>() } } + // Don't use this method to access query results, instead use the methods on TyCtxt. #[inline(always)] - fn query_cache(self, qcx: QueryCtxt<'tcx>) -> &'tcx Self::Cache { + fn query_cache(self, qcx: QueryCtxt<'tcx>) -> &'tcx C { // Safety: // This is just manually doing the subfield referencing through pointer math. unsafe { &*(&qcx.tcx.query_system.caches as *const QueryCaches<'tcx>) .byte_add(self.vtable.query_cache) - .cast::() + .cast::() } } + /// Calls `tcx.$query(key)` for this query, and discards the returned value. + /// See [`QueryVTable::call_query_method_fn`] for details of this strange operation. #[inline(always)] - fn execute_query(self, tcx: TyCtxt<'tcx>, key: Self::Key) -> Self::Value { - (self.vtable.execute_query)(tcx, key) + fn call_query_method(self, tcx: TyCtxt<'tcx>, key: C::Key) { + (self.vtable.call_query_method_fn)(tcx, key) } + /// Calls the actual provider function for this query. + /// See [`QueryVTable::invoke_provider_fn`] for more details. #[inline(always)] - fn compute(self, qcx: QueryCtxt<'tcx>, key: Self::Key) -> Self::Value { - (self.vtable.compute_fn)(qcx.tcx, key) + fn invoke_provider(self, qcx: QueryCtxt<'tcx>, key: C::Key) -> C::Value { + (self.vtable.invoke_provider_fn)(qcx.tcx, key) } #[inline(always)] fn try_load_from_disk( self, qcx: QueryCtxt<'tcx>, - key: &Self::Key, + key: &C::Key, prev_index: SerializedDepNodeIndex, index: DepNodeIndex, - ) -> Option { + ) -> Option { // `?` will return None immediately for queries that never cache to disk. self.vtable.try_load_from_disk_fn?(qcx.tcx, key, prev_index, index) } @@ -144,23 +141,24 @@ where fn is_loadable_from_disk( self, qcx: QueryCtxt<'tcx>, - key: &Self::Key, + key: &C::Key, index: SerializedDepNodeIndex, ) -> bool { self.vtable.is_loadable_from_disk_fn.map_or(false, |f| f(qcx.tcx, key, index)) } + /// Synthesize an error value to let compilation continue after a cycle. fn value_from_cycle_error( self, tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed, - ) -> Self::Value { + ) -> C::Value { (self.vtable.value_from_cycle_error)(tcx, cycle_error, guar) } #[inline(always)] - fn format_value(self) -> fn(&Self::Value) -> String { + fn format_value(self) -> fn(&C::Value) -> String { self.vtable.format_value } @@ -195,13 +193,17 @@ where } #[inline(always)] - fn hash_result(self) -> HashResult { + fn hash_result(self) -> HashResult { self.vtable.hash_result } + + fn construct_dep_node(self, tcx: TyCtxt<'tcx>, key: &C::Key) -> DepNode { + DepNode::construct(tcx, self.dep_kind(), key) + } } /// Provides access to vtable-like operations for a query -/// (by creating a [`QueryDispatcher`]), +/// (by creating a [`SemiDynamicQueryDispatcher`]), /// but also keeps track of the "unerased" value type of the query /// (i.e. the actual result type in the query declaration). /// @@ -211,17 +213,14 @@ where /// /// There is one macro-generated implementation of this trait for each query, /// on the type `rustc_query_impl::query_impl::$name::QueryType`. -trait QueryDispatcherUnerased<'tcx> { +trait QueryDispatcherUnerased<'tcx, C: QueryCache, const FLAGS: QueryFlags> { type UnerasedValue; - type Dispatcher: QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>; const NAME: &'static &'static str; - fn query_dispatcher(tcx: TyCtxt<'tcx>) -> Self::Dispatcher; + fn query_dispatcher(tcx: TyCtxt<'tcx>) -> SemiDynamicQueryDispatcher<'tcx, C, FLAGS>; - fn restore_val( - value: >::Value, - ) -> Self::UnerasedValue; + fn restore_val(value: C::Value) -> Self::UnerasedValue; } pub fn query_system<'tcx>( diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 0b6283347413..9804e6b21756 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -14,7 +14,8 @@ use rustc_hir::limit::Limit; use rustc_index::Idx; use rustc_middle::bug; use rustc_middle::dep_graph::{ - self, DepContext, DepKindVTable, DepNode, DepNodeIndex, SerializedDepNodeIndex, dep_kinds, + self, DepContext, DepKindVTable, DepNode, DepNodeIndex, DepsType, SerializedDepNodeIndex, + dep_kinds, }; use rustc_middle::query::Key; use rustc_middle::query::on_disk_cache::{ @@ -26,16 +27,17 @@ use rustc_middle::ty::print::with_reduced_queries; use rustc_middle::ty::tls::{self, ImplicitCtxt}; use rustc_middle::ty::{self, TyCtxt}; use rustc_query_system::dep_graph::{DepNodeKey, FingerprintStyle, HasDepContext}; -use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ - QueryCache, QueryContext, QueryDispatcher, QueryJobId, QueryMap, QuerySideEffect, - QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra, force_query, + QueryCache, QueryContext, QueryJobId, QuerySideEffect, QueryStackDeferred, QueryStackFrame, + QueryStackFrameExtra, }; use rustc_serialize::{Decodable, Encodable}; use rustc_span::def_id::LOCAL_CRATE; -use crate::QueryDispatcherUnerased; use crate::error::{QueryOverflow, QueryOverflowNote}; +use crate::execution::{all_inactive, force_query}; +use crate::job::{QueryJobMap, find_dep_kind_root}; +use crate::{QueryDispatcherUnerased, QueryFlags, SemiDynamicQueryDispatcher}; /// Implements [`QueryContext`] for use by [`rustc_query_system`], since that /// crate does not have direct access to [`TyCtxt`]. @@ -51,10 +53,10 @@ impl<'tcx> QueryCtxt<'tcx> { } fn depth_limit_error(self, job: QueryJobId) { - let query_map = self + let job_map = self .collect_active_jobs_from_all_queries(true) .expect("failed to collect active queries"); - let (info, depth) = job.find_dep_kind_root(query_map); + let (info, depth) = find_dep_kind_root(job, job_map); let suggested_limit = match self.tcx.recursion_limit() { Limit(0) => Limit(2), @@ -68,26 +70,9 @@ impl<'tcx> QueryCtxt<'tcx> { crate_name: self.tcx.crate_name(LOCAL_CRATE), }); } -} - -impl<'tcx> HasDepContext for QueryCtxt<'tcx> { - type Deps = rustc_middle::dep_graph::DepsType; - type DepContext = TyCtxt<'tcx>; #[inline] - fn dep_context(&self) -> &Self::DepContext { - &self.tcx - } -} - -impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { - #[inline] - fn jobserver_proxy(&self) -> &Proxy { - &self.tcx.jobserver_proxy - } - - #[inline] - fn next_job_id(self) -> QueryJobId { + pub(crate) fn next_job_id(self) -> QueryJobId { QueryJobId( NonZero::new( self.tcx.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed), @@ -97,60 +82,14 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { } #[inline] - fn current_query_job(self) -> Option { + pub(crate) fn current_query_job(self) -> Option { tls::with_related_context(self.tcx, |icx| icx.query) } - /// Returns a map of currently active query jobs, collected from all queries. - /// - /// If `require_complete` is `true`, this function locks all shards of the - /// query results to produce a complete map, which always returns `Ok`. - /// Otherwise, it may return an incomplete map as an error if any shard - /// lock cannot be acquired. - /// - /// Prefer passing `false` to `require_complete` to avoid potential deadlocks, - /// especially when called from within a deadlock handler, unless a - /// complete map is needed and no deadlock is possible at this call site. - fn collect_active_jobs_from_all_queries( - self, - require_complete: bool, - ) -> Result, QueryMap<'tcx>> { - let mut jobs = QueryMap::default(); - let mut complete = true; - - for gather_fn in crate::PER_QUERY_GATHER_ACTIVE_JOBS_FNS.iter() { - if gather_fn(self.tcx, &mut jobs, require_complete).is_none() { - complete = false; - } - } - - if complete { Ok(jobs) } else { Err(jobs) } - } - - // Interactions with on_disk_cache - fn load_side_effect( - self, - prev_dep_node_index: SerializedDepNodeIndex, - ) -> Option { - self.tcx - .query_system - .on_disk_cache - .as_ref() - .and_then(|c| c.load_side_effect(self.tcx, prev_dep_node_index)) - } - - #[inline(never)] - #[cold] - fn store_side_effect(self, dep_node_index: DepNodeIndex, side_effect: QuerySideEffect) { - if let Some(c) = self.tcx.query_system.on_disk_cache.as_ref() { - c.store_side_effect(dep_node_index, side_effect) - } - } - /// Executes a job by changing the `ImplicitCtxt` to point to the /// new query job while it executes. #[inline(always)] - fn start_query( + pub(crate) fn start_query( self, token: QueryJobId, depth_limit: bool, @@ -178,6 +117,69 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { tls::enter_context(&new_icx, compute) }) } + + /// Returns a map of currently active query jobs, collected from all queries. + /// + /// If `require_complete` is `true`, this function locks all shards of the + /// query results to produce a complete map, which always returns `Ok`. + /// Otherwise, it may return an incomplete map as an error if any shard + /// lock cannot be acquired. + /// + /// Prefer passing `false` to `require_complete` to avoid potential deadlocks, + /// especially when called from within a deadlock handler, unless a + /// complete map is needed and no deadlock is possible at this call site. + pub fn collect_active_jobs_from_all_queries( + self, + require_complete: bool, + ) -> Result, QueryJobMap<'tcx>> { + let mut job_map_out = QueryJobMap::default(); + let mut complete = true; + + for gather_fn in crate::PER_QUERY_GATHER_ACTIVE_JOBS_FNS.iter() { + if gather_fn(self.tcx, require_complete, &mut job_map_out).is_none() { + complete = false; + } + } + + if complete { Ok(job_map_out) } else { Err(job_map_out) } + } +} + +impl<'tcx> HasDepContext for QueryCtxt<'tcx> { + type Deps = DepsType; + type DepContext = TyCtxt<'tcx>; + + #[inline] + fn dep_context(&self) -> &Self::DepContext { + &self.tcx + } +} + +impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { + #[inline] + fn jobserver_proxy(&self) -> &Proxy { + &self.tcx.jobserver_proxy + } + + // Interactions with on_disk_cache + fn load_side_effect( + self, + prev_dep_node_index: SerializedDepNodeIndex, + ) -> Option { + self.tcx + .query_system + .on_disk_cache + .as_ref() + .and_then(|c| c.load_side_effect(self.tcx, prev_dep_node_index)) + } + + #[inline(never)] + #[cold] + fn store_side_effect(self, dep_node_index: DepNodeIndex, side_effect: QuerySideEffect) { + if let Some(c) = self.tcx.query_system.on_disk_cache.as_ref() { + c.store_side_effect(dep_node_index, side_effect) + } + } } pub(super) fn try_mark_green<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool { @@ -374,7 +376,7 @@ pub(crate) fn create_deferred_query_stack_frame<'tcx, Cache>( ) -> QueryStackFrame> where Cache: QueryCache, - Cache::Key: Key + DynSend + DynSync + for<'a> HashStable> + 'tcx, + Cache::Key: Key + DynSend + DynSync, { let kind = vtable.dep_kind; @@ -392,18 +394,18 @@ where QueryStackFrame::new(info, kind, hash, def_id, def_id_for_ty_in_cycle) } -pub(crate) fn encode_query_results<'a, 'tcx, Q>( - query: Q::Dispatcher, +pub(crate) fn encode_query_results<'a, 'tcx, Q, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, qcx: QueryCtxt<'tcx>, encoder: &mut CacheEncoder<'a, 'tcx>, query_result_index: &mut EncodedDepNodeIndex, ) where - Q: QueryDispatcherUnerased<'tcx>, + Q: QueryDispatcherUnerased<'tcx, C, FLAGS>, Q::UnerasedValue: Encodable>, { let _timer = qcx.tcx.prof.generic_activity_with_arg("encode_query_results_for", query.name()); - assert!(query.query_state(qcx).all_inactive()); + assert!(all_inactive(query.query_state(qcx))); let cache = query.query_cache(qcx); cache.iter(&mut |key, value, dep_node| { if query.will_cache_on_disk_for_key(qcx.tcx, key) { @@ -419,8 +421,8 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q>( }); } -pub(crate) fn query_key_hash_verify<'tcx>( - query: impl QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>, +pub(crate) fn query_key_hash_verify<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, qcx: QueryCtxt<'tcx>, ) { let _timer = qcx.tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name()); @@ -445,17 +447,20 @@ pub(crate) fn query_key_hash_verify<'tcx>( }); } -fn try_load_from_on_disk_cache<'tcx, Q>(query: Q, tcx: TyCtxt<'tcx>, dep_node: DepNode) -where - Q: QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>, -{ +fn try_load_from_on_disk_cache<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + tcx: TyCtxt<'tcx>, + dep_node: DepNode, +) { debug_assert!(tcx.dep_graph.is_green(&dep_node)); - let key = Q::Key::recover(tcx, &dep_node).unwrap_or_else(|| { + let key = C::Key::recover(tcx, &dep_node).unwrap_or_else(|| { panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash) }); if query.will_cache_on_disk_for_key(tcx, &key) { - let _ = query.execute_query(tcx, key); + // Call `tcx.$query(key)` for its side-effect of loading the disk-cached + // value into memory. + query.call_query_method(tcx, key); } } @@ -491,10 +496,11 @@ where value } -fn force_from_dep_node<'tcx, Q>(query: Q, tcx: TyCtxt<'tcx>, dep_node: DepNode) -> bool -where - Q: QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>, -{ +fn force_from_dep_node<'tcx, C: QueryCache, const FLAGS: QueryFlags>( + query: SemiDynamicQueryDispatcher<'tcx, C, FLAGS>, + tcx: TyCtxt<'tcx>, + dep_node: DepNode, +) -> bool { // We must avoid ever having to call `force_from_dep_node()` for a // `DepNode::codegen_unit`: // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we @@ -513,7 +519,7 @@ where "calling force_from_dep_node() on dep_kinds::codegen_unit" ); - if let Some(key) = Q::Key::recover(tcx, &dep_node) { + if let Some(key) = C::Key::recover(tcx, &dep_node) { force_query(query, QueryCtxt::new(tcx), key, dep_node); true } else { @@ -521,17 +527,22 @@ where } } -pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q>( - is_anon: bool, +pub(crate) fn make_dep_kind_vtable_for_query< + 'tcx, + Q, + C: QueryCache + 'tcx, + const FLAGS: QueryFlags, +>( is_eval_always: bool, ) -> DepKindVTable<'tcx> where - Q: QueryDispatcherUnerased<'tcx>, + Q: QueryDispatcherUnerased<'tcx, C, FLAGS>, { + let is_anon = FLAGS.is_anon; let fingerprint_style = if is_anon { FingerprintStyle::Opaque } else { - ::Key::fingerprint_style() + >>::fingerprint_style() }; if is_anon || !fingerprint_style.reconstructible() { @@ -588,7 +599,7 @@ macro_rules! define_queries { ) -> Option>> { #[cfg(debug_assertions)] let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); - get_query_incr( + execution::get_query_incr( QueryType::query_dispatcher(tcx), QueryCtxt::new(tcx), span, @@ -608,7 +619,7 @@ macro_rules! define_queries { key: queries::$name::Key<'tcx>, __mode: QueryMode, ) -> Option>> { - Some(get_query_non_incr( + Some(execution::get_query_non_incr( QueryType::query_dispatcher(tcx), QueryCtxt::new(tcx), span, @@ -617,14 +628,15 @@ macro_rules! define_queries { } } - /// Defines a `compute` function for this query, to be used as a - /// function pointer in the query's vtable. - mod compute_fn { + /// Defines an `invoke_provider` function that calls the query's provider, + /// to be used as a function pointer in the query's vtable. + /// + /// To mark a short-backtrace boundary, the function's actual name + /// (after demangling) must be `__rust_begin_short_backtrace`. + mod invoke_provider_fn { use super::*; use ::rustc_middle::queries::$name::{Key, Value, provided_to_erased}; - /// This function would be named `compute`, but we also want it - /// to mark the boundaries of an omitted region in backtraces. #[inline(never)] pub(crate) fn __rust_begin_short_backtrace<'tcx>( tcx: TyCtxt<'tcx>, @@ -635,10 +647,13 @@ macro_rules! define_queries { // Call the actual provider function for this query. let provided_value = call_provider!([$($modifiers)*][tcx, $name, key]); + rustc_middle::ty::print::with_reduced_queries!({ tracing::trace!(?provided_value); }); + // Erase the returned value, because `QueryVTable` uses erased values. + // For queries with `arena_cache`, this also arena-allocates the value. provided_to_erased(tcx, provided_value) } } @@ -658,8 +673,12 @@ macro_rules! define_queries { } { None }), - execute_query: |tcx, key| erase::erase_val(tcx.$name(key)), - compute_fn: self::compute_fn::__rust_begin_short_backtrace, + call_query_method_fn: |tcx, key| { + // Call the query method for its side-effect of loading a value + // from disk-cache; the caller doesn't need the value. + let _ = tcx.$name(key); + }, + invoke_provider_fn: self::invoke_provider_fn::__rust_begin_short_backtrace, try_load_from_disk_fn: if_cache_on_disk!([$($modifiers)*] { Some(|tcx, key, prev_index, index| { // Check the `cache_on_disk_if` condition for this key. @@ -705,25 +724,26 @@ macro_rules! define_queries { is_feedable: feedable!([$($modifiers)*]), }; - impl<'tcx> QueryDispatcherUnerased<'tcx> for QueryType<'tcx> { + impl<'tcx> QueryDispatcherUnerased<'tcx, queries::$name::Storage<'tcx>, FLAGS> + for QueryType<'tcx> + { type UnerasedValue = queries::$name::Value<'tcx>; - type Dispatcher = SemiDynamicQueryDispatcher< - 'tcx, - queries::$name::Storage<'tcx>, - FLAGS, - >; const NAME: &'static &'static str = &stringify!($name); #[inline(always)] - fn query_dispatcher(tcx: TyCtxt<'tcx>) -> Self::Dispatcher { + fn query_dispatcher(tcx: TyCtxt<'tcx>) + -> SemiDynamicQueryDispatcher<'tcx, queries::$name::Storage<'tcx>, FLAGS> + { SemiDynamicQueryDispatcher { vtable: &tcx.query_system.query_vtables.$name, } } #[inline(always)] - fn restore_val(value: >::Value) -> Self::UnerasedValue { + fn restore_val(value: as QueryCache>::Value) + -> Self::UnerasedValue + { erase::restore_val::>(value) } } @@ -733,8 +753,8 @@ macro_rules! define_queries { /// Should only be called through `PER_QUERY_GATHER_ACTIVE_JOBS_FNS`. pub(crate) fn gather_active_jobs<'tcx>( tcx: TyCtxt<'tcx>, - qmap: &mut QueryMap<'tcx>, require_complete: bool, + job_map_out: &mut QueryJobMap<'tcx>, ) -> Option<()> { let make_frame = |tcx: TyCtxt<'tcx>, key| { let vtable = &tcx.query_system.query_vtables.$name; @@ -742,11 +762,11 @@ macro_rules! define_queries { }; // Call `gather_active_jobs_inner` to do the actual work. - let res = tcx.query_system.states.$name.gather_active_jobs_inner( + let res = crate::execution::gather_active_jobs_inner(&tcx.query_system.states.$name, tcx, make_frame, - qmap, require_complete, + job_map_out, ); // this can be called during unwinding, and the function has a `try_`-prefix, so @@ -779,7 +799,11 @@ macro_rules! define_queries { encoder: &mut CacheEncoder<'_, 'tcx>, query_result_index: &mut EncodedDepNodeIndex ) { - $crate::plumbing::encode_query_results::>( + $crate::plumbing::encode_query_results::< + query_impl::$name::QueryType<'tcx>, + _, + _ + > ( query_impl::$name::QueryType::query_dispatcher(tcx), QueryCtxt::new(tcx), encoder, @@ -825,9 +849,13 @@ macro_rules! define_queries { /// each individual query, so that we have distinct function names to /// grep for.) const PER_QUERY_GATHER_ACTIVE_JOBS_FNS: &[ - for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<'tcx>, require_complete: bool) -> Option<()> + for<'tcx> fn( + tcx: TyCtxt<'tcx>, + require_complete: bool, + job_map_out: &mut QueryJobMap<'tcx>, + ) -> Option<()> ] = &[ - $(query_impl::$name::gather_active_jobs),* + $( $crate::query_impl::$name::gather_active_jobs ),* ]; const ALLOC_SELF_PROFILE_QUERY_STRINGS: &[ @@ -959,8 +987,7 @@ macro_rules! define_queries { $(pub(crate) fn $name<'tcx>() -> DepKindVTable<'tcx> { use $crate::query_impl::$name::QueryType; - $crate::plumbing::make_dep_kind_vtable_for_query::>( - is_anon!([$($modifiers)*]), + $crate::plumbing::make_dep_kind_vtable_for_query::, _, _>( is_eval_always!([$($modifiers)*]), ) })* diff --git a/compiler/rustc_middle/src/query/values.rs b/compiler/rustc_query_impl/src/values.rs similarity index 98% rename from compiler/rustc_middle/src/query/values.rs rename to compiler/rustc_query_impl/src/values.rs index 0828758cc94e..783e7a10d12a 100644 --- a/compiler/rustc_middle/src/query/values.rs +++ b/compiler/rustc_query_impl/src/values.rs @@ -7,15 +7,17 @@ use rustc_errors::codes::*; use rustc_errors::{Applicability, MultiSpan, pluralize, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_query_system::query::{CycleError, report_cycle}; +use rustc_middle::dep_graph::dep_kinds; +use rustc_middle::query::plumbing::CyclePlaceholder; +use rustc_middle::ty::{self, Representability, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug}; +use rustc_query_system::query::CycleError; use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span}; -use crate::dep_graph::dep_kinds; -use crate::query::plumbing::CyclePlaceholder; -use crate::ty::{self, Representability, Ty, TyCtxt}; +use crate::job::report_cycle; -pub trait Value<'tcx>: Sized { +pub(crate) trait Value<'tcx>: Sized { fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) -> Self; } diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 06e576baf22a..94a04b148c4b 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -82,7 +82,7 @@ pub(super) enum DepNodeColor { Unknown, } -pub(crate) struct DepGraphData { +pub struct DepGraphData { /// The new encoding of the dependency graph, optimized for red/green /// tracking. The `current` field is the dependency graph of only the /// current compilation session: We don't merge the previous dep-graph into @@ -171,7 +171,7 @@ impl DepGraph { } #[inline] - pub(crate) fn data(&self) -> Option<&DepGraphData> { + pub fn data(&self) -> Option<&DepGraphData> { self.data.as_deref() } @@ -323,7 +323,7 @@ impl DepGraphData { /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html #[inline(always)] - pub(crate) fn with_task, A: Debug, R>( + pub fn with_task, A: Debug, R>( &self, key: DepNode, cx: Ctxt, @@ -336,13 +336,17 @@ impl DepGraphData { // in `DepGraph::try_mark_green()`. // 2. Two distinct query keys get mapped to the same `DepNode` // (see for example #48923). - self.assert_dep_node_not_yet_allocated_in_current_session(&key, || { - format!( - "forcing query with already existing `DepNode`\n\ + self.assert_dep_node_not_yet_allocated_in_current_session( + cx.dep_context().sess(), + &key, + || { + format!( + "forcing query with already existing `DepNode`\n\ - query-key: {arg:?}\n\ - dep-node: {key:?}" - ) - }); + ) + }, + ); let with_deps = |task_deps| D::with_deps(task_deps, || task(cx, arg)); let (result, edges) = if cx.dep_context().is_eval_always(key.kind) { @@ -373,7 +377,7 @@ impl DepGraphData { /// FIXME: This could perhaps return a `WithDepNode` to ensure that the /// user of this function actually performs the read; we'll have to see /// how to make that work with `anon` in `execute_job_incr`, though. - pub(crate) fn with_anon_task_inner, OP, R>( + pub fn with_anon_task_inner, OP, R>( &self, cx: Tcx, dep_kind: DepKind, @@ -627,12 +631,20 @@ impl DepGraph { impl DepGraphData { fn assert_dep_node_not_yet_allocated_in_current_session( &self, + sess: &Session, dep_node: &DepNode, msg: impl FnOnce() -> S, ) { if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) { - let current = self.colors.get(prev_index); - assert_matches!(current, DepNodeColor::Unknown, "{}", msg()) + let color = self.colors.get(prev_index); + let ok = match color { + DepNodeColor::Unknown => true, + DepNodeColor::Red => false, + DepNodeColor::Green(..) => sess.threads() > 1, // Other threads may mark this green + }; + if !ok { + panic!("{}", msg()) + } } else if let Some(nodes_in_current_session) = &self.current.nodes_in_current_session { outline(|| { let seen = nodes_in_current_session.lock().contains_key(dep_node); @@ -653,12 +665,12 @@ impl DepGraphData { /// Returns true if the given node has been marked as green during the /// current compilation session. Used in various assertions #[inline] - pub(crate) fn is_index_green(&self, prev_index: SerializedDepNodeIndex) -> bool { + pub fn is_index_green(&self, prev_index: SerializedDepNodeIndex) -> bool { matches!(self.colors.get(prev_index), DepNodeColor::Green(_)) } #[inline] - pub(crate) fn prev_fingerprint_of(&self, prev_index: SerializedDepNodeIndex) -> Fingerprint { + pub fn prev_fingerprint_of(&self, prev_index: SerializedDepNodeIndex) -> Fingerprint { self.previous.fingerprint_by_index(prev_index) } @@ -667,7 +679,7 @@ impl DepGraphData { self.previous.index_to_node(prev_index) } - pub(crate) fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) { + pub fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) { self.debug_loaded_from_disk.lock().insert(dep_node); } @@ -776,17 +788,22 @@ impl DepGraphData { } } - fn promote_node_and_deps_to_current(&self, prev_index: SerializedDepNodeIndex) -> DepNodeIndex { + fn promote_node_and_deps_to_current( + &self, + prev_index: SerializedDepNodeIndex, + ) -> Option { self.current.debug_assert_not_in_new_nodes(&self.previous, prev_index); let dep_node_index = self.current.encoder.send_promoted(prev_index, &self.colors); #[cfg(debug_assertions)] - self.current.record_edge( - dep_node_index, - *self.previous.index_to_node(prev_index), - self.previous.fingerprint_by_index(prev_index), - ); + if let Some(dep_node_index) = dep_node_index { + self.current.record_edge( + dep_node_index, + *self.previous.index_to_node(prev_index), + self.previous.fingerprint_by_index(prev_index), + ); + } dep_node_index } @@ -863,7 +880,7 @@ impl DepGraphData { /// A node will have an index, when it's already been marked green, or when we can mark it /// green. This function will mark the current task as a reader of the specified node, when /// a node index can be found for that node. - pub(crate) fn try_mark_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( + pub fn try_mark_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( &self, qcx: Qcx, dep_node: &DepNode, @@ -1006,8 +1023,10 @@ impl DepGraphData { // There may be multiple threads trying to mark the same dep node green concurrently // We allocating an entry for the node in the current dependency graph and - // adding all the appropriate edges imported from the previous graph - let dep_node_index = self.promote_node_and_deps_to_current(prev_dep_node_index); + // adding all the appropriate edges imported from the previous graph. + // + // `no_hash` nodes may fail this promotion due to already being conservatively colored red. + let dep_node_index = self.promote_node_and_deps_to_current(prev_dep_node_index)?; // ... and finally storing a "Green" entry in the color map. // Multiple threads can all write the same color here @@ -1035,11 +1054,12 @@ impl DepGraph { pub fn assert_dep_node_not_yet_allocated_in_current_session( &self, + sess: &Session, dep_node: &DepNode, msg: impl FnOnce() -> S, ) { if let Some(data) = &self.data { - data.assert_dep_node_not_yet_allocated_in_current_session(dep_node, msg) + data.assert_dep_node_not_yet_allocated_in_current_session(sess, dep_node, msg) } } @@ -1074,7 +1094,7 @@ impl DepGraph { if let Some(data) = &self.data { data.current.encoder.finish(&data.current) } else { Ok(0) } } - pub(crate) fn next_virtual_depnode_index(&self) -> DepNodeIndex { + pub fn next_virtual_depnode_index(&self) -> DepNodeIndex { debug_assert!(self.data.is_none()); let index = self.virtual_dep_node_index.fetch_add(1, Ordering::Relaxed); DepNodeIndex::from_u32(index) @@ -1377,26 +1397,27 @@ impl DepNodeColorMap { } /// This tries to atomically mark a node green and assign `index` as the new - /// index. This returns `Ok` if `index` gets assigned, otherwise it returns - /// the already allocated index in `Err`. - #[inline] - pub(super) fn try_mark_green( + /// index if `green` is true, otherwise it will try to atomicaly mark it red. + /// + /// This returns `Ok` if `index` gets assigned or the node is marked red, otherwise it returns + /// the already allocated index in `Err` if it is green already. If it was already + /// red, `Err(None)` is returned. + #[inline(always)] + pub(super) fn try_mark( &self, prev_index: SerializedDepNodeIndex, index: DepNodeIndex, - ) -> Result<(), DepNodeIndex> { + green: bool, + ) -> Result<(), Option> { let value = &self.values[prev_index]; match value.compare_exchange( COMPRESSED_UNKNOWN, - index.as_u32(), + if green { index.as_u32() } else { COMPRESSED_RED }, Ordering::Relaxed, Ordering::Relaxed, ) { Ok(_) => Ok(()), - Err(v) => Err({ - assert_ne!(v, COMPRESSED_RED, "tried to mark a red node as green"); - DepNodeIndex::from_u32(v) - }), + Err(v) => Err(if v == COMPRESSED_RED { None } else { Some(DepNodeIndex::from_u32(v)) }), } } @@ -1419,7 +1440,7 @@ impl DepNodeColorMap { pub(super) fn insert_red(&self, index: SerializedDepNodeIndex) { let value = self.values[index].swap(COMPRESSED_RED, Ordering::Release); // Sanity check for duplicate nodes - assert_eq!(value, COMPRESSED_UNKNOWN, "trying to encode a dep node twice"); + assert_eq!(value, COMPRESSED_UNKNOWN, "tried to color an already colored node as red"); } } diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index f2cb01d5e9b4..da110ad2c77c 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -8,8 +8,9 @@ mod serialized; use std::panic; pub use dep_node::{DepKind, DepKindVTable, DepNode, DepNodeKey, WorkProductId}; -pub(crate) use graph::DepGraphData; -pub use graph::{DepGraph, DepNodeIndex, TaskDepsRef, WorkProduct, WorkProductMap, hash_result}; +pub use graph::{ + DepGraph, DepGraphData, DepNodeIndex, TaskDepsRef, WorkProduct, WorkProductMap, hash_result, +}; pub use query::DepGraphQuery; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::DynSync; diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index aa4c928d3cdc..3b5954fc8f94 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -898,15 +898,12 @@ impl GraphEncoder { let index = self.status.next_index(&mut *local); - if is_green { - // Use `try_mark_green` to avoid racing when `send_promoted` is called concurrently - // on the same index. - match colors.try_mark_green(prev_index, index) { - Ok(()) => (), - Err(dep_node_index) => return dep_node_index, - } - } else { - colors.insert_red(prev_index); + // Use `try_mark` to avoid racing when `send_promoted` is called concurrently + // on the same index. + match colors.try_mark(prev_index, index, is_green) { + Ok(()) => (), + Err(None) => panic!("dep node {:?} is unexpectedly red", prev_index), + Err(Some(dep_node_index)) => return dep_node_index, } self.status.bump_index(&mut *local); @@ -918,13 +915,13 @@ impl GraphEncoder { /// from the previous dep graph and expects all edges to already have a new dep node index /// assigned. /// - /// This will also ensure the dep node is marked green. + /// This will also ensure the dep node is marked green if `Some` is returned. #[inline] pub(crate) fn send_promoted( &self, prev_index: SerializedDepNodeIndex, colors: &DepNodeColorMap, - ) -> DepNodeIndex { + ) -> Option { let _prof_timer = self.profiler.generic_activity("incr_comp_encode_dep_graph"); let mut local = self.status.local.borrow_mut(); @@ -932,7 +929,7 @@ impl GraphEncoder { // Use `try_mark_green` to avoid racing when `send_promoted` or `send_and_color` // is called concurrently on the same index. - match colors.try_mark_green(prev_index, index) { + match colors.try_mark(prev_index, index, true) { Ok(()) => { self.status.bump_index(&mut *local); self.status.encode_promoted_node( @@ -942,7 +939,7 @@ impl GraphEncoder { colors, &mut *local, ); - index + Some(index) } Err(dep_node_index) => dep_node_index, } diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index adce85bf31be..f48afe6f75fa 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -1,62 +1,4 @@ -use rustc_errors::codes::*; -use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_span::Span; - -#[derive(Subdiagnostic)] -#[note("...which requires {$desc}...")] -pub(crate) struct CycleStack { - #[primary_span] - pub span: Span, - pub desc: String, -} - -#[derive(Subdiagnostic)] -pub(crate) enum StackCount { - #[note("...which immediately requires {$stack_bottom} again")] - Single, - #[note("...which again requires {$stack_bottom}, completing the cycle")] - Multiple, -} - -#[derive(Subdiagnostic)] -pub(crate) enum Alias { - #[note("type aliases cannot be recursive")] - #[help("consider using a struct, enum, or union instead to break the cycle")] - #[help( - "see for more information" - )] - Ty, - #[note("trait aliases cannot be recursive")] - Trait, -} - -#[derive(Subdiagnostic)] -#[note("cycle used when {$usage}")] -pub(crate) struct CycleUsage { - #[primary_span] - pub span: Span, - pub usage: String, -} - -#[derive(Diagnostic)] -#[diag("cycle detected when {$stack_bottom}", code = E0391)] -pub(crate) struct Cycle { - #[primary_span] - pub span: Span, - pub stack_bottom: String, - #[subdiagnostic] - pub cycle_stack: Vec, - #[subdiagnostic] - pub stack_count: StackCount, - #[subdiagnostic] - pub alias: Option, - #[subdiagnostic] - pub cycle_usage: Option, - #[note( - "see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information" - )] - pub note_span: (), -} +use rustc_macros::Diagnostic; #[derive(Diagnostic)] #[diag("internal compiler error: reentrant incremental verify failure, suppressing message")] diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index 840f0b35266d..2e118dc3359f 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -1,13 +1,20 @@ -use rustc_ast as ast; +use std::hash::Hash; + use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::definitions::DefPathHash; use rustc_session::Session; use rustc_session::cstore::Untracked; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, CachingSourceMapView, DUMMY_SP, SourceFile, Span, SpanData, Symbol}; +use rustc_span::{CachingSourceMapView, DUMMY_SP, Pos, Span}; -use crate::ich; +// Very often, we are hashing something that does not need the `CachingSourceMapView`, so we +// initialize it lazily. +#[derive(Clone)] +enum CachingSourceMap<'a> { + Unused(&'a SourceMap), + InUse(CachingSourceMapView<'a>), +} /// This is the context state available during incr. comp. hashing. It contains /// enough information to transform `DefId`s and `HirId`s into stable `DefPath`s (i.e., @@ -19,10 +26,7 @@ pub struct StableHashingContext<'a> { // The value of `-Z incremental-ignore-spans`. // This field should only be used by `unstable_opts_incremental_ignore_span` incremental_ignore_spans: bool, - // Very often, we are hashing something that does not need the - // `CachingSourceMapView`, so we initialize it lazily. - raw_source_map: &'a SourceMap, - caching_source_map: Option>, + caching_source_map: CachingSourceMap<'a>, hashing_controls: HashingControls, } @@ -34,8 +38,7 @@ impl<'a> StableHashingContext<'a> { StableHashingContext { untracked, incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans, - caching_source_map: None, - raw_source_map: sess.source_map(), + caching_source_map: CachingSourceMap::Unused(sess.source_map()), hashing_controls: HashingControls { hash_spans: hash_spans_initial }, } } @@ -49,81 +52,145 @@ impl<'a> StableHashingContext<'a> { } #[inline] - pub fn def_path_hash(&self, def_id: DefId) -> DefPathHash { - if let Some(def_id) = def_id.as_local() { - self.local_def_path_hash(def_id) - } else { - self.untracked.cstore.read().def_path_hash(def_id) - } - } - - #[inline] - pub fn local_def_path_hash(&self, def_id: LocalDefId) -> DefPathHash { - self.untracked.definitions.read().def_path_hash(def_id) - } - - #[inline] - pub fn source_map(&mut self) -> &mut CachingSourceMapView<'a> { + fn source_map(&mut self) -> &mut CachingSourceMapView<'a> { match self.caching_source_map { - Some(ref mut sm) => sm, - ref mut none => { - *none = Some(CachingSourceMapView::new(self.raw_source_map)); - none.as_mut().unwrap() + CachingSourceMap::InUse(ref mut sm) => sm, + CachingSourceMap::Unused(sm) => { + self.caching_source_map = CachingSourceMap::InUse(CachingSourceMapView::new(sm)); + self.source_map() // this recursive call will hit the `InUse` case } } } - #[inline] - pub fn is_ignored_attr(&self, name: Symbol) -> bool { - ich::IGNORED_ATTRIBUTES.contains(&name) - } - - #[inline] - pub fn hashing_controls(&self) -> HashingControls { - self.hashing_controls.clone() - } -} - -impl<'a> HashStable> for ast::NodeId { - #[inline] - fn hash_stable(&self, _: &mut StableHashingContext<'a>, _: &mut StableHasher) { - panic!("Node IDs should not appear in incremental state"); - } -} - -impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { - #[inline] - fn hash_spans(&self) -> bool { - self.hashing_controls.hash_spans - } - - #[inline] - fn unstable_opts_incremental_ignore_spans(&self) -> bool { - self.incremental_ignore_spans - } - - #[inline] - fn def_path_hash(&self, def_id: DefId) -> DefPathHash { - self.def_path_hash(def_id) - } - #[inline] fn def_span(&self, def_id: LocalDefId) -> Span { self.untracked.source_span.get(def_id).unwrap_or(DUMMY_SP) } #[inline] - fn span_data_to_lines_and_cols( - &mut self, - span: &SpanData, - ) -> Option<(&SourceFile, usize, BytePos, usize, BytePos)> { - self.source_map().span_data_to_lines_and_cols(span) - } - - #[inline] - fn hashing_controls(&self) -> HashingControls { - self.hashing_controls.clone() + pub fn hashing_controls(&self) -> HashingControls { + self.hashing_controls } } +impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { + /// Hashes a span in a stable way. We can't directly hash the span's `BytePos` fields (that + /// would be similar to hashing pointers, since those are just offsets into the `SourceMap`). + /// Instead, we hash the (file name, line, column) triple, which stays the same even if the + /// containing `SourceFile` has moved within the `SourceMap`. + /// + /// Also note that we are hashing byte offsets for the column, not unicode codepoint offsets. + /// For the purpose of the hash that's sufficient. Also, hashing filenames is expensive so we + /// avoid doing it twice when the span starts and ends in the same file, which is almost always + /// the case. + /// + /// IMPORTANT: changes to this method should be reflected in implementations of `SpanEncoder`. + #[inline] + fn span_hash_stable(&mut self, span: Span, hasher: &mut StableHasher) { + const TAG_VALID_SPAN: u8 = 0; + const TAG_INVALID_SPAN: u8 = 1; + const TAG_RELATIVE_SPAN: u8 = 2; + + if !self.hashing_controls().hash_spans { + return; + } + + let span = span.data_untracked(); + span.ctxt.hash_stable(self, hasher); + span.parent.hash_stable(self, hasher); + + if span.is_dummy() { + Hash::hash(&TAG_INVALID_SPAN, hasher); + return; + } + + let parent = span.parent.map(|parent| self.def_span(parent).data_untracked()); + if let Some(parent) = parent + && parent.contains(span) + { + // This span is enclosed in a definition: only hash the relative position. This catches + // a subset of the cases from the `file.contains(parent.lo)`. But we can do this check + // cheaply without the expensive `span_data_to_lines_and_cols` query. + Hash::hash(&TAG_RELATIVE_SPAN, hasher); + (span.lo - parent.lo).to_u32().hash_stable(self, hasher); + (span.hi - parent.lo).to_u32().hash_stable(self, hasher); + return; + } + + // If this is not an empty or invalid span, we want to hash the last position that belongs + // to it, as opposed to hashing the first position past it. + let Some((file, line_lo, col_lo, line_hi, col_hi)) = + self.source_map().span_data_to_lines_and_cols(&span) + else { + Hash::hash(&TAG_INVALID_SPAN, hasher); + return; + }; + + if let Some(parent) = parent + && file.contains(parent.lo) + { + // This span is relative to another span in the same file, + // only hash the relative position. + Hash::hash(&TAG_RELATIVE_SPAN, hasher); + Hash::hash(&(span.lo.0.wrapping_sub(parent.lo.0)), hasher); + Hash::hash(&(span.hi.0.wrapping_sub(parent.lo.0)), hasher); + return; + } + + Hash::hash(&TAG_VALID_SPAN, hasher); + Hash::hash(&file.stable_id, hasher); + + // Hash both the length and the end location (line/column) of a span. If we hash only the + // length, for example, then two otherwise equal spans with different end locations will + // have the same hash. This can cause a problem during incremental compilation wherein a + // previous result for a query that depends on the end location of a span will be + // incorrectly reused when the end location of the span it depends on has changed (see + // issue #74890). A similar analysis applies if some query depends specifically on the + // length of the span, but we only hash the end location. So hash both. + + let col_lo_trunc = (col_lo.0 as u64) & 0xFF; + let line_lo_trunc = ((line_lo as u64) & 0xFF_FF_FF) << 8; + let col_hi_trunc = (col_hi.0 as u64) & 0xFF << 32; + let line_hi_trunc = ((line_hi as u64) & 0xFF_FF_FF) << 40; + let col_line = col_lo_trunc | line_lo_trunc | col_hi_trunc | line_hi_trunc; + let len = (span.hi - span.lo).0; + Hash::hash(&col_line, hasher); + Hash::hash(&len, hasher); + } + + #[inline] + fn def_path_hash(&self, def_id: DefId) -> DefPathHash { + if let Some(def_id) = def_id.as_local() { + self.untracked.definitions.read().def_path_hash(def_id) + } else { + self.untracked.cstore.read().def_path_hash(def_id) + } + } + + /// Assert that the provided `HashStableContext` is configured with the default + /// `HashingControls`. We should always have bailed out before getting to here with a + /// non-default mode. With this check in place, we can avoid the need to maintain separate + /// versions of `ExpnData` hashes for each permutation of `HashingControls` settings. + #[inline] + fn assert_default_hashing_controls(&self, msg: &str) { + let hashing_controls = self.hashing_controls; + let HashingControls { hash_spans } = hashing_controls; + + // Note that we require that `hash_spans` be the inverse of the global `-Z + // incremental-ignore-spans` option. Normally, this option is disabled, in which case + // `hash_spans` must be true. + // + // Span hashing can also be disabled without `-Z incremental-ignore-spans`. This is the + // case for instance when building a hash for name mangling. Such configuration must not be + // used for metadata. + assert_eq!( + hash_spans, !self.incremental_ignore_spans, + "Attempted hashing of {msg} with non-default HashingControls: {hashing_controls:?}" + ); + } +} + +impl<'a> rustc_abi::HashStableContext for StableHashingContext<'a> {} +impl<'a> rustc_ast::HashStableContext for StableHashingContext<'a> {} +impl<'a> rustc_hir::HashStableContext for StableHashingContext<'a> {} impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {} diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index fe6fb3d65194..5592f6553971 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -2,14 +2,18 @@ //! from various crates in no particular order. use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir::{self as hir, HashIgnoredAttrId}; -use rustc_span::SourceFile; +use rustc_span::{SourceFile, Symbol, sym}; use smallvec::SmallVec; +use {rustc_ast as ast, rustc_hir as hir}; use crate::ich::StableHashingContext; -impl<'ctx> rustc_abi::HashStableContext for StableHashingContext<'ctx> {} -impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> {} +impl<'a> HashStable> for ast::NodeId { + #[inline] + fn hash_stable(&self, _: &mut StableHashingContext<'a>, _: &mut StableHasher) { + panic!("Node IDs should not appear in incremental state"); + } +} impl<'a> HashStable> for [hir::Attribute] { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { @@ -24,7 +28,7 @@ impl<'a> HashStable> for [hir::Attribute] { .filter(|attr| { attr.is_doc_comment().is_none() // FIXME(jdonszelmann) have a better way to handle ignored attrs - && !attr.name().is_some_and(|ident| hcx.is_ignored_attr(ident)) + && !attr.name().is_some_and(|ident| is_ignored_attr(ident)) }) .collect(); @@ -35,10 +39,18 @@ impl<'a> HashStable> for [hir::Attribute] { } } -impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { - fn hash_attr_id(&mut self, _id: &HashIgnoredAttrId, _hasher: &mut StableHasher) { - /* we don't hash HashIgnoredAttrId, we ignore them */ - } +#[inline] +fn is_ignored_attr(name: Symbol) -> bool { + const IGNORED_ATTRIBUTES: &[Symbol] = &[ + sym::cfg_trace, // FIXME(#138844) should this really be ignored? + sym::rustc_if_this_changed, + sym::rustc_then_this_would_need, + sym::rustc_clean, + sym::rustc_partition_reused, + sym::rustc_partition_codegened, + sym::rustc_expected_cgu_reuse, + ]; + IGNORED_ATTRIBUTES.contains(&name) } impl<'a> HashStable> for SourceFile { diff --git a/compiler/rustc_query_system/src/ich/mod.rs b/compiler/rustc_query_system/src/ich/mod.rs index 72a7f3b8f970..99575299c978 100644 --- a/compiler/rustc_query_system/src/ich/mod.rs +++ b/compiler/rustc_query_system/src/ich/mod.rs @@ -1,18 +1,6 @@ //! ICH - Incremental Compilation Hash -use rustc_span::{Symbol, sym}; - pub use self::hcx::StableHashingContext; mod hcx; mod impls_syntax; - -pub const IGNORED_ATTRIBUTES: &[Symbol] = &[ - sym::cfg_trace, // FIXME should this really be ignored? - sym::rustc_if_this_changed, - sym::rustc_then_this_would_need, - sym::rustc_clean, - sym::rustc_partition_reused, - sym::rustc_partition_codegened, - sym::rustc_expected_cgu_reuse, -]; diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 61edf397886b..e8d857cf3178 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -1,11 +1,10 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![feature(assert_matches)] -#![feature(core_intrinsics)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(min_specialization)] +#![feature(trait_alias)] // tidy-alphabetical-end -pub mod cache; pub mod dep_graph; mod error; pub mod ich; diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index 67ad767d4d31..c1f5e5b67085 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -3,12 +3,17 @@ use std::hash::Hash; use std::sync::OnceLock; use rustc_data_structures::sharded::ShardedHashMap; +use rustc_data_structures::stable_hasher::HashStable; pub use rustc_data_structures::vec_cache::VecCache; use rustc_hir::def_id::LOCAL_CRATE; use rustc_index::Idx; use rustc_span::def_id::{DefId, DefIndex}; use crate::dep_graph::DepNodeIndex; +use crate::ich::StableHashingContext; + +/// Traits that all query keys must satisfy. +pub trait QueryCacheKey = Hash + Eq + Copy + Debug + for<'a> HashStable>; /// Trait for types that serve as an in-memory cache for query results, /// for a given key (argument) type and value (return) type. @@ -16,7 +21,7 @@ use crate::dep_graph::DepNodeIndex; /// Types implementing this trait are associated with actual key/value types /// by the `Cache` associated type of the `rustc_middle::query::Key` trait. pub trait QueryCache: Sized { - type Key: Hash + Eq + Copy + Debug; + type Key: QueryCacheKey; type Value: Copy; /// Returns the cached value (and other information) associated with the @@ -48,7 +53,7 @@ impl Default for DefaultCache { impl QueryCache for DefaultCache where - K: Eq + Hash + Copy + Debug, + K: QueryCacheKey, V: Copy, { type Key = K; @@ -175,7 +180,7 @@ where impl QueryCache for VecCache where - K: Idx + Eq + Hash + Copy + Debug, + K: Idx + QueryCacheKey, V: Copy, { type Key = K; diff --git a/compiler/rustc_query_system/src/query/dispatcher.rs b/compiler/rustc_query_system/src/query/dispatcher.rs deleted file mode 100644 index 840b09024ffa..000000000000 --- a/compiler/rustc_query_system/src/query/dispatcher.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::fmt::Debug; -use std::hash::Hash; - -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_span::ErrorGuaranteed; - -use super::QueryStackFrameExtra; -use crate::dep_graph::{DepKind, DepNode, DepNodeKey, HasDepContext, SerializedDepNodeIndex}; -use crate::ich::StableHashingContext; -use crate::query::caches::QueryCache; -use crate::query::{CycleError, CycleErrorHandling, DepNodeIndex, QueryContext, QueryState}; - -pub type HashResult = Option, &V) -> Fingerprint>; - -/// Unambiguous shorthand for `::DepContext`. -#[expect(type_alias_bounds)] -type DepContextOf<'tcx, This: QueryDispatcher<'tcx>> = - <>::Qcx as HasDepContext>::DepContext; - -/// Trait that can be used as a vtable for a single query, providing operations -/// and metadata for that query. -/// -/// Implemented by `rustc_query_impl::SemiDynamicQueryDispatcher`, which -/// mostly delegates to `rustc_middle::query::plumbing::QueryVTable`. -/// Those types are not visible from this `rustc_query_system` crate. -/// -/// "Dispatcher" should be understood as a near-synonym of "vtable". -pub trait QueryDispatcher<'tcx>: Copy + 'tcx { - fn name(self) -> &'static str; - - /// Query context used by this dispatcher, i.e. `rustc_query_impl::QueryCtxt`. - type Qcx: QueryContext<'tcx>; - - // `Key` and `Value` are `Copy` instead of `Clone` to ensure copying them stays cheap, - // but it isn't necessary. - type Key: DepNodeKey> + Eq + Hash + Copy + Debug; - type Value: Copy; - - type Cache: QueryCache; - - fn format_value(self) -> fn(&Self::Value) -> String; - - // Don't use this method to access query results, instead use the methods on TyCtxt - fn query_state(self, tcx: Self::Qcx) -> &'tcx QueryState<'tcx, Self::Key>; - - // Don't use this method to access query results, instead use the methods on TyCtxt - fn query_cache(self, tcx: Self::Qcx) -> &'tcx Self::Cache; - - fn will_cache_on_disk_for_key(self, tcx: DepContextOf<'tcx, Self>, key: &Self::Key) -> bool; - - // Don't use this method to compute query results, instead use the methods on TyCtxt - fn execute_query(self, tcx: DepContextOf<'tcx, Self>, k: Self::Key) -> Self::Value; - - fn compute(self, tcx: Self::Qcx, key: Self::Key) -> Self::Value; - - fn try_load_from_disk( - self, - tcx: Self::Qcx, - key: &Self::Key, - prev_index: SerializedDepNodeIndex, - index: DepNodeIndex, - ) -> Option; - - fn is_loadable_from_disk( - self, - qcx: Self::Qcx, - key: &Self::Key, - idx: SerializedDepNodeIndex, - ) -> bool; - - /// Synthesize an error value to let compilation continue after a cycle. - fn value_from_cycle_error( - self, - tcx: DepContextOf<'tcx, Self>, - cycle_error: &CycleError, - guar: ErrorGuaranteed, - ) -> Self::Value; - - fn anon(self) -> bool; - fn eval_always(self) -> bool; - fn depth_limit(self) -> bool; - fn feedable(self) -> bool; - - fn dep_kind(self) -> DepKind; - fn cycle_error_handling(self) -> CycleErrorHandling; - fn hash_result(self) -> HashResult; - - // Just here for convenience and checking that the key matches the kind, don't override this. - fn construct_dep_node(self, tcx: DepContextOf<'tcx, Self>, key: &Self::Key) -> DepNode { - DepNode::construct(tcx, self.dep_kind(), key) - } -} diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 50cb58f0b4d5..7349fe223ef9 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -1,20 +1,12 @@ use std::fmt::Debug; use std::hash::Hash; -use std::io::Write; -use std::iter; use std::num::NonZero; use std::sync::Arc; use parking_lot::{Condvar, Mutex}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{Diag, DiagCtxtHandle}; -use rustc_hir::def::DefKind; -use rustc_session::Session; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::Span; use super::{QueryStackDeferred, QueryStackFrameExtra}; -use crate::dep_graph::DepContext; -use crate::error::CycleStack; use crate::query::plumbing::CycleError; use crate::query::{QueryContext, QueryStackFrame}; @@ -32,40 +24,12 @@ impl<'tcx> QueryInfo> { } } -/// Map from query job IDs to job information collected by -/// [`QueryContext::collect_active_jobs_from_all_queries`]. -pub type QueryMap<'tcx> = FxHashMap>; - /// A value uniquely identifying an active query job. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct QueryJobId(pub NonZero); -impl QueryJobId { - fn frame<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> QueryStackFrame> { - map.get(&self).unwrap().frame.clone() - } - - fn span<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Span { - map.get(&self).unwrap().job.span - } - - fn parent<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Option { - map.get(&self).unwrap().job.parent - } - - fn latch<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Option<&'a QueryLatch<'tcx>> { - map.get(&self).unwrap().job.latch.as_ref() - } -} - -#[derive(Clone, Debug)] -pub struct QueryJobInfo<'tcx> { - pub frame: QueryStackFrame>, - pub job: QueryJob<'tcx>, -} - /// Represents an active query job. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct QueryJob<'tcx> { pub id: QueryJobId, @@ -76,13 +40,7 @@ pub struct QueryJob<'tcx> { pub parent: Option, /// The latch that is used to wait on this job. - latch: Option>, -} - -impl<'tcx> Clone for QueryJob<'tcx> { - fn clone(&self) -> Self { - Self { id: self.id, span: self.span, parent: self.parent, latch: self.latch.clone() } - } + pub latch: Option>, } impl<'tcx> QueryJob<'tcx> { @@ -92,7 +50,7 @@ impl<'tcx> QueryJob<'tcx> { QueryJob { id, span, parent, latch: None } } - pub(super) fn latch(&mut self) -> QueryLatch<'tcx> { + pub fn latch(&mut self) -> QueryLatch<'tcx> { if self.latch.is_none() { self.latch = Some(QueryLatch::new()); } @@ -111,91 +69,23 @@ impl<'tcx> QueryJob<'tcx> { } } -impl QueryJobId { - pub(super) fn find_cycle_in_stack<'tcx>( - &self, - query_map: QueryMap<'tcx>, - current_job: &Option, - span: Span, - ) -> CycleError> { - // Find the waitee amongst `current_job` parents - let mut cycle = Vec::new(); - let mut current_job = Option::clone(current_job); - - while let Some(job) = current_job { - let info = query_map.get(&job).unwrap(); - cycle.push(QueryInfo { span: info.job.span, frame: info.frame.clone() }); - - if job == *self { - cycle.reverse(); - - // This is the end of the cycle - // The span entry we included was for the usage - // of the cycle itself, and not part of the cycle - // Replace it with the span which caused the cycle to form - cycle[0].span = span; - // Find out why the cycle itself was used - let usage = info - .job - .parent - .as_ref() - .map(|parent| (info.job.span, parent.frame(&query_map))); - return CycleError { usage, cycle }; - } - - current_job = info.job.parent; - } - - panic!("did not find a cycle") - } - - #[cold] - #[inline(never)] - pub fn find_dep_kind_root<'tcx>( - &self, - query_map: QueryMap<'tcx>, - ) -> (QueryJobInfo<'tcx>, usize) { - let mut depth = 1; - let info = query_map.get(&self).unwrap(); - let dep_kind = info.frame.dep_kind; - let mut current_id = info.job.parent; - let mut last_layout = (info.clone(), depth); - - while let Some(id) = current_id { - let info = query_map.get(&id).unwrap(); - if info.frame.dep_kind == dep_kind { - depth += 1; - last_layout = (info.clone(), depth); - } - current_id = info.job.parent; - } - last_layout - } +#[derive(Debug)] +pub struct QueryWaiter<'tcx> { + pub query: Option, + pub condvar: Condvar, + pub span: Span, + pub cycle: Mutex>>>, } #[derive(Debug)] -struct QueryWaiter<'tcx> { - query: Option, - condvar: Condvar, - span: Span, - cycle: Mutex>>>, +pub struct QueryLatchInfo<'tcx> { + pub complete: bool, + pub waiters: Vec>>, } -#[derive(Debug)] -struct QueryLatchInfo<'tcx> { - complete: bool, - waiters: Vec>>, -} - -#[derive(Debug)] -pub(super) struct QueryLatch<'tcx> { - info: Arc>>, -} - -impl<'tcx> Clone for QueryLatch<'tcx> { - fn clone(&self) -> Self { - Self { info: Arc::clone(&self.info) } - } +#[derive(Clone, Debug)] +pub struct QueryLatch<'tcx> { + pub info: Arc>>, } impl<'tcx> QueryLatch<'tcx> { @@ -206,7 +96,7 @@ impl<'tcx> QueryLatch<'tcx> { } /// Awaits for the query job to complete. - pub(super) fn wait_on( + pub fn wait_on( &self, qcx: impl QueryContext<'tcx>, query: Option, @@ -262,399 +152,10 @@ impl<'tcx> QueryLatch<'tcx> { /// Removes a single waiter from the list of waiters. /// This is used to break query cycles. - fn extract_waiter(&self, waiter: usize) -> Arc> { + pub fn extract_waiter(&self, waiter: usize) -> Arc> { let mut info = self.info.lock(); debug_assert!(!info.complete); // Remove the waiter from the list of waiters info.waiters.remove(waiter) } } - -/// A resumable waiter of a query. The usize is the index into waiters in the query's latch -type Waiter = (QueryJobId, usize); - -/// Visits all the non-resumable and resumable waiters of a query. -/// Only waiters in a query are visited. -/// `visit` is called for every waiter and is passed a query waiting on `query_ref` -/// and a span indicating the reason the query waited on `query_ref`. -/// If `visit` returns Some, this function returns. -/// For visits of non-resumable waiters it returns the return value of `visit`. -/// For visits of resumable waiters it returns Some(Some(Waiter)) which has the -/// required information to resume the waiter. -/// If all `visit` calls returns None, this function also returns None. -fn visit_waiters<'tcx, F>( - query_map: &QueryMap<'tcx>, - query: QueryJobId, - mut visit: F, -) -> Option> -where - F: FnMut(Span, QueryJobId) -> Option>, -{ - // Visit the parent query which is a non-resumable waiter since it's on the same stack - if let Some(parent) = query.parent(query_map) - && let Some(cycle) = visit(query.span(query_map), parent) - { - return Some(cycle); - } - - // Visit the explicit waiters which use condvars and are resumable - if let Some(latch) = query.latch(query_map) { - for (i, waiter) in latch.info.lock().waiters.iter().enumerate() { - if let Some(waiter_query) = waiter.query { - if visit(waiter.span, waiter_query).is_some() { - // Return a value which indicates that this waiter can be resumed - return Some(Some((query, i))); - } - } - } - } - - None -} - -/// Look for query cycles by doing a depth first search starting at `query`. -/// `span` is the reason for the `query` to execute. This is initially DUMMY_SP. -/// If a cycle is detected, this initial value is replaced with the span causing -/// the cycle. -fn cycle_check<'tcx>( - query_map: &QueryMap<'tcx>, - query: QueryJobId, - span: Span, - stack: &mut Vec<(Span, QueryJobId)>, - visited: &mut FxHashSet, -) -> Option> { - if !visited.insert(query) { - return if let Some(p) = stack.iter().position(|q| q.1 == query) { - // We detected a query cycle, fix up the initial span and return Some - - // Remove previous stack entries - stack.drain(0..p); - // Replace the span for the first query with the cycle cause - stack[0].0 = span; - Some(None) - } else { - None - }; - } - - // Query marked as visited is added it to the stack - stack.push((span, query)); - - // Visit all the waiters - let r = visit_waiters(query_map, query, |span, successor| { - cycle_check(query_map, successor, span, stack, visited) - }); - - // Remove the entry in our stack if we didn't find a cycle - if r.is_none() { - stack.pop(); - } - - r -} - -/// Finds out if there's a path to the compiler root (aka. code which isn't in a query) -/// from `query` without going through any of the queries in `visited`. -/// This is achieved with a depth first search. -fn connected_to_root<'tcx>( - query_map: &QueryMap<'tcx>, - query: QueryJobId, - visited: &mut FxHashSet, -) -> bool { - // We already visited this or we're deliberately ignoring it - if !visited.insert(query) { - return false; - } - - // This query is connected to the root (it has no query parent), return true - if query.parent(query_map).is_none() { - return true; - } - - visit_waiters(query_map, query, |_, successor| { - connected_to_root(query_map, successor, visited).then_some(None) - }) - .is_some() -} - -// Deterministically pick an query from a list -fn pick_query<'a, 'tcx, T, F>(query_map: &QueryMap<'tcx>, queries: &'a [T], f: F) -> &'a T -where - F: Fn(&T) -> (Span, QueryJobId), -{ - // Deterministically pick an entry point - // FIXME: Sort this instead - queries - .iter() - .min_by_key(|v| { - let (span, query) = f(v); - let hash = query.frame(query_map).hash; - // Prefer entry points which have valid spans for nicer error messages - // We add an integer to the tuple ensuring that entry points - // with valid spans are picked first - let span_cmp = if span == DUMMY_SP { 1 } else { 0 }; - (span_cmp, hash) - }) - .unwrap() -} - -/// Looks for query cycles starting from the last query in `jobs`. -/// If a cycle is found, all queries in the cycle is removed from `jobs` and -/// the function return true. -/// If a cycle was not found, the starting query is removed from `jobs` and -/// the function returns false. -fn remove_cycle<'tcx>( - query_map: &QueryMap<'tcx>, - jobs: &mut Vec, - wakelist: &mut Vec>>, -) -> bool { - let mut visited = FxHashSet::default(); - let mut stack = Vec::new(); - // Look for a cycle starting with the last query in `jobs` - if let Some(waiter) = - cycle_check(query_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) - { - // The stack is a vector of pairs of spans and queries; reverse it so that - // the earlier entries require later entries - let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip(); - - // Shift the spans so that queries are matched with the span for their waitee - spans.rotate_right(1); - - // Zip them back together - let mut stack: Vec<_> = iter::zip(spans, queries).collect(); - - // Remove the queries in our cycle from the list of jobs to look at - for r in &stack { - if let Some(pos) = jobs.iter().position(|j| j == &r.1) { - jobs.remove(pos); - } - } - - // Find the queries in the cycle which are - // connected to queries outside the cycle - let entry_points = stack - .iter() - .filter_map(|&(span, query)| { - if query.parent(query_map).is_none() { - // This query is connected to the root (it has no query parent) - Some((span, query, None)) - } else { - let mut waiters = Vec::new(); - // Find all the direct waiters who lead to the root - visit_waiters(query_map, query, |span, waiter| { - // Mark all the other queries in the cycle as already visited - let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); - - if connected_to_root(query_map, waiter, &mut visited) { - waiters.push((span, waiter)); - } - - None - }); - if waiters.is_empty() { - None - } else { - // Deterministically pick one of the waiters to show to the user - let waiter = *pick_query(query_map, &waiters, |s| *s); - Some((span, query, Some(waiter))) - } - } - }) - .collect::)>>(); - - // Deterministically pick an entry point - let (_, entry_point, usage) = pick_query(query_map, &entry_points, |e| (e.0, e.1)); - - // Shift the stack so that our entry point is first - let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point); - if let Some(pos) = entry_point_pos { - stack.rotate_left(pos); - } - - let usage = usage.as_ref().map(|(span, query)| (*span, query.frame(query_map))); - - // Create the cycle error - let error = CycleError { - usage, - cycle: stack - .iter() - .map(|&(s, ref q)| QueryInfo { span: s, frame: q.frame(query_map) }) - .collect(), - }; - - // We unwrap `waiter` here since there must always be one - // edge which is resumable / waited using a query latch - let (waitee_query, waiter_idx) = waiter.unwrap(); - - // Extract the waiter we want to resume - let waiter = waitee_query.latch(query_map).unwrap().extract_waiter(waiter_idx); - - // Set the cycle error so it will be picked up when resumed - *waiter.cycle.lock() = Some(error); - - // Put the waiter on the list of things to resume - wakelist.push(waiter); - - true - } else { - false - } -} - -/// Detects query cycles by using depth first search over all active query jobs. -/// If a query cycle is found it will break the cycle by finding an edge which -/// uses a query latch and then resuming that waiter. -/// There may be multiple cycles involved in a deadlock, so this searches -/// all active queries for cycles before finally resuming all the waiters at once. -pub fn break_query_cycles<'tcx>(query_map: QueryMap<'tcx>, registry: &rustc_thread_pool::Registry) { - let mut wakelist = Vec::new(); - // It is OK per the comments: - // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932 - // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798866392 - #[allow(rustc::potential_query_instability)] - let mut jobs: Vec = query_map.keys().cloned().collect(); - - let mut found_cycle = false; - - while jobs.len() > 0 { - if remove_cycle(&query_map, &mut jobs, &mut wakelist) { - found_cycle = true; - } - } - - // Check that a cycle was found. It is possible for a deadlock to occur without - // a query cycle if a query which can be waited on uses Rayon to do multithreading - // internally. Such a query (X) may be executing on 2 threads (A and B) and A may - // wait using Rayon on B. Rayon may then switch to executing another query (Y) - // which in turn will wait on X causing a deadlock. We have a false dependency from - // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here - // only considers the true dependency and won't detect a cycle. - if !found_cycle { - panic!( - "deadlock detected as we're unable to find a query cycle to break\n\ - current query map:\n{:#?}", - query_map - ); - } - - // Mark all the thread we're about to wake up as unblocked. This needs to be done before - // we wake the threads up as otherwise Rayon could detect a deadlock if a thread we - // resumed fell asleep and this thread had yet to mark the remaining threads as unblocked. - for _ in 0..wakelist.len() { - rustc_thread_pool::mark_unblocked(registry); - } - - for waiter in wakelist.into_iter() { - waiter.condvar.notify_one(); - } -} - -#[inline(never)] -#[cold] -pub fn report_cycle<'a>( - sess: &'a Session, - CycleError { usage, cycle: stack }: &CycleError, -) -> Diag<'a> { - assert!(!stack.is_empty()); - - let span = stack[0].frame.info.default_span(stack[1 % stack.len()].span); - - let mut cycle_stack = Vec::new(); - - use crate::error::StackCount; - let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple }; - - for i in 1..stack.len() { - let frame = &stack[i].frame; - let span = frame.info.default_span(stack[(i + 1) % stack.len()].span); - cycle_stack.push(CycleStack { span, desc: frame.info.description.to_owned() }); - } - - let mut cycle_usage = None; - if let Some((span, ref query)) = *usage { - cycle_usage = Some(crate::error::CycleUsage { - span: query.info.default_span(span), - usage: query.info.description.to_string(), - }); - } - - let alias = - if stack.iter().all(|entry| matches!(entry.frame.info.def_kind, Some(DefKind::TyAlias))) { - Some(crate::error::Alias::Ty) - } else if stack.iter().all(|entry| entry.frame.info.def_kind == Some(DefKind::TraitAlias)) { - Some(crate::error::Alias::Trait) - } else { - None - }; - - let cycle_diag = crate::error::Cycle { - span, - cycle_stack, - stack_bottom: stack[0].frame.info.description.to_owned(), - alias, - cycle_usage, - stack_count, - note_span: (), - }; - - sess.dcx().create_err(cycle_diag) -} - -pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>( - qcx: Qcx, - mut current_query: Option, - dcx: DiagCtxtHandle<'_>, - limit_frames: Option, - mut file: Option, -) -> usize { - // Be careful relying on global state here: this code is called from - // a panic hook, which means that the global `DiagCtxt` may be in a weird - // state if it was responsible for triggering the panic. - let mut count_printed = 0; - let mut count_total = 0; - - // Make use of a partial query map if we fail to take locks collecting active queries. - let query_map = match qcx.collect_active_jobs_from_all_queries(false) { - Ok(query_map) => query_map, - Err(query_map) => query_map, - }; - - if let Some(ref mut file) = file { - let _ = writeln!(file, "\n\nquery stack during panic:"); - } - while let Some(query) = current_query { - let Some(query_info) = query_map.get(&query) else { - break; - }; - let query_extra = query_info.frame.info.extract(); - if Some(count_printed) < limit_frames || limit_frames.is_none() { - // Only print to stderr as many stack frames as `num_frames` when present. - dcx.struct_failure_note(format!( - "#{} [{:?}] {}", - count_printed, query_info.frame.dep_kind, query_extra.description - )) - .with_span(query_info.job.span) - .emit(); - count_printed += 1; - } - - if let Some(ref mut file) = file { - let _ = writeln!( - file, - "#{} [{}] {}", - count_total, - qcx.dep_context().dep_kind_vtable(query_info.frame.dep_kind).name, - query_extra.description - ); - } - - current_query = query_info.job.parent; - count_total += 1; - } - - if let Some(ref mut file) = file { - let _ = writeln!(file, "end of query stack"); - } - count_total -} diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index dbf7395bd61a..c33af941f802 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -12,17 +12,14 @@ use rustc_macros::{Decodable, Encodable}; use rustc_span::Span; use rustc_span::def_id::DefId; -pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache}; -pub use self::dispatcher::{HashResult, QueryDispatcher}; -pub use self::job::{ - QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap, break_query_cycles, print_query_stack, - report_cycle, +pub use self::caches::{ + DefIdCache, DefaultCache, QueryCache, QueryCacheKey, SingleCache, VecCache, }; +pub use self::job::{QueryInfo, QueryJob, QueryJobId, QueryLatch, QueryWaiter}; pub use self::plumbing::*; use crate::dep_graph::{DepKind, DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; mod caches; -mod dispatcher; mod job; mod plumbing; @@ -52,7 +49,7 @@ pub struct QueryStackFrame { pub dep_kind: DepKind, /// This hash is used to deterministically pick /// a query to remove cycles in the parallel compiler. - hash: Hash64, + pub hash: Hash64, pub def_id: Option, /// A def-id that is extracted from a `Ty` in a query key pub def_id_for_ty_in_cycle: Option, @@ -84,7 +81,7 @@ impl<'tcx> QueryStackFrame> { #[derive(Clone, Debug)] pub struct QueryStackFrameExtra { pub description: String, - span: Option, + pub span: Option, pub def_kind: Option, } @@ -161,16 +158,6 @@ pub trait QueryContext<'tcx>: HasDepContext { /// a token while waiting on a query. fn jobserver_proxy(&self) -> &Proxy; - fn next_job_id(self) -> QueryJobId; - - /// Get the query information from the TLS context. - fn current_query_job(self) -> Option; - - fn collect_active_jobs_from_all_queries( - self, - require_complete: bool, - ) -> Result, QueryMap<'tcx>>; - /// Load a side effect associated to the node in the previous session. fn load_side_effect( self, @@ -179,9 +166,4 @@ pub trait QueryContext<'tcx>: HasDepContext { /// Register a side effect for the given node, for use in next session. fn store_side_effect(self, dep_node_index: DepNodeIndex, side_effect: QuerySideEffect); - - /// Executes a job by changing the `ImplicitCtxt` to point to the - /// new query job while it executes. - fn start_query(self, token: QueryJobId, depth_limit: bool, compute: impl FnOnce() -> R) - -> R; } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 32c4f6e1c847..d7a24d613583 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -1,36 +1,17 @@ -//! The implementation of the query system itself. This defines the macros that -//! generate the actual methods on tcx which find and execute the provider, -//! manage the caches, and so forth. - use std::cell::Cell; use std::fmt::Debug; -use std::hash::Hash; -use std::mem; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::hash_table::{self, Entry, HashTable}; -use rustc_data_structures::sharded::{self, Sharded}; -use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::{outline, sync}; -use rustc_errors::{Diag, FatalError, StashKey}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_data_structures::hash_table::HashTable; +use rustc_data_structures::sharded::Sharded; +use rustc_span::Span; use tracing::instrument; -use super::{QueryDispatcher, QueryStackDeferred, QueryStackFrameExtra}; -use crate::dep_graph::{ - DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeKey, HasDepContext, -}; +use super::{QueryStackDeferred, QueryStackFrameExtra}; +use crate::dep_graph::{DepContext, DepGraphData}; use crate::ich::StableHashingContext; -use crate::query::caches::QueryCache; -use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, report_cycle}; -use crate::query::{ - CycleErrorHandling, QueryContext, QueryMap, QueryStackFrame, SerializedDepNodeIndex, -}; - -#[inline] -fn equivalent_key(k: &K) -> impl Fn(&(K, V)) -> bool + '_ { - move |x| x.0 == *k -} +use crate::query::job::{QueryInfo, QueryJob}; +use crate::query::{QueryStackFrame, SerializedDepNodeIndex}; /// For a particular query, keeps track of "active" keys, i.e. keys whose /// evaluation has started but has not yet finished successfully. @@ -38,7 +19,7 @@ fn equivalent_key(k: &K) -> impl Fn(&(K, V)) -> bool + '_ { /// (Successful query evaluation for a key is represented by an entry in the /// query's in-memory cache.) pub struct QueryState<'tcx, K> { - active: Sharded)>>, + pub active: Sharded)>>, } /// For a particular query and key, tracks the status of a query evaluation @@ -46,7 +27,7 @@ pub struct QueryState<'tcx, K> { /// /// (Successful query evaluation for a key is represented by an entry in the /// query's in-memory cache.) -enum ActiveKeyStatus<'tcx> { +pub enum ActiveKeyStatus<'tcx> { /// Some thread is already evaluating the query for this key. /// /// The enclosed [`QueryJob`] can be used to wait for it to finish. @@ -57,199 +38,12 @@ enum ActiveKeyStatus<'tcx> { Poisoned, } -impl<'tcx> ActiveKeyStatus<'tcx> { - /// Obtains the enclosed [`QueryJob`], or panics if this query evaluation - /// was poisoned by a panic. - fn expect_job(self) -> QueryJob<'tcx> { - match self { - Self::Started(job) => job, - Self::Poisoned => { - panic!("job for query failed to start and was poisoned") - } - } - } -} - -impl<'tcx, K> QueryState<'tcx, K> -where - K: Eq + Hash + Copy + Debug, -{ - pub fn all_inactive(&self) -> bool { - self.active.lock_shards().all(|shard| shard.is_empty()) - } - - /// Internal plumbing for collecting the set of active jobs for this query. - /// - /// Should only be called from `gather_active_jobs`. - pub fn gather_active_jobs_inner( - &self, - qcx: Qcx, - make_frame: fn(Qcx, K) -> QueryStackFrame>, - jobs: &mut QueryMap<'tcx>, - require_complete: bool, - ) -> Option<()> { - let mut active = Vec::new(); - - // Helper to gather active jobs from a single shard. - let mut gather_shard_jobs = |shard: &HashTable<(K, ActiveKeyStatus<'tcx>)>| { - for (k, v) in shard.iter() { - if let ActiveKeyStatus::Started(ref job) = *v { - active.push((*k, job.clone())); - } - } - }; - - // Lock shards and gather jobs from each shard. - if require_complete { - for shard in self.active.lock_shards() { - gather_shard_jobs(&shard); - } - } else { - // We use try_lock_shards here since we are called from the - // deadlock handler, and this shouldn't be locked. - for shard in self.active.try_lock_shards() { - let shard = shard?; - gather_shard_jobs(&shard); - } - } - - // Call `make_frame` while we're not holding a `self.active` lock as `make_frame` may call - // queries leading to a deadlock. - for (key, job) in active { - let frame = make_frame(qcx, key); - jobs.insert(job.id, QueryJobInfo { frame, job }); - } - - Some(()) - } -} - impl<'tcx, K> Default for QueryState<'tcx, K> { fn default() -> QueryState<'tcx, K> { QueryState { active: Default::default() } } } -/// A type representing the responsibility to execute the job in the `job` field. -/// This will poison the relevant query if dropped. -struct JobOwner<'tcx, K> -where - K: Eq + Hash + Copy, -{ - state: &'tcx QueryState<'tcx, K>, - key: K, -} - -#[cold] -#[inline(never)] -fn mk_cycle<'tcx, Q>(query: Q, qcx: Q::Qcx, cycle_error: CycleError) -> Q::Value -where - Q: QueryDispatcher<'tcx>, -{ - let error = report_cycle(qcx.dep_context().sess(), &cycle_error); - handle_cycle_error(query, qcx, &cycle_error, error) -} - -fn handle_cycle_error<'tcx, Q>( - query: Q, - qcx: Q::Qcx, - cycle_error: &CycleError, - error: Diag<'_>, -) -> Q::Value -where - Q: QueryDispatcher<'tcx>, -{ - match query.cycle_error_handling() { - CycleErrorHandling::Error => { - let guar = error.emit(); - query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar) - } - CycleErrorHandling::Fatal => { - error.emit(); - qcx.dep_context().sess().dcx().abort_if_errors(); - unreachable!() - } - CycleErrorHandling::DelayBug => { - let guar = error.delay_as_bug(); - query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar) - } - CycleErrorHandling::Stash => { - let guar = if let Some(root) = cycle_error.cycle.first() - && let Some(span) = root.frame.info.span - { - error.stash(span, StashKey::Cycle).unwrap() - } else { - error.emit() - }; - query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar) - } - } -} - -impl<'tcx, K> JobOwner<'tcx, K> -where - K: Eq + Hash + Copy, -{ - /// Completes the query by updating the query cache with the `result`, - /// signals the waiter and forgets the JobOwner, so it won't poison the query - fn complete(self, cache: &C, key_hash: u64, result: C::Value, dep_node_index: DepNodeIndex) - where - C: QueryCache, - { - let key = self.key; - let state = self.state; - - // Forget ourself so our destructor won't poison the query - mem::forget(self); - - // Mark as complete before we remove the job from the active state - // so no other thread can re-execute this query. - cache.complete(key, result, dep_node_index); - - let job = { - // don't keep the lock during the `unwrap()` of the retrieved value, or we taint the - // underlying shard. - // since unwinding also wants to look at this map, this can also prevent a double - // panic. - let mut shard = state.active.lock_shard_by_hash(key_hash); - match shard.find_entry(key_hash, equivalent_key(&key)) { - Err(_) => None, - Ok(occupied) => Some(occupied.remove().0.1), - } - }; - let job = job.expect("active query job entry").expect_job(); - - job.signal_complete(); - } -} - -impl<'tcx, K> Drop for JobOwner<'tcx, K> -where - K: Eq + Hash + Copy, -{ - #[inline(never)] - #[cold] - fn drop(&mut self) { - // Poison the query so jobs waiting on it panic. - let state = self.state; - let job = { - let key_hash = sharded::make_hash(&self.key); - let mut shard = state.active.lock_shard_by_hash(key_hash); - match shard.find_entry(key_hash, equivalent_key(&self.key)) { - Err(_) => panic!(), - Ok(occupied) => { - let ((key, value), vacant) = occupied.remove(); - vacant.insert((key, ActiveKeyStatus::Poisoned)); - value.expect_job() - } - } - }; - // Also signal the completion of the job, so waiters - // will continue execution. - job.signal_complete(); - } -} - #[derive(Clone, Debug)] pub struct CycleError { /// The query and related span that uses the cycle. @@ -258,7 +52,7 @@ pub struct CycleError { } impl<'tcx> CycleError> { - fn lift(&self) -> CycleError { + pub fn lift(&self) -> CycleError { CycleError { usage: self.usage.as_ref().map(|(span, frame)| (*span, frame.lift())), cycle: self.cycle.iter().map(|info| info.lift()).collect(), @@ -266,427 +60,9 @@ impl<'tcx> CycleError> { } } -/// Checks whether there is already a value for this key in the in-memory -/// query cache, returning that value if present. -/// -/// (Also performs some associated bookkeeping, if a value was found.) -#[inline(always)] -pub fn try_get_cached(tcx: Tcx, cache: &C, key: &C::Key) -> Option -where - C: QueryCache, - Tcx: DepContext, -{ - match cache.lookup(key) { - Some((value, index)) => { - tcx.profiler().query_cache_hit(index.into()); - tcx.dep_graph().read_index(index); - Some(value) - } - None => None, - } -} - -#[cold] -#[inline(never)] -fn cycle_error<'tcx, Q>( - query: Q, - qcx: Q::Qcx, - try_execute: QueryJobId, - span: Span, -) -> (Q::Value, Option) -where - Q: QueryDispatcher<'tcx>, -{ - // Ensure there was no errors collecting all active jobs. - // We need the complete map to ensure we find a cycle to break. - let query_map = qcx - .collect_active_jobs_from_all_queries(false) - .ok() - .expect("failed to collect active queries"); - - let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span); - (mk_cycle(query, qcx, error.lift()), None) -} - -#[inline(always)] -fn wait_for_query<'tcx, Q>( - query: Q, - qcx: Q::Qcx, - span: Span, - key: Q::Key, - latch: QueryLatch<'tcx>, - current: Option, -) -> (Q::Value, Option) -where - Q: QueryDispatcher<'tcx>, -{ - // For parallel queries, we'll block and wait until the query running - // in another thread has completed. Record how long we wait in the - // self-profiler. - let query_blocked_prof_timer = qcx.dep_context().profiler().query_blocked(); - - // With parallel queries we might just have to wait on some other - // thread. - let result = latch.wait_on(qcx, current, span); - - match result { - Ok(()) => { - let Some((v, index)) = query.query_cache(qcx).lookup(&key) else { - outline(|| { - // We didn't find the query result in the query cache. Check if it was - // poisoned due to a panic instead. - let key_hash = sharded::make_hash(&key); - let shard = query.query_state(qcx).active.lock_shard_by_hash(key_hash); - match shard.find(key_hash, equivalent_key(&key)) { - // The query we waited on panicked. Continue unwinding here. - Some((_, ActiveKeyStatus::Poisoned)) => FatalError.raise(), - _ => panic!( - "query '{}' result must be in the cache or the query must be poisoned after a wait", - query.name() - ), - } - }) - }; - - qcx.dep_context().profiler().query_cache_hit(index.into()); - query_blocked_prof_timer.finish_with_query_invocation_id(index.into()); - - (v, Some(index)) - } - Err(cycle) => (mk_cycle(query, qcx, cycle.lift()), None), - } -} - -#[inline(never)] -fn try_execute_query<'tcx, Q, const INCR: bool>( - query: Q, - qcx: Q::Qcx, - span: Span, - key: Q::Key, - dep_node: Option, -) -> (Q::Value, Option) -where - Q: QueryDispatcher<'tcx>, -{ - let state = query.query_state(qcx); - let key_hash = sharded::make_hash(&key); - let mut state_lock = state.active.lock_shard_by_hash(key_hash); - - // For the parallel compiler we need to check both the query cache and query state structures - // while holding the state lock to ensure that 1) the query has not yet completed and 2) the - // query is not still executing. Without checking the query cache here, we can end up - // re-executing the query since `try_start` only checks that the query is not currently - // executing, but another thread may have already completed the query and stores it result - // in the query cache. - if qcx.dep_context().sess().threads() > 1 { - if let Some((value, index)) = query.query_cache(qcx).lookup(&key) { - qcx.dep_context().profiler().query_cache_hit(index.into()); - return (value, Some(index)); - } - } - - let current_job_id = qcx.current_query_job(); - - match state_lock.entry(key_hash, equivalent_key(&key), |(k, _)| sharded::make_hash(k)) { - Entry::Vacant(entry) => { - // Nothing has computed or is computing the query, so we start a new job and insert it in the - // state map. - let id = qcx.next_job_id(); - let job = QueryJob::new(id, span, current_job_id); - entry.insert((key, ActiveKeyStatus::Started(job))); - - // Drop the lock before we start executing the query - drop(state_lock); - - execute_job::(query, qcx, state, key, key_hash, id, dep_node) - } - Entry::Occupied(mut entry) => { - match &mut entry.get_mut().1 { - ActiveKeyStatus::Started(job) => { - if sync::is_dyn_thread_safe() { - // Get the latch out - let latch = job.latch(); - drop(state_lock); - - // Only call `wait_for_query` if we're using a Rayon thread pool - // as it will attempt to mark the worker thread as blocked. - return wait_for_query(query, qcx, span, key, latch, current_job_id); - } - - let id = job.id; - drop(state_lock); - - // If we are single-threaded we know that we have cycle error, - // so we just return the error. - cycle_error(query, qcx, id, span) - } - ActiveKeyStatus::Poisoned => FatalError.raise(), - } - } - } -} - -#[inline(always)] -fn execute_job<'tcx, Q, const INCR: bool>( - query: Q, - qcx: Q::Qcx, - state: &'tcx QueryState<'tcx, Q::Key>, - key: Q::Key, - key_hash: u64, - id: QueryJobId, - dep_node: Option, -) -> (Q::Value, Option) -where - Q: QueryDispatcher<'tcx>, -{ - // Use `JobOwner` so the query will be poisoned if executing it panics. - let job_owner = JobOwner { state, key }; - - debug_assert_eq!(qcx.dep_context().dep_graph().is_fully_enabled(), INCR); - - let (result, dep_node_index) = if INCR { - execute_job_incr( - query, - qcx, - qcx.dep_context().dep_graph().data().unwrap(), - key, - dep_node, - id, - ) - } else { - execute_job_non_incr(query, qcx, key, id) - }; - - let cache = query.query_cache(qcx); - if query.feedable() { - // We should not compute queries that also got a value via feeding. - // This can't happen, as query feeding adds the very dependencies to the fed query - // as its feeding query had. So if the fed query is red, so is its feeder, which will - // get evaluated first, and re-feed the query. - if let Some((cached_result, _)) = cache.lookup(&key) { - let Some(hasher) = query.hash_result() else { - panic!( - "no_hash fed query later has its value computed.\n\ - Remove `no_hash` modifier to allow recomputation.\n\ - The already cached value: {}", - (query.format_value())(&cached_result) - ); - }; - - let (old_hash, new_hash) = qcx.dep_context().with_stable_hashing_context(|mut hcx| { - (hasher(&mut hcx, &cached_result), hasher(&mut hcx, &result)) - }); - let formatter = query.format_value(); - if old_hash != new_hash { - // We have an inconsistency. This can happen if one of the two - // results is tainted by errors. - assert!( - qcx.dep_context().sess().dcx().has_errors().is_some(), - "Computed query value for {:?}({:?}) is inconsistent with fed value,\n\ - computed={:#?}\nfed={:#?}", - query.dep_kind(), - key, - formatter(&result), - formatter(&cached_result), - ); - } - } - } - job_owner.complete(cache, key_hash, result, dep_node_index); - - (result, Some(dep_node_index)) -} - -// Fast path for when incr. comp. is off. -#[inline(always)] -fn execute_job_non_incr<'tcx, Q>( - query: Q, - qcx: Q::Qcx, - key: Q::Key, - job_id: QueryJobId, -) -> (Q::Value, DepNodeIndex) -where - Q: QueryDispatcher<'tcx>, -{ - debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled()); - - // Fingerprint the key, just to assert that it doesn't - // have anything we don't consider hashable - if cfg!(debug_assertions) { - let _ = key.to_fingerprint(*qcx.dep_context()); - } - - let prof_timer = qcx.dep_context().profiler().query_provider(); - let result = qcx.start_query(job_id, query.depth_limit(), || query.compute(qcx, key)); - let dep_node_index = qcx.dep_context().dep_graph().next_virtual_depnode_index(); - prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - - // Similarly, fingerprint the result to assert that - // it doesn't have anything not considered hashable. - if cfg!(debug_assertions) - && let Some(hash_result) = query.hash_result() - { - qcx.dep_context().with_stable_hashing_context(|mut hcx| { - hash_result(&mut hcx, &result); - }); - } - - (result, dep_node_index) -} - -#[inline(always)] -fn execute_job_incr<'tcx, Q>( - query: Q, - qcx: Q::Qcx, - dep_graph_data: &DepGraphData<::Deps>, - key: Q::Key, - mut dep_node_opt: Option, - job_id: QueryJobId, -) -> (Q::Value, DepNodeIndex) -where - Q: QueryDispatcher<'tcx>, -{ - if !query.anon() && !query.eval_always() { - // `to_dep_node` is expensive for some `DepKind`s. - let dep_node = - dep_node_opt.get_or_insert_with(|| query.construct_dep_node(*qcx.dep_context(), &key)); - - // The diagnostics for this query will be promoted to the current session during - // `try_mark_green()`, so we can ignore them here. - if let Some(ret) = qcx.start_query(job_id, false, || { - try_load_from_disk_and_cache_in_memory(query, dep_graph_data, qcx, &key, dep_node) - }) { - return ret; - } - } - - let prof_timer = qcx.dep_context().profiler().query_provider(); - - let (result, dep_node_index) = qcx.start_query(job_id, query.depth_limit(), || { - if query.anon() { - return dep_graph_data.with_anon_task_inner( - *qcx.dep_context(), - query.dep_kind(), - || query.compute(qcx, key), - ); - } - - // `to_dep_node` is expensive for some `DepKind`s. - let dep_node = - dep_node_opt.unwrap_or_else(|| query.construct_dep_node(*qcx.dep_context(), &key)); - - dep_graph_data.with_task( - dep_node, - (qcx, query), - key, - |(qcx, query), key| query.compute(qcx, key), - query.hash_result(), - ) - }); - - prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - - (result, dep_node_index) -} - -#[inline(always)] -fn try_load_from_disk_and_cache_in_memory<'tcx, Q>( - query: Q, - dep_graph_data: &DepGraphData<::Deps>, - qcx: Q::Qcx, - key: &Q::Key, - dep_node: &DepNode, -) -> Option<(Q::Value, DepNodeIndex)> -where - Q: QueryDispatcher<'tcx>, -{ - // Note this function can be called concurrently from the same query - // We must ensure that this is handled correctly. - - let (prev_dep_node_index, dep_node_index) = dep_graph_data.try_mark_green(qcx, dep_node)?; - - debug_assert!(dep_graph_data.is_index_green(prev_dep_node_index)); - - // First we try to load the result from the on-disk cache. - // Some things are never cached on disk. - if let Some(result) = query.try_load_from_disk(qcx, key, prev_dep_node_index, dep_node_index) { - if std::intrinsics::unlikely(qcx.dep_context().sess().opts.unstable_opts.query_dep_graph) { - dep_graph_data.mark_debug_loaded_from_disk(*dep_node) - } - - let prev_fingerprint = dep_graph_data.prev_fingerprint_of(prev_dep_node_index); - // If `-Zincremental-verify-ich` is specified, re-hash results from - // the cache and make sure that they have the expected fingerprint. - // - // If not, we still seek to verify a subset of fingerprints loaded - // from disk. Re-hashing results is fairly expensive, so we can't - // currently afford to verify every hash. This subset should still - // give us some coverage of potential bugs though. - let try_verify = prev_fingerprint.split().1.as_u64().is_multiple_of(32); - if std::intrinsics::unlikely( - try_verify || qcx.dep_context().sess().opts.unstable_opts.incremental_verify_ich, - ) { - incremental_verify_ich( - *qcx.dep_context(), - dep_graph_data, - &result, - prev_dep_node_index, - query.hash_result(), - query.format_value(), - ); - } - - return Some((result, dep_node_index)); - } - - // We always expect to find a cached result for things that - // can be forced from `DepNode`. - debug_assert!( - !query.will_cache_on_disk_for_key(*qcx.dep_context(), key) - || !qcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(), - "missing on-disk cache entry for {dep_node:?}" - ); - - // Sanity check for the logic in `ensure`: if the node is green and the result loadable, - // we should actually be able to load it. - debug_assert!( - !query.is_loadable_from_disk(qcx, key, prev_dep_node_index), - "missing on-disk cache entry for loadable {dep_node:?}" - ); - - // We could not load a result from the on-disk cache, so - // recompute. - let prof_timer = qcx.dep_context().profiler().query_provider(); - - // The dep-graph for this computation is already in-place. - let result = qcx.dep_context().dep_graph().with_ignore(|| query.compute(qcx, *key)); - - prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - - // Verify that re-running the query produced a result with the expected hash - // This catches bugs in query implementations, turning them into ICEs. - // For example, a query might sort its result by `DefId` - since `DefId`s are - // not stable across compilation sessions, the result could get up getting sorted - // in a different order when the query is re-run, even though all of the inputs - // (e.g. `DefPathHash` values) were green. - // - // See issue #82920 for an example of a miscompilation that would get turned into - // an ICE by this check - incremental_verify_ich( - *qcx.dep_context(), - dep_graph_data, - &result, - prev_dep_node_index, - query.hash_result(), - query.format_value(), - ); - - Some((result, dep_node_index)) -} - #[inline] #[instrument(skip(tcx, dep_graph_data, result, hash_result, format_value), level = "debug")] -pub(crate) fn incremental_verify_ich( +pub fn incremental_verify_ich( tcx: Tcx, dep_graph_data: &DepGraphData, result: &V, @@ -767,121 +143,8 @@ fn incremental_verify_ich_failed( INSIDE_VERIFY_PANIC.set(old_in_panic); } -/// Ensure that either this query has all green inputs or been executed. -/// Executing `query::ensure(D)` is considered a read of the dep-node `D`. -/// Returns true if the query should still run. -/// -/// This function is particularly useful when executing passes for their -/// side-effects -- e.g., in order to report errors for erroneous programs. -/// -/// Note: The optimization is only available during incr. comp. -#[inline(never)] -fn ensure_must_run<'tcx, Q>( - query: Q, - qcx: Q::Qcx, - key: &Q::Key, - check_cache: bool, -) -> (bool, Option) -where - Q: QueryDispatcher<'tcx>, -{ - if query.eval_always() { - return (true, None); - } - - // Ensuring an anonymous query makes no sense - assert!(!query.anon()); - - let dep_node = query.construct_dep_node(*qcx.dep_context(), key); - - let dep_graph = qcx.dep_context().dep_graph(); - let serialized_dep_node_index = match dep_graph.try_mark_green(qcx, &dep_node) { - None => { - // A None return from `try_mark_green` means that this is either - // a new dep node or that the dep node has already been marked red. - // Either way, we can't call `dep_graph.read()` as we don't have the - // DepNodeIndex. We must invoke the query itself. The performance cost - // this introduces should be negligible as we'll immediately hit the - // in-memory cache, or another query down the line will. - return (true, Some(dep_node)); - } - Some((serialized_dep_node_index, dep_node_index)) => { - dep_graph.read_index(dep_node_index); - qcx.dep_context().profiler().query_cache_hit(dep_node_index.into()); - serialized_dep_node_index - } - }; - - // We do not need the value at all, so do not check the cache. - if !check_cache { - return (false, None); - } - - let loadable = query.is_loadable_from_disk(qcx, key, serialized_dep_node_index); - (!loadable, Some(dep_node)) -} - #[derive(Debug)] pub enum QueryMode { Get, Ensure { check_cache: bool }, } - -#[inline(always)] -pub fn get_query_non_incr<'tcx, Q>(query: Q, qcx: Q::Qcx, span: Span, key: Q::Key) -> Q::Value -where - Q: QueryDispatcher<'tcx>, -{ - debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled()); - - ensure_sufficient_stack(|| try_execute_query::(query, qcx, span, key, None).0) -} - -#[inline(always)] -pub fn get_query_incr<'tcx, Q>( - query: Q, - qcx: Q::Qcx, - span: Span, - key: Q::Key, - mode: QueryMode, -) -> Option -where - Q: QueryDispatcher<'tcx>, -{ - debug_assert!(qcx.dep_context().dep_graph().is_fully_enabled()); - - let dep_node = if let QueryMode::Ensure { check_cache } = mode { - let (must_run, dep_node) = ensure_must_run(query, qcx, &key, check_cache); - if !must_run { - return None; - } - dep_node - } else { - None - }; - - let (result, dep_node_index) = - ensure_sufficient_stack(|| try_execute_query::(query, qcx, span, key, dep_node)); - if let Some(dep_node_index) = dep_node_index { - qcx.dep_context().dep_graph().read_index(dep_node_index) - } - Some(result) -} - -pub fn force_query<'tcx, Q>(query: Q, qcx: Q::Qcx, key: Q::Key, dep_node: DepNode) -where - Q: QueryDispatcher<'tcx>, -{ - // We may be concurrently trying both execute and force a query. - // Ensure that only one of them runs the query. - if let Some((_, index)) = query.query_cache(qcx).lookup(&key) { - qcx.dep_context().profiler().query_cache_hit(index.into()); - return; - } - - debug_assert!(!query.anon()); - - ensure_sufficient_stack(|| { - try_execute_query::(query, qcx, DUMMY_SP, key, Some(dep_node)) - }); -} diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index 01e268d911d2..feb0a93d0788 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -21,7 +21,6 @@ rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } -rustc_query_system = { path = "../rustc_query_system" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index f09b98715799..9a88f74b5b17 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1015,11 +1015,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { span, name, param_kind: is_type, - help: self - .tcx - .sess - .is_nightly_build() - .then_some(errs::ParamInNonTrivialAnonConstHelp), + help: self.tcx.sess.is_nightly_build(), }) } ResolutionError::ParamInEnumDiscriminant { name, param_kind: is_type } => self diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 7afdbf6a469f..28c5a128ea36 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -429,14 +429,10 @@ pub(crate) struct ParamInNonTrivialAnonConst { pub(crate) name: Symbol, #[subdiagnostic] pub(crate) param_kind: ParamKindInNonTrivialAnonConst, - #[subdiagnostic] - pub(crate) help: Option, + #[help("add `#![feature(generic_const_exprs)]` to allow generic const expressions")] + pub(crate) help: bool, } -#[derive(Subdiagnostic)] -#[help("add `#![feature(generic_const_exprs)]` to allow generic const expressions")] -pub(crate) struct ParamInNonTrivialAnonConstHelp; - #[derive(Debug)] #[derive(Subdiagnostic)] pub(crate) enum ParamKindInNonTrivialAnonConst { diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 78ad139cff79..4e7622d08462 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -406,8 +406,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } else if !old_glob_decl.vis().is_at_least(glob_decl.vis(), self.tcx) { // We are glob-importing the same item but with greater visibility. - old_glob_decl.vis.set_unchecked(glob_decl.vis()); - old_glob_decl + // FIXME: Update visibility in place, but without regressions + // (#152004, #151124, #152347). + glob_decl } else if glob_decl.is_ambiguity_recursive() && !old_glob_decl.is_ambiguity_recursive() { // Overwriting a non-ambiguous glob import with an ambiguous glob import. old_glob_decl.ambiguity.set_unchecked(Some(glob_decl)); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4faf7715de6e..fe6346f6c6e9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2847,11 +2847,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ident, generics, ty, - rhs, + rhs_kind, define_opaque, defaultness: _, }) => { - let is_type_const = attr::contains_name(&item.attrs, sym::type_const); self.with_generic_param_rib( &generics.params, RibKind::Item( @@ -2871,7 +2870,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { this.with_lifetime_rib( LifetimeRibKind::Elided(LifetimeRes::Static), |this| { - if is_type_const + if rhs_kind.is_type_const() && !this.r.tcx.features().generic_const_parameter_types() { this.with_rib(TypeNS, RibKind::ConstParamTy, |this| { @@ -2888,12 +2887,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }, ); - if let Some(rhs) = rhs { - this.resolve_const_item_rhs( - rhs, - Some((*ident, ConstantItemKind::Const)), - ); - } + this.resolve_const_item_rhs( + rhs_kind, + Some((*ident, ConstantItemKind::Const)), + ); }, ); self.resolve_define_opaques(define_opaque); @@ -3242,11 +3239,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { AssocItemKind::Const(box ast::ConstItem { generics, ty, - rhs, + rhs_kind, define_opaque, .. }) => { - let is_type_const = attr::contains_name(&item.attrs, sym::type_const); self.with_generic_param_rib( &generics.params, RibKind::AssocItem, @@ -3261,7 +3257,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }, |this| { this.visit_generics(generics); - if is_type_const + if rhs_kind.is_type_const() && !this.r.tcx.features().generic_const_parameter_types() { this.with_rib(TypeNS, RibKind::ConstParamTy, |this| { @@ -3278,14 +3274,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // Only impose the restrictions of `ConstRibKind` for an // actual constant expression in a provided default. - if let Some(rhs) = rhs { - // We allow arbitrary const expressions inside of associated consts, - // even if they are potentially not const evaluatable. - // - // Type parameters can already be used and as associated consts are - // not used as part of the type system, this is far less surprising. - this.resolve_const_item_rhs(rhs, None); - } + // + // We allow arbitrary const expressions inside of associated consts, + // even if they are potentially not const evaluatable. + // + // Type parameters can already be used and as associated consts are + // not used as part of the type system, this is far less surprising. + this.resolve_const_item_rhs(rhs_kind, None); }, ) }, @@ -3463,12 +3458,11 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ident, generics, ty, - rhs, + rhs_kind, define_opaque, .. }) => { debug!("resolve_implementation AssocItemKind::Const"); - let is_type_const = attr::contains_name(&item.attrs, sym::type_const); self.with_generic_param_rib( &generics.params, RibKind::AssocItem, @@ -3505,7 +3499,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ); this.visit_generics(generics); - if is_type_const + if rhs_kind.is_type_const() && !this .r .tcx @@ -3527,14 +3521,12 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } else { this.visit_ty(ty); } - if let Some(rhs) = rhs { - // We allow arbitrary const expressions inside of associated consts, - // even if they are potentially not const evaluatable. - // - // Type parameters can already be used and as associated consts are - // not used as part of the type system, this is far less surprising. - this.resolve_const_item_rhs(rhs, None); - } + // We allow arbitrary const expressions inside of associated consts, + // even if they are potentially not const evaluatable. + // + // Type parameters can already be used and as associated consts are + // not used as part of the type system, this is far less surprising. + this.resolve_const_item_rhs(rhs_kind, None); }, ) }, @@ -3756,18 +3748,19 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn resolve_const_item_rhs( &mut self, - rhs: &'ast ConstItemRhs, + rhs_kind: &'ast ConstItemRhsKind, item: Option<(Ident, ConstantItemKind)>, ) { - self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| match rhs { - ConstItemRhs::TypeConst(anon_const) => { + self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| match rhs_kind { + ConstItemRhsKind::TypeConst { rhs: Some(anon_const) } => { this.resolve_anon_const(anon_const, AnonConstKind::ConstArg(IsRepeatExpr::No)); } - ConstItemRhs::Body(expr) => { + ConstItemRhsKind::Body { rhs: Some(expr) } => { this.with_constant_rib(IsRepeatExpr::No, ConstantHasGenerics::Yes, item, |this| { this.visit_expr(expr) }); } + _ => (), }) } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 8794c4ff8b02..95f0d3e67ef2 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3510,12 +3510,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { span: lifetime_ref.ident.span, name: lifetime_ref.ident.name, param_kind: errors::ParamKindInNonTrivialAnonConst::Lifetime, - help: self - .r - .tcx - .sess - .is_nightly_build() - .then_some(errors::ParamInNonTrivialAnonConstHelp), + help: self.r.tcx.sess.is_nightly_build(), }) .emit(); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 36ec173cc571..1399f9933ad4 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -8,9 +8,9 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![cfg_attr(bootstrap, feature(ptr_as_ref_unchecked))] #![feature(arbitrary_self_types)] -#![feature(assert_matches)] #![feature(box_patterns)] #![feature(const_default)] #![feature(const_trait_impl)] @@ -72,7 +72,6 @@ use rustc_middle::ty::{ self, DelegationFnSig, DelegationInfo, Feed, MainDefinition, RegisteredTools, ResolverAstLowering, ResolverGlobalCtxt, TyCtxt, TyCtxtFeed, Visibility, }; -use rustc_query_system::ich::StableHashingContext; use rustc_session::config::CrateType; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; @@ -1839,10 +1838,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ResolverOutputs { global_ctxt, ast_lowering } } - fn create_stable_hashing_context(&self) -> StableHashingContext<'_> { - StableHashingContext::new(self.tcx.sess, self.tcx.untracked()) - } - fn cstore(&self) -> FreezeReadGuard<'_, CStore> { CStore::from_tcx(self.tcx) } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index b933c2b9d036..e0973271da52 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -222,17 +222,19 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { ) -> LocalExpnId { let parent_module = parent_module_id.map(|module_id| self.local_def_id(module_id).to_def_id()); - let expn_id = LocalExpnId::fresh( - ExpnData::allow_unstable( - ExpnKind::AstPass(pass), - call_site, - self.tcx.sess.edition(), - features.into(), - None, - parent_module, - ), - self.create_stable_hashing_context(), - ); + let expn_id = self.tcx.with_stable_hashing_context(|hcx| { + LocalExpnId::fresh( + ExpnData::allow_unstable( + ExpnKind::AstPass(pass), + call_site, + self.tcx.sess.edition(), + features.into(), + None, + parent_module, + ), + hcx, + ) + }); let parent_scope = parent_module.map_or(self.empty_module, |def_id| self.expect_module(def_id)); @@ -322,17 +324,19 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { let span = invoc.span(); let def_id = if deleg_impl.is_some() { None } else { res.opt_def_id() }; - invoc_id.set_expn_data( - ext.expn_data( - parent_scope.expansion, - span, - fast_print_path(path), - kind, - def_id, - def_id.map(|def_id| self.macro_def_scope(def_id).nearest_parent_mod()), - ), - self.create_stable_hashing_context(), - ); + self.tcx.with_stable_hashing_context(|hcx| { + invoc_id.set_expn_data( + ext.expn_data( + parent_scope.expansion, + span, + fast_print_path(path), + kind, + def_id, + def_id.map(|def_id| self.macro_def_scope(def_id).nearest_parent_mod()), + ), + hcx, + ) + }); Ok(ext) } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 5e2671ef4ef6..b278a6179fe7 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -88,7 +88,7 @@ pub enum CFProtection { Full, } -#[derive(Clone, Copy, Debug, PartialEq, Hash, HashStable_Generic)] +#[derive(Clone, Copy, Debug, PartialEq, Hash, HashStable_Generic, Encodable, Decodable)] pub enum OptLevel { /// `-Copt-level=0` No, @@ -108,7 +108,7 @@ pub enum OptLevel { /// and taking other command line options into account. /// /// Note that linker plugin-based LTO is a different mechanism entirely. -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Encodable, Decodable)] pub enum Lto { /// Don't do any LTO whatsoever. No, @@ -190,7 +190,7 @@ pub enum CoverageLevel { } // The different settings that the `-Z offload` flag can have. -#[derive(Clone, PartialEq, Hash, Debug)] +#[derive(Clone, PartialEq, Hash, Debug, Encodable, Decodable)] pub enum Offload { /// Entry point for `std::offload`, enables kernel compilation for a gpu device Device, @@ -201,7 +201,7 @@ pub enum Offload { } /// The different settings that the `-Z autodiff` flag can have. -#[derive(Clone, PartialEq, Hash, Debug)] +#[derive(Clone, PartialEq, Hash, Debug, Encodable, Decodable)] pub enum AutoDiff { /// Enable the autodiff opt pipeline Enable, @@ -528,7 +528,7 @@ impl FmtDebug { } } -#[derive(Clone, PartialEq, Hash, Debug)] +#[derive(Clone, PartialEq, Hash, Debug, Encodable, Decodable)] pub enum SwitchWithOptPath { Enabled(Option), Disabled, @@ -583,7 +583,7 @@ pub enum MirStripDebugInfo { /// DWARF provides a mechanism which allows the linker to skip the sections which don't require /// link-time relocation - either by putting those sections in DWARF object files, or by keeping /// them in the object file in such a way that the linker will skip them. -#[derive(Clone, Copy, Debug, PartialEq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable)] pub enum SplitDwarfKind { /// Sections which do not require relocation are written into object file but ignored by the /// linker. @@ -1539,7 +1539,7 @@ pub enum EntryFnType { pub use rustc_hir::attrs::CrateType; -#[derive(Clone, Hash, Debug, PartialEq, Eq)] +#[derive(Clone, Hash, Debug, PartialEq, Eq, Encodable, Decodable)] pub enum Passes { Some(Vec), All, diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index d198675c0b22..62c23424f371 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -280,8 +280,8 @@ pub struct ParseSess { impl ParseSess { /// Used for testing. - pub fn new(locale_resources: Vec<&'static str>) -> Self { - let translator = Translator::with_fallback_bundle(locale_resources, false); + pub fn new() -> Self { + let translator = Translator::new(); let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let emitter = Box::new( AnnotateSnippetEmitter::new(stderr_destination(ColorConfig::Auto), translator) @@ -313,8 +313,8 @@ impl ParseSess { } } - pub fn emitter_with_note(locale_resources: Vec<&'static str>, note: String) -> Self { - let translator = Translator::with_fallback_bundle(locale_resources, false); + pub fn emitter_with_note(note: String) -> Self { + let translator = Translator::new(); let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let emitter = Box::new(AnnotateSnippetEmitter::new( stderr_destination(ColorConfig::Auto), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 298b25088151..ed37e9e960cd 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -19,7 +19,7 @@ use rustc_errors::timings::TimingSectionHandler; use rustc_errors::translation::Translator; use rustc_errors::{ Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort, - TerminalUrl, fallback_fluent_bundle, + TerminalUrl, }; use rustc_hir::limit::Limit; use rustc_macros::HashStable_Generic; @@ -967,8 +967,6 @@ pub fn build_session( sopts: config::Options, io: CompilerIO, fluent_bundle: Option>, - registry: rustc_errors::registry::Registry, - fluent_resources: Vec<&'static str>, driver_lint_caps: FxHashMap, target: Target, cfg_version: &'static str, @@ -986,19 +984,12 @@ pub fn build_session( let cap_lints_allow = sopts.lint_cap.is_some_and(|cap| cap == lint::Allow); let can_emit_warnings = !(warnings_allow || cap_lints_allow); - let translator = Translator { - fluent_bundle, - fallback_fluent_bundle: fallback_fluent_bundle( - fluent_resources, - sopts.unstable_opts.translate_directionality_markers, - ), - }; + let translator = Translator { fluent_bundle }; let source_map = rustc_span::source_map::get_source_map().unwrap(); let emitter = default_emitter(&sopts, Arc::clone(&source_map), translator); - let mut dcx = DiagCtxt::new(emitter) - .with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)) - .with_registry(registry); + let mut dcx = + DiagCtxt::new(emitter).with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)); if let Some(ice_file) = ice_file { dcx = dcx.with_ice_file(ice_file); } @@ -1432,7 +1423,7 @@ impl EarlyDiagCtxt { fn mk_emitter(output: ErrorOutputType) -> Box { // FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will // need to reference every crate that might emit an early error for translation to work. - let translator = Translator::with_fallback_bundle(vec![], false); + let translator = Translator::new(); let emitter: Box = match output { config::ErrorOutputType::HumanReadable { kind, color_config } => match kind { HumanReadableErrorType { short, unicode } => Box::new( diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 05f4c8e268a9..25834f03af5e 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -30,7 +30,7 @@ use std::{fmt, iter, mem}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lock; use rustc_data_structures::unhash::UnhashMap; use rustc_hashes::Hash64; @@ -126,27 +126,6 @@ rustc_index::newtype_index! { impl !Ord for LocalExpnId {} impl !PartialOrd for LocalExpnId {} -/// Assert that the provided `HashStableContext` is configured with the 'default' -/// `HashingControls`. We should always have bailed out before getting to here -/// with a non-default mode. With this check in place, we can avoid the need -/// to maintain separate versions of `ExpnData` hashes for each permutation -/// of `HashingControls` settings. -fn assert_default_hashing_controls(ctx: &impl HashStableContext, msg: &str) { - match ctx.hashing_controls() { - // Note that we require that `hash_spans` be set according to the global - // `-Z incremental-ignore-spans` option. Normally, this option is disabled, - // which will cause us to require that this method always be called with `Span` hashing - // enabled. - // - // Span hashing can also be disabled without `-Z incremental-ignore-spans`. - // This is the case for instance when building a hash for name mangling. - // Such configuration must not be used for metadata. - HashingControls { hash_spans } - if hash_spans != ctx.unstable_opts_incremental_ignore_spans() => {} - other => panic!("Attempted hashing of {msg} with non-default HashingControls: {other:?}"), - } -} - /// A unique hash value associated to an expansion. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encodable, Decodable, HashStable_Generic)] pub struct ExpnHash(Fingerprint); @@ -1506,7 +1485,7 @@ pub fn raw_encode_syntax_context( fn update_disambiguator(expn_data: &mut ExpnData, mut ctx: impl HashStableContext) -> ExpnHash { // This disambiguator should not have been set yet. assert_eq!(expn_data.disambiguator, 0, "Already set disambiguator for ExpnData: {expn_data:?}"); - assert_default_hashing_controls(&ctx, "ExpnData (disambiguator)"); + ctx.assert_default_hashing_controls("ExpnData (disambiguator)"); let mut expn_hash = expn_data.hash_expn(&mut ctx); let disambiguator = HygieneData::with(|data| { @@ -1556,7 +1535,7 @@ impl HashStable for SyntaxContext { impl HashStable for ExpnId { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - assert_default_hashing_controls(ctx, "ExpnId"); + ctx.assert_default_hashing_controls("ExpnId"); let hash = if *self == ExpnId::root() { // Avoid fetching TLS storage for a trivial often-used value. Fingerprint::ZERO diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index a178f3260d30..00646dc7bcf4 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -54,7 +54,6 @@ use hygiene::Transparency; pub use hygiene::{ DesugaringKind, ExpnData, ExpnHash, ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext, }; -use rustc_data_structures::stable_hasher::HashingControls; pub mod def_id; use def_id::{CrateNum, DefId, DefIndex, DefPathHash, LOCAL_CRATE, LocalDefId, StableCrateId}; pub mod edit_distance; @@ -2402,14 +2401,12 @@ impl SourceFile { /// normalized one. Hence we need to convert those offsets to the normalized /// form when constructing spans. pub fn normalized_byte_pos(&self, offset: u32) -> BytePos { - let diff = match self - .normalized_pos - .binary_search_by(|np| (np.pos.0 + np.diff).cmp(&(self.start_pos.0 + offset))) - { - Ok(i) => self.normalized_pos[i].diff, - Err(0) => 0, - Err(i) => self.normalized_pos[i - 1].diff, - }; + let diff = + match self.normalized_pos.binary_search_by(|np| (np.pos.0 + np.diff).cmp(&offset)) { + Ok(i) => self.normalized_pos[i].diff, + Err(0) => 0, + Err(i) => self.normalized_pos[i - 1].diff, + }; BytePos::from_u32(self.start_pos.0 + offset - diff) } @@ -2798,106 +2795,24 @@ impl InnerSpan { /// This is a hack to allow using the [`HashStable_Generic`] derive macro /// instead of implementing everything in rustc_middle. pub trait HashStableContext { + /// The main event: stable hashing of a span. + fn span_hash_stable(&mut self, span: Span, hasher: &mut StableHasher); + + /// Compute a `DefPathHash`. fn def_path_hash(&self, def_id: DefId) -> DefPathHash; - fn hash_spans(&self) -> bool; - /// Accesses `sess.opts.unstable_opts.incremental_ignore_spans` since - /// we don't have easy access to a `Session` - fn unstable_opts_incremental_ignore_spans(&self) -> bool; - fn def_span(&self, def_id: LocalDefId) -> Span; - fn span_data_to_lines_and_cols( - &mut self, - span: &SpanData, - ) -> Option<(&SourceFile, usize, BytePos, usize, BytePos)>; - fn hashing_controls(&self) -> HashingControls; + + /// Assert that the provided `HashStableContext` is configured with the default + /// `HashingControls`. We should always have bailed out before getting to here with a + fn assert_default_hashing_controls(&self, msg: &str); } impl HashStable for Span where CTX: HashStableContext, { - /// Hashes a span in a stable way. We can't directly hash the span's `BytePos` - /// fields (that would be similar to hashing pointers, since those are just - /// offsets into the `SourceMap`). Instead, we hash the (file name, line, column) - /// triple, which stays the same even if the containing `SourceFile` has moved - /// within the `SourceMap`. - /// - /// Also note that we are hashing byte offsets for the column, not unicode - /// codepoint offsets. For the purpose of the hash that's sufficient. - /// Also, hashing filenames is expensive so we avoid doing it twice when the - /// span starts and ends in the same file, which is almost always the case. - /// - /// IMPORTANT: changes to this method should be reflected in implementations of `SpanEncoder`. fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - const TAG_VALID_SPAN: u8 = 0; - const TAG_INVALID_SPAN: u8 = 1; - const TAG_RELATIVE_SPAN: u8 = 2; - - if !ctx.hash_spans() { - return; - } - - let span = self.data_untracked(); - span.ctxt.hash_stable(ctx, hasher); - span.parent.hash_stable(ctx, hasher); - - if span.is_dummy() { - Hash::hash(&TAG_INVALID_SPAN, hasher); - return; - } - - let parent = span.parent.map(|parent| ctx.def_span(parent).data_untracked()); - if let Some(parent) = parent - && parent.contains(span) - { - // This span is enclosed in a definition: only hash the relative position. - // This catches a subset of the cases from the `file.contains(parent.lo)`, - // But we can do this check cheaply without the expensive `span_data_to_lines_and_cols` query. - Hash::hash(&TAG_RELATIVE_SPAN, hasher); - (span.lo - parent.lo).to_u32().hash_stable(ctx, hasher); - (span.hi - parent.lo).to_u32().hash_stable(ctx, hasher); - return; - } - - // If this is not an empty or invalid span, we want to hash the last - // position that belongs to it, as opposed to hashing the first - // position past it. - let Some((file, line_lo, col_lo, line_hi, col_hi)) = ctx.span_data_to_lines_and_cols(&span) - else { - Hash::hash(&TAG_INVALID_SPAN, hasher); - return; - }; - - if let Some(parent) = parent - && file.contains(parent.lo) - { - // This span is relative to another span in the same file, - // only hash the relative position. - Hash::hash(&TAG_RELATIVE_SPAN, hasher); - Hash::hash(&(span.lo.0.wrapping_sub(parent.lo.0)), hasher); - Hash::hash(&(span.hi.0.wrapping_sub(parent.lo.0)), hasher); - return; - } - - Hash::hash(&TAG_VALID_SPAN, hasher); - Hash::hash(&file.stable_id, hasher); - - // Hash both the length and the end location (line/column) of a span. If we - // hash only the length, for example, then two otherwise equal spans with - // different end locations will have the same hash. This can cause a problem - // during incremental compilation wherein a previous result for a query that - // depends on the end location of a span will be incorrectly reused when the - // end location of the span it depends on has changed (see issue #74890). A - // similar analysis applies if some query depends specifically on the length - // of the span, but we only hash the end location. So hash both. - - let col_lo_trunc = (col_lo.0 as u64) & 0xFF; - let line_lo_trunc = ((line_lo as u64) & 0xFF_FF_FF) << 8; - let col_hi_trunc = (col_hi.0 as u64) & 0xFF << 32; - let line_hi_trunc = ((line_hi as u64) & 0xFF_FF_FF) << 40; - let col_line = col_lo_trunc | line_lo_trunc | col_hi_trunc | line_hi_trunc; - let len = (span.hi - span.lo).0; - Hash::hash(&col_line, hasher); - Hash::hash(&len, hasher); + // `span_hash_stable` does all the work. + ctx.span_hash_stable(*self, hasher) } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index aac4cf1de8c2..b9a9d8029d28 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -211,6 +211,7 @@ symbols! { CoercePointeeValidated, CoerceUnsized, Command, + Const, ConstParamTy, ConstParamTy_, Context, @@ -234,6 +235,7 @@ symbols! { DynTrait, Encodable, Encoder, + Enum, Enumerate, Eq, Equal, @@ -292,6 +294,7 @@ symbols! { IteratorMap, Layout, Left, + Lifetime, LinkedList, LintDiagnostic, LintPass, @@ -372,8 +375,8 @@ symbols! { Stdin, Str, String, + Struct, StructuralPartialEq, - SubdiagMessage, Subdiagnostic, SymbolIntern, Sync, @@ -395,6 +398,8 @@ symbols! { Ty, TyCtxt, TyKind, + Type, + Union, Unknown, Unsize, UnsizedConstParamTy, @@ -577,6 +582,7 @@ symbols! { automatically_derived, available_externally, avr, + avr_target_feature, avx, avx10_target_feature, avx512_target_feature, @@ -1085,6 +1091,7 @@ symbols! { ffi_returns_twice, field, field_init_shorthand, + fields, file, file_options, flags, @@ -1173,6 +1180,7 @@ symbols! { generic_const_parameter_types, generic_param_attrs, generic_pattern_types, + generics, get_context, global_alloc_ty, global_allocator, @@ -1452,6 +1460,7 @@ symbols! { meta, meta_sized, metadata_type, + mgca_type_const_syntax, min_const_fn, min_const_generics, min_const_unsafe_fn, @@ -1627,6 +1636,7 @@ symbols! { on_const, on_unimplemented, opaque, + opaque_generic_const_args, opaque_module_name_placeholder: "", open_options_new, ops, @@ -2337,7 +2347,6 @@ symbols! { type_ascribe, type_ascription, type_changing_struct_update, - type_const, type_id, type_id_eq, type_info, @@ -2472,6 +2481,7 @@ symbols! { values, var, variant_count, + variants, vec, vec_as_mut_slice, vec_as_slice, diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 7e126cb6a6e9..35a3d0dac8af 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -88,7 +88,7 @@ //! DefPaths which are much more robust in the face of changes to the code base. // tidy-alphabetical-start -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] // tidy-alphabetical-end use rustc_hir::def::DefKind; diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 50935e7caf33..8f842e030011 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -4,16 +4,14 @@ //! def-path. This is used for unit testing the code that generates //! paths etc in all kinds of annoying scenarios. +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::LocalDefId; +use rustc_hir::find_attr; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{GenericArgs, Instance, TyCtxt}; -use rustc_span::{Symbol, sym}; use crate::errors::{Kind, TestOutput}; -const SYMBOL_NAME: Symbol = sym::rustc_symbol_name; -const DEF_PATH: Symbol = sym::rustc_def_path; - pub fn report_symbol_names(tcx: TyCtxt<'_>) { // if the `rustc_attrs` feature is not enabled, then the // attributes we are interested in cannot be present anyway, so @@ -54,7 +52,11 @@ impl SymbolNamesTest<'_> { // The formatting of `tag({})` is chosen so that tests can elect // to test the entirety of the string, if they choose, or else just // some subset. - for attr in tcx.get_attrs(def_id, SYMBOL_NAME) { + + if let Some(attr_span) = find_attr!( + tcx.get_all_attrs(def_id), + AttributeKind::RustcSymbolName(span) => span + ) { let def_id = def_id.to_def_id(); let instance = Instance::new_raw( def_id, @@ -62,27 +64,30 @@ impl SymbolNamesTest<'_> { ); let mangled = tcx.symbol_name(instance); tcx.dcx().emit_err(TestOutput { - span: attr.span(), + span: *attr_span, kind: Kind::SymbolName, content: format!("{mangled}"), }); if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) { tcx.dcx().emit_err(TestOutput { - span: attr.span(), + span: *attr_span, kind: Kind::Demangling, content: format!("{demangling}"), }); tcx.dcx().emit_err(TestOutput { - span: attr.span(), + span: *attr_span, kind: Kind::DemanglingAlt, content: format!("{demangling:#}"), }); } } - for attr in tcx.get_attrs(def_id, DEF_PATH) { + if let Some(attr_span) = find_attr!( + tcx.get_all_attrs(def_id), + AttributeKind::RustcDefPath(span) => span + ) { tcx.dcx().emit_err(TestOutput { - span: attr.span(), + span: *attr_span, kind: Kind::DefPath, content: with_no_trimmed_paths!(tcx.def_path_str(def_id)), }); diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index fc732170dcb7..f55d03d89e8f 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -1,214 +1,203 @@ -// FIXME: This needs an audit for correctness and completeness. - use rustc_abi::{ - BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Scalar, Size, TyAbiInterface, - TyAndLayout, + Align, BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface, + TyAndLayout, Variants, }; use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Uniform}; -use crate::spec::{Env, HasTargetSpec, Os}; +use crate::spec::{HasTargetSpec, Os}; -#[derive(Clone, Debug)] -struct Sdata { - pub prefix: [Option; 8], - pub prefix_index: usize, - pub last_offset: Size, - pub has_float: bool, - pub arg_attribute: ArgAttribute, +// NOTE: GCC and Clang/LLVM have disagreements that the ABI doesn't resolve, we match the +// Clang/LLVM behavior in these cases. + +#[derive(Copy, Clone)] +enum DoubleWord { + F64, + F128Start, + F128End, + Words([Word; 2]), } -fn arg_scalar(cx: &C, scalar: &Scalar, offset: Size, mut data: Sdata) -> Sdata -where - C: HasDataLayout, -{ - let dl = cx.data_layout(); - - if !matches!(scalar.primitive(), Primitive::Float(Float::F32 | Float::F64)) { - return data; - } - - data.has_float = true; - - if !data.last_offset.is_aligned(dl.f64_align) && data.last_offset < offset { - if data.prefix_index == data.prefix.len() { - return data; - } - data.prefix[data.prefix_index] = Some(Reg::i32()); - data.prefix_index += 1; - data.last_offset = data.last_offset + Reg::i32().size; - } - - for _ in 0..((offset - data.last_offset).bits() / 64) - .min((data.prefix.len() - data.prefix_index) as u64) - { - data.prefix[data.prefix_index] = Some(Reg::i64()); - data.prefix_index += 1; - data.last_offset = data.last_offset + Reg::i64().size; - } - - if data.last_offset < offset { - if data.prefix_index == data.prefix.len() { - return data; - } - data.prefix[data.prefix_index] = Some(Reg::i32()); - data.prefix_index += 1; - data.last_offset = data.last_offset + Reg::i32().size; - } - - if data.prefix_index == data.prefix.len() { - return data; - } - - if scalar.primitive() == Primitive::Float(Float::F32) { - data.arg_attribute = ArgAttribute::InReg; - data.prefix[data.prefix_index] = Some(Reg::f32()); - data.last_offset = offset + Reg::f32().size; - } else { - data.prefix[data.prefix_index] = Some(Reg::f64()); - data.last_offset = offset + Reg::f64().size; - } - data.prefix_index += 1; - data +#[derive(Copy, Clone)] +enum Word { + F32, + Integer, } -fn arg_scalar_pair( +fn classify<'a, Ty, C>( cx: &C, - scalar1: &Scalar, - scalar2: &Scalar, - mut offset: Size, - mut data: Sdata, -) -> Sdata -where - C: HasDataLayout, -{ - data = arg_scalar(cx, scalar1, offset, data); - match (scalar1.primitive(), scalar2.primitive()) { - (Primitive::Float(Float::F32), _) => offset += Reg::f32().size, - (_, Primitive::Float(Float::F64)) => offset += Reg::f64().size, - (Primitive::Int(i, _signed), _) => offset += i.size(), - (Primitive::Pointer(_), _) => offset += Reg::i64().size, - _ => {} - } - - if !offset.bytes().is_multiple_of(4) - && matches!(scalar2.primitive(), Primitive::Float(Float::F32 | Float::F64)) - { - offset += Size::from_bytes(4 - (offset.bytes() % 4)); - } - data = arg_scalar(cx, scalar2, offset, data); - data -} - -fn parse_structure<'a, Ty, C>( - cx: &C, - layout: TyAndLayout<'a, Ty>, - mut data: Sdata, - mut offset: Size, -) -> Sdata -where + arg_layout: &TyAndLayout<'a, Ty>, + offset: Size, + double_words: &mut [DoubleWord; 4], +) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - if let FieldsShape::Union(_) = layout.fields { - return data; - } + // If this function does not update the `double_words` array, the value will be passed via + // integer registers. The array is initialized with `DoubleWord::Words([Word::Integer; 2])`. - match layout.backend_repr { - BackendRepr::Scalar(scalar) => { - data = arg_scalar(cx, &scalar, offset, data); - } - BackendRepr::Memory { .. } => { - for i in 0..layout.fields.count() { - if offset < layout.fields.offset(i) { - offset = layout.fields.offset(i); + match arg_layout.backend_repr { + BackendRepr::Scalar(scalar) => match scalar.primitive() { + Primitive::Float(float) => { + if offset.is_aligned(Ord::min(*float.align(cx), Align::EIGHT)) { + let index = offset.bytes_usize() / 8; + match float { + Float::F128 => { + double_words[index] = DoubleWord::F128Start; + double_words[index + 1] = DoubleWord::F128End; + } + Float::F64 => { + double_words[index] = DoubleWord::F64; + } + Float::F32 => match &mut double_words[index] { + DoubleWord::Words(words) => { + words[(offset.bytes_usize() % 8) / 4] = Word::F32; + } + _ => unreachable!(), + }, + Float::F16 => { + // Match LLVM by passing `f16` in integer registers. + } + } + } else { + /* pass unaligned floats in integer registers */ } - data = parse_structure(cx, layout.field(cx, i), data.clone(), offset); } - } - _ => { - if let BackendRepr::ScalarPair(scalar1, scalar2) = &layout.backend_repr { - data = arg_scalar_pair(cx, scalar1, scalar2, offset, data); + Primitive::Int(_, _) | Primitive::Pointer(_) => { /* pass in integer registers */ } + }, + BackendRepr::SimdVector { .. } => {} + BackendRepr::ScalableVector { .. } => {} + BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => match arg_layout.fields { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") } - } + FieldsShape::Union(_) => { + if !arg_layout.is_zst() { + if arg_layout.is_transparent() { + let non_1zst_elem = arg_layout.non_1zst_field(cx).expect("not exactly one non-1-ZST field in non-ZST repr(transparent) union").1; + classify(cx, &non_1zst_elem, offset, double_words); + } + } + } + FieldsShape::Array { .. } => {} + FieldsShape::Arbitrary { .. } => match arg_layout.variants { + Variants::Multiple { .. } => {} + Variants::Single { .. } | Variants::Empty => { + // Match Clang by ignoring whether a struct is packed and just considering + // whether individual fields are aligned. GCC currently uses only integer + // registers when passing packed structs. + for i in arg_layout.fields.index_by_increasing_offset() { + classify( + cx, + &arg_layout.field(cx, i), + offset + arg_layout.fields.offset(i), + double_words, + ); + } + } + }, + }, } - - data } -fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, in_registers_max: Size) -where +fn classify_arg<'a, Ty, C>( + cx: &C, + arg: &mut ArgAbi<'a, Ty>, + in_registers_max: Size, + total_double_word_count: &mut usize, +) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { + // 64-bit SPARC allocates argument stack space in 64-bit chunks (double words), some of which + // are promoted to registers based on their position on the stack. + + // Keep track of the total number of double words used by arguments so far. This allows padding + // arguments to be inserted where necessary to ensure that 16-aligned arguments are passed in an + // aligned set of registers. + + let pad = !total_double_word_count.is_multiple_of(2) && arg.layout.align.abi.bytes() == 16; + // The number of double words used by this argument. + let double_word_count = arg.layout.size.bytes_usize().div_ceil(8); + // The number of double words before this argument, including any padding. + let start_double_word_count = *total_double_word_count + usize::from(pad); + if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { arg.make_indirect(); + *total_double_word_count += 1; return; } + if !arg.layout.is_aggregate() { arg.extend_integer_width_to(64); + *total_double_word_count = start_double_word_count + double_word_count; return; } let total = arg.layout.size; if total > in_registers_max { arg.make_indirect(); + *total_double_word_count += 1; return; } - match arg.layout.fields { - FieldsShape::Primitive => unreachable!(), - FieldsShape::Array { .. } => { - // Arrays are passed indirectly - arg.make_indirect(); - return; + *total_double_word_count = start_double_word_count + double_word_count; + + const ARGUMENT_REGISTERS: usize = 8; + + let mut double_words = [DoubleWord::Words([Word::Integer; 2]); ARGUMENT_REGISTERS / 2]; + classify(cx, &arg.layout, Size::ZERO, &mut double_words); + + let mut regs = [None; ARGUMENT_REGISTERS]; + let mut i = 0; + let mut push = |reg| { + regs[i] = Some(reg); + i += 1; + }; + let mut attrs = ArgAttribute::empty(); + + for (index, double_word) in double_words.into_iter().enumerate() { + if arg.layout.size.bytes_usize() <= index * 8 { + break; } - FieldsShape::Union(_) => { - // Unions and are always treated as a series of 64-bit integer chunks - } - FieldsShape::Arbitrary { .. } => { - // Structures with floating point numbers need special care. - - let mut data = parse_structure( - cx, - arg.layout, - Sdata { - prefix: [None; 8], - prefix_index: 0, - last_offset: Size::ZERO, - has_float: false, - arg_attribute: ArgAttribute::default(), - }, - Size::ZERO, - ); - - if data.has_float { - // Structure { float, int, int } doesn't like to be handled like - // { float, long int }. Other way around it doesn't mind. - if data.last_offset < arg.layout.size - && !data.last_offset.bytes().is_multiple_of(8) - && data.prefix_index < data.prefix.len() - { - data.prefix[data.prefix_index] = Some(Reg::i32()); - data.prefix_index += 1; - data.last_offset += Reg::i32().size; + match double_word { + // `f128` must be aligned to be assigned a float register. + DoubleWord::F128Start if (start_double_word_count + index).is_multiple_of(2) => { + push(Reg::f128()); + } + DoubleWord::F128Start => { + // Clang currently handles this case nonsensically, always returning a packed + // `struct { long double x; }` in an aligned quad floating-point register even when + // the `long double` isn't aligned on the stack, which also makes all future + // arguments get passed in the wrong registers. This passes the `f128` in integer + // registers when it is unaligned, same as with `f32` and `f64`. + push(Reg::i64()); + push(Reg::i64()); + } + DoubleWord::F128End => {} // Already handled by `F128Start` + DoubleWord::F64 => push(Reg::f64()), + DoubleWord::Words([Word::Integer, Word::Integer]) => push(Reg::i64()), + DoubleWord::Words(words) => { + attrs |= ArgAttribute::InReg; + for word in words { + match word { + Word::F32 => push(Reg::f32()), + Word::Integer => push(Reg::i32()), + } } - - let mut rest_size = arg.layout.size - data.last_offset; - if !rest_size.bytes().is_multiple_of(8) && data.prefix_index < data.prefix.len() { - data.prefix[data.prefix_index] = Some(Reg::i32()); - rest_size = rest_size - Reg::i32().size; - } - - arg.cast_to( - CastTarget::prefixed(data.prefix, Uniform::new(Reg::i64(), rest_size)) - .with_attrs(data.arg_attribute.into()), - ); - return; } } } - arg.cast_to(Uniform::new(Reg::i64(), total)); + let cast_target = match regs { + [Some(reg), None, rest @ ..] => { + // Just a single register is needed for this value. + debug_assert!(rest.iter().all(|x| x.is_none())); + CastTarget::from(reg) + } + _ => CastTarget::prefixed(regs, Uniform::new(Reg::i8(), Size::ZERO)), + }; + + arg.cast_to_and_pad_i32(cast_target.with_attrs(attrs.into()), pad); } pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) @@ -217,23 +206,26 @@ where C: HasDataLayout + HasTargetSpec, { if !fn_abi.ret.is_ignore() && fn_abi.ret.layout.is_sized() { - classify_arg(cx, &mut fn_abi.ret, Size::from_bytes(32)); + // A return value of 32 bytes or smaller is passed via registers. + classify_arg(cx, &mut fn_abi.ret, Size::from_bytes(32), &mut 0); } + // sparc64-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs. + let passes_zsts = matches!(cx.target_spec().os, Os::Linux); + + let mut double_word_count = 0; for arg in fn_abi.args.iter_mut() { if !arg.layout.is_sized() { continue; } if arg.is_ignore() { - // sparc64-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs. - if cx.target_spec().os == Os::Linux - && matches!(cx.target_spec().env, Env::Gnu | Env::Musl | Env::Uclibc) - && arg.layout.is_zst() - { + if passes_zsts && arg.layout.is_zst() { arg.make_indirect_from_ignore(); + double_word_count += 1; } - return; + continue; } - classify_arg(cx, arg, Size::from_bytes(16)); + // An argument of 16 bytes or smaller is passed via registers. + classify_arg(cx, arg, Size::from_bytes(16), &mut double_word_count); } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 537185f536ab..2d2f15651c43 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1082,7 +1082,7 @@ crate::target_spec_enum! { } crate::target_spec_enum! { - #[derive(Default)] + #[derive(Default, Encodable, Decodable)] pub enum SplitDebuginfo { /// Split debug-information is disabled, meaning that on supported platforms /// you can find all debug information in the executable itself. This is diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs index 0916864ac4cc..513a022adec1 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target, metadata: TargetMetadata { description: Some("ARM64 Apple tvOS".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs index 2e18abdef399..0f48a36d5d77 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target, metadata: TargetMetadata { description: Some("ARM64 Apple tvOS Simulator".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs index b771b3ff10b9..453809fb3bb3 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target, metadata: TargetMetadata { description: Some("ARM64 Apple visionOS".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs index d4b4d94115b4..1aacb41ffeaa 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target, metadata: TargetMetadata { description: Some("ARM64 Apple visionOS simulator".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs index 9f0a24ddd334..82c986a0f8fe 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target, metadata: TargetMetadata { description: Some("ARM64 Apple watchOS".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs index fabf01d641c3..77b03c1de722 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target, metadata: TargetMetadata { description: Some("ARM64 Apple watchOS Simulator".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index fb735b54dd82..4b6f5b655760 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -19,8 +19,15 @@ pub(crate) fn target() -> Target { pre_link_args, post_link_args, relocation_model: RelocModel::Pic, + // crt_static should always be true for an executable and always false + // for a shared library. There is no easy way to indicate this and it + // doesn't seem to matter much so we set crt_static_allows_dylibs to + // true and leave crt_static as true when linking dynamic libraries. + // wasi also sets crt_static_allows_dylibs: true so this is at least + // aligned between wasm targets. crt_static_respected: true, crt_static_default: true, + crt_static_allows_dylibs: true, panic_strategy: PanicStrategy::Unwind, no_default_libraries: false, families: cvs!["unix", "wasm"], diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index dfa2b839a4c0..f17927484648 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -898,6 +898,28 @@ static M68K_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; +static AVR_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("addsubiw", Unstable(sym::avr_target_feature), &[]), + ("break", Unstable(sym::avr_target_feature), &[]), + ("eijmpcall", Unstable(sym::avr_target_feature), &[]), + ("elpm", Unstable(sym::avr_target_feature), &[]), + ("elpmx", Unstable(sym::avr_target_feature), &[]), + ("ijmpcall", Unstable(sym::avr_target_feature), &[]), + ("jmpcall", Unstable(sym::avr_target_feature), &[]), + ("lowbytefirst", Unstable(sym::avr_target_feature), &[]), + ("lpm", Unstable(sym::avr_target_feature), &[]), + ("lpmx", Unstable(sym::avr_target_feature), &[]), + ("movw", Unstable(sym::avr_target_feature), &[]), + ("mul", Unstable(sym::avr_target_feature), &[]), + ("rmw", Unstable(sym::avr_target_feature), &[]), + ("spm", Unstable(sym::avr_target_feature), &[]), + ("spmx", Unstable(sym::avr_target_feature), &[]), + ("sram", Forbidden { reason: "devices that have no SRAM are unsupported" }, &[]), + ("tinyencoding", Unstable(sym::avr_target_feature), &[]), + // tidy-alphabetical-end +]; + /// When rustdoc is running, provide a list of all known features so that all their respective /// primitives may be documented. /// @@ -919,6 +941,7 @@ pub fn all_rust_features() -> impl Iterator { .chain(IBMZ_FEATURES) .chain(SPARC_FEATURES) .chain(M68K_FEATURES) + .chain(AVR_FEATURES) .cloned() .map(|(f, s, _)| (f, s)) } @@ -961,8 +984,11 @@ const RISCV_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'stat const SPARC_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = &[/*(64, "vis")*/]; -const HEXAGON_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = - &[(512, "hvx-length64b"), (1024, "hvx-length128b")]; +const HEXAGON_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = &[ + (512, "hvx-length64b"), // HvxVector in 64-byte mode + (1024, "hvx-length128b"), // HvxVector in 128-byte mode, or HvxVectorPair in 64-byte mode + (2048, "hvx-length128b"), // HvxVectorPair in 128-byte mode +]; const MIPS_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "msa")]; const CSKY_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = @@ -996,12 +1022,8 @@ impl Target { Arch::S390x => IBMZ_FEATURES, Arch::Sparc | Arch::Sparc64 => SPARC_FEATURES, Arch::M68k => M68K_FEATURES, - Arch::AmdGpu - | Arch::Avr - | Arch::Msp430 - | Arch::SpirV - | Arch::Xtensa - | Arch::Other(_) => &[], + Arch::Avr => AVR_FEATURES, + Arch::AmdGpu | Arch::Msp430 | Arch::SpirV | Arch::Xtensa | Arch::Other(_) => &[], } } @@ -1023,11 +1045,11 @@ impl Target { MIPS_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI } Arch::AmdGpu => AMDGPU_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, - Arch::Nvptx64 | Arch::Bpf | Arch::M68k => &[], // no vector ABI + Arch::Nvptx64 | Arch::Bpf | Arch::M68k | Arch::Avr => &[], // no vector ABI Arch::CSky => CSKY_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, // FIXME: for some tier3 targets, we are overly cautious and always give warnings // when passing args in vector registers. - Arch::Avr | Arch::Msp430 | Arch::SpirV | Arch::Xtensa | Arch::Other(_) => &[], + Arch::Msp430 | Arch::SpirV | Arch::Xtensa | Arch::Other(_) => &[], } } @@ -1240,6 +1262,12 @@ impl Target { } } } + Arch::Avr => { + // SRAM is minimum requirement for C/C++ in both avr-gcc and Clang, + // and backends of them only support assembly for devices have no SRAM. + // See the discussion in https://github.com/rust-lang/rust/pull/146900 for more. + FeatureConstraints { required: &["sram"], incompatible: &[] } + } _ => NOTHING, } } diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 1071105522d1..cd61a8f65473 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -10,7 +10,6 @@ rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_infer = { path = "../rustc_infer" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl deleted file mode 100644 index fcb250250871..000000000000 --- a/compiler/rustc_trait_selection/messages.ftl +++ /dev/null @@ -1,470 +0,0 @@ -trait_selection_actual_impl_expl_but_actually_implemented_for_ty = ...but `{$trait_path}` is actually implemented for the type `{$ty}`{$has_lifetime -> - [true] , for some specific lifetime `'{$lifetime}` - *[false] {""} -} -trait_selection_actual_impl_expl_but_actually_implements_trait = ...but it actually implements `{$trait_path}`{$has_lifetime -> - [true] , for some specific lifetime `'{$lifetime}` - *[false] {""} -} -trait_selection_actual_impl_expl_but_actually_ty_implements = ...but `{$ty}` actually implements `{$trait_path}`{$has_lifetime -> - [true] , for some specific lifetime `'{$lifetime}` - *[false] {""} -} - -trait_selection_actual_impl_expl_expected_other_any = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`... -trait_selection_actual_impl_expl_expected_other_nothing = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$ty_or_sig}` must implement `{$trait_path}` - -trait_selection_actual_impl_expl_expected_other_some = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`... -trait_selection_actual_impl_expl_expected_other_two = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... -trait_selection_actual_impl_expl_expected_passive_any = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any lifetime `'{$lifetime_1}`... -trait_selection_actual_impl_expl_expected_passive_nothing = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}` -trait_selection_actual_impl_expl_expected_passive_some = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for some specific lifetime `'{$lifetime_1}`... -trait_selection_actual_impl_expl_expected_passive_two = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... -trait_selection_actual_impl_expl_expected_signature_any = {$leading_ellipsis -> - [true] ... - *[false] {""} -}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`... -trait_selection_actual_impl_expl_expected_signature_nothing = {$leading_ellipsis -> - [true] ... - *[false] {""} -}closure with signature `{$ty_or_sig}` must implement `{$trait_path}` -trait_selection_actual_impl_expl_expected_signature_some = {$leading_ellipsis -> - [true] ... - *[false] {""} -}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`... -trait_selection_actual_impl_expl_expected_signature_two = {$leading_ellipsis -> - [true] ... - *[false] {""} -}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... -trait_selection_adjust_signature_borrow = consider adjusting the signature so it borrows its {$len -> - [one] argument - *[other] arguments - } - -trait_selection_adjust_signature_remove_borrow = consider adjusting the signature so it does not borrow its {$len -> - [one] argument - *[other] arguments - } - -trait_selection_ascribe_user_type_prove_predicate = ...so that the where clause holds - -trait_selection_await_both_futures = consider `await`ing on both `Future`s -trait_selection_await_future = consider `await`ing on the `Future` -trait_selection_await_note = calling an async function returns a future - -trait_selection_but_calling_introduces = {$has_param_name -> - [true] `{$param_name}` - *[false] `fn` parameter -} has {$lifetime_kind -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` -} but calling `{$assoc_item}` introduces an implicit `'static` lifetime requirement - .label1 = {$has_lifetime -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` - } - .label2 = ...is used and required to live as long as `'static` here because of an implicit lifetime bound on the {$has_impl_path -> - [true] `impl` of `{$impl_path}` - *[false] inherent `impl` - } - -trait_selection_but_needs_to_satisfy = {$has_param_name -> - [true] `{$param_name}` - *[false] `fn` parameter -} has {$has_lifetime -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` -} but it needs to satisfy a `'static` lifetime requirement - .influencer = this data with {$has_lifetime -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` - }... - .require = {$spans_empty -> - *[true] ...is used and required to live as long as `'static` here - [false] ...and is required to live as long as `'static` here - } - .used_here = ...is used here... - .introduced_by_bound = `'static` lifetime requirement introduced by this bound - -trait_selection_closure_fn_mut_label = closure is `{$trait_prefix}FnMut` because it mutates the variable `{$place}` here - -trait_selection_closure_fn_once_label = closure is `{$trait_prefix}FnOnce` because it moves the variable `{$place}` out of its environment - -trait_selection_closure_kind_mismatch = expected a closure that implements the `{$trait_prefix}{$expected}` trait, but this closure only implements `{$trait_prefix}{$found}` - .label = this closure implements `{$trait_prefix}{$found}`, not `{$trait_prefix}{$expected}` - -trait_selection_closure_kind_requirement = the requirement to implement `{$trait_prefix}{$expected}` derives from here - -trait_selection_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait -trait_selection_consider_specifying_length = consider specifying the actual array length -trait_selection_coro_closure_not_fn = {$coro_kind}closure does not implement `{$kind}` because it captures state from its environment - -trait_selection_data_flows = ...but data{$label_var1_exists -> - [true] {" "}from `{$label_var1}` - *[false] {""} -} flows{$label_var2_exists -> - [true] {" "}into `{$label_var2}` - *[false] {""} -} here - -trait_selection_data_lifetime_flow = ...but data with one lifetime flows into the other here -trait_selection_data_returned = ...but data{$label_var1_exists -> - [true] {" "}from `{$label_var1}` - *[false] {""} -} is returned here - -trait_selection_declared_different = this parameter and the return type are declared with different lifetimes... -trait_selection_declared_multiple = this type is declared with multiple lifetimes... -trait_selection_disallowed_positional_argument = positional format arguments are not allowed here - .help = only named format arguments with the name of one of the generic types are allowed in this context - -trait_selection_does_not_outlive_static_from_impl = ...does not necessarily outlive the static lifetime introduced by the compatible `impl` -trait_selection_dtcs_has_lifetime_req_label = this has an implicit `'static` lifetime requirement -trait_selection_dtcs_has_req_note = the used `impl` has a `'static` requirement -trait_selection_dtcs_introduces_requirement = calling this method introduces the `impl`'s `'static` requirement -trait_selection_dtcs_suggestion = consider relaxing the implicit `'static` requirement - -trait_selection_explicit_lifetime_required_sugg_with_ident = add explicit lifetime `{$named}` to the type of `{$simple_ident}` - -trait_selection_explicit_lifetime_required_sugg_with_param_type = add explicit lifetime `{$named}` to type - -trait_selection_explicit_lifetime_required_with_ident = explicit lifetime required in the type of `{$simple_ident}` - .label = lifetime `{$named}` required - -trait_selection_explicit_lifetime_required_with_param_type = explicit lifetime required in parameter type - .label = lifetime `{$named}` required - -trait_selection_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}` - -trait_selection_fn_consider_casting_both = consider casting both fn items to fn pointers using `as {$sig}` - -trait_selection_fn_uniq_types = different fn items have unique types, even if their signatures are the same -trait_selection_fps_cast = consider casting to a fn pointer -trait_selection_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}` - -trait_selection_fps_items_are_distinct = fn items are distinct from fn pointers -trait_selection_fps_remove_ref = consider removing the reference -trait_selection_fps_use_ref = consider using a reference -trait_selection_fulfill_req_lifetime = the type `{$ty}` does not fulfill the required lifetime - -trait_selection_ignored_diagnostic_option = `{$option_name}` is ignored due to previous definition of `{$option_name}` - .other_label = `{$option_name}` is first declared here - .label = `{$option_name}` is already declared here - -trait_selection_implicit_static_lifetime_note = this has an implicit `'static` lifetime requirement -trait_selection_implicit_static_lifetime_suggestion = consider relaxing the implicit `'static` requirement -trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}` - -trait_selection_invalid_format_specifier = invalid format specifier - .help = no format specifier are supported in this position - -trait_selection_label_bad = {$bad_kind -> - *[other] cannot infer type - [more_info] cannot infer {$prefix_kind -> - *[type] type for {$prefix} - [const_with_param] the value of const parameter - [const] the value of the constant - } `{$name}`{$has_parent -> - [true] {" "}declared on the {$parent_prefix} `{$parent_name}` - *[false] {""} - } -} - -trait_selection_lf_bound_not_satisfied = lifetime bound not satisfied -trait_selection_lifetime_mismatch = lifetime mismatch - -trait_selection_lifetime_param_suggestion = consider {$is_reuse -> - [true] reusing - *[false] introducing -} a named lifetime parameter{$is_impl -> - [true] {" "}and update trait if needed - *[false] {""} -} -trait_selection_lifetime_param_suggestion_elided = each elided lifetime in input position becomes a distinct lifetime - -trait_selection_malformed_on_unimplemented_attr = malformed `on_unimplemented` attribute - .help = only `message`, `note` and `label` are allowed as options - .label = invalid option found here - -trait_selection_meant_byte_literal = if you meant to write a byte literal, prefix with `b` -trait_selection_meant_char_literal = if you meant to write a `char` literal, use single quotes -trait_selection_meant_str_literal = if you meant to write a string literal, use double quotes -trait_selection_mismatched_static_lifetime = incompatible lifetime on type -trait_selection_missing_options_for_on_unimplemented_attr = missing options for `on_unimplemented` attribute - .help = at least one of the `message`, `note` and `label` options are expected - -trait_selection_msl_introduces_static = introduces a `'static` lifetime requirement -trait_selection_msl_unmet_req = because this has an unmet lifetime requirement - -trait_selection_negative_positive_conflict = found both positive and negative implementation of trait `{$trait_desc}`{$self_desc -> - [none] {""} - *[default] {" "}for type `{$self_desc}` - }: - .negative_implementation_here = negative implementation here - .negative_implementation_in_crate = negative implementation in crate `{$negative_impl_cname}` - .positive_implementation_here = positive implementation here - .positive_implementation_in_crate = positive implementation in crate `{$positive_impl_cname}` - -trait_selection_nothing = {""} - -trait_selection_oc_cant_coerce_force_inline = - cannot coerce functions which must be inlined to function pointers -trait_selection_oc_cant_coerce_intrinsic = cannot coerce intrinsics to function pointers -trait_selection_oc_closure_selfref = closure/coroutine type that references itself -trait_selection_oc_const_compat = const not compatible with trait -trait_selection_oc_fn_lang_correct_type = {$lang_item_name -> - [panic_impl] `#[panic_handler]` - *[lang_item_name] lang item `{$lang_item_name}` - } function has wrong type -trait_selection_oc_fn_main_correct_type = `main` function has wrong type -trait_selection_oc_generic = mismatched types - -trait_selection_oc_if_else_different = `if` and `else` have incompatible types -trait_selection_oc_intrinsic_correct_type = intrinsic has wrong type -trait_selection_oc_match_compat = `match` arms have incompatible types -trait_selection_oc_method_compat = method not compatible with trait -trait_selection_oc_method_correct_type = mismatched `self` parameter type -trait_selection_oc_no_diverge = `else` clause of `let...else` does not diverge -trait_selection_oc_no_else = `if` may be missing an `else` clause -trait_selection_oc_try_compat = `?` operator has incompatible types -trait_selection_oc_type_compat = type not compatible with trait - -trait_selection_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds - .label = opaque type defined here -trait_selection_opaque_type_non_generic_param = - expected generic {$kind} parameter, found `{$arg}` - .label = {STREQ($arg, "'static") -> - [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type - *[other] this generic parameter must be used with a generic {$kind} parameter - } - -trait_selection_outlives_bound = lifetime of the source pointer does not outlive lifetime bound of the object type -trait_selection_outlives_content = lifetime of reference outlives lifetime of borrowed content... - -trait_selection_precise_capturing_existing = add `{$new_lifetime}` to the `use<...>` bound to explicitly capture it -trait_selection_precise_capturing_new = add a `use<...>` bound to explicitly capture `{$new_lifetime}` - -trait_selection_precise_capturing_new_but_apit = add a `use<...>` bound to explicitly capture `{$new_lifetime}` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate - -trait_selection_precise_capturing_overcaptures = use the precise capturing `use<...>` syntax to make the captures explicit - -trait_selection_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here... -trait_selection_prlf_defined_without_sub = the lifetime defined here... -trait_selection_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 for more information) - -trait_selection_prlf_must_outlive_with_sup = ...must outlive the lifetime `{$sup_symbol}` defined here -trait_selection_prlf_must_outlive_without_sup = ...must outlive the lifetime defined here -trait_selection_reborrow = ...so that reference does not outlive borrowed content -trait_selection_ref_longer_than_data = in type `{$ty}`, reference has a longer lifetime than the data it references - -trait_selection_reference_outlives_referent = ...so that the reference type `{$name}` does not outlive the data it points at -trait_selection_region_explanation = {$pref_kind -> - *[should_not_happen] [{$pref_kind}] - [ref_valid_for] ...the reference is valid for - [content_valid_for] ...but the borrowed content is only valid for - [type_obj_valid_for] object type is valid for - [source_pointer_valid_for] source pointer is only valid for - [type_satisfy] type must satisfy - [type_outlive] type must outlive - [lf_param_instantiated_with] lifetime parameter instantiated with - [lf_param_must_outlive] but lifetime parameter must outlive - [lf_instantiated_with] lifetime instantiated with - [lf_must_outlive] but lifetime must outlive - [pointer_valid_for] the pointer is valid for - [data_valid_for] but the referenced data is only valid for - [empty] {""} -}{$pref_kind -> - [empty] {""} - *[other] {" "} -}{$desc_kind -> - *[should_not_happen] [{$desc_kind}] - [restatic] the static lifetime - [revar] lifetime {$desc_arg} - [as_defined] the lifetime `{$desc_arg}` as defined here - [as_defined_anon] the anonymous lifetime as defined here - [defined_here] the anonymous lifetime defined here - [defined_here_reg] the lifetime `{$desc_arg}` as defined here -}{$suff_kind -> - *[should_not_happen] [{$suff_kind}] - [empty]{""} - [continues] ... - [req_by_binding] {" "}as required by this binding -} - -trait_selection_relate_object_bound = ...so that it can be closed over into an object -trait_selection_relate_param_bound = ...so that the type `{$name}` will meet its required lifetime bounds{$continues -> - [true] ... - *[false] {""} -} -trait_selection_relate_param_bound_2 = ...that is required by this bound -trait_selection_relate_region_param_bound = ...so that the declared lifetime parameter bounds are satisfied -trait_selection_ril_because_of = because of this returned expression -trait_selection_ril_introduced_by = requirement introduced by this return type -trait_selection_ril_introduced_here = `'static` requirement introduced here -trait_selection_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type - -trait_selection_rustc_on_unimplemented_empty_on_clause = empty `on`-clause in `#[rustc_on_unimplemented]` - .label = empty `on`-clause here -trait_selection_rustc_on_unimplemented_expected_identifier = expected an identifier inside this `on`-clause - .label = expected an identifier here, not `{$path}` -trait_selection_rustc_on_unimplemented_expected_one_predicate_in_not = expected a single predicate in `not(..)` - .label = unexpected quantity of predicates here -trait_selection_rustc_on_unimplemented_invalid_flag = invalid flag in `on`-clause - .label = expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}` -trait_selection_rustc_on_unimplemented_invalid_name = invalid name in `on`-clause - .label = expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `{$invalid_name}` -trait_selection_rustc_on_unimplemented_invalid_predicate = this predicate is invalid - .label = expected one of `any`, `all` or `not` here, not `{$invalid_pred}` -trait_selection_rustc_on_unimplemented_missing_value = this attribute must have a value - .label = expected value here - .note = e.g. `#[rustc_on_unimplemented(message="foo")]` -trait_selection_rustc_on_unimplemented_unsupported_literal_in_on = literals inside `on`-clauses are not supported - .label = unexpected literal here - -trait_selection_source_kind_closure_return = - try giving this closure an explicit return type - -# coroutine_kind may need to be translated -trait_selection_source_kind_fully_qualified = - try using a fully qualified path to specify the expected types - -trait_selection_source_kind_subdiag_generic_label = - cannot infer {$is_type -> - [true] type - *[false] the value - } of the {$is_type -> - [true] type - *[false] const - } {$parent_exists -> - [true] parameter `{$param_name}` declared on the {$parent_prefix} `{$parent_name}` - *[false] parameter {$param_name} - } - -trait_selection_source_kind_subdiag_generic_suggestion = - consider specifying the generic {$arg_count -> - [one] argument - *[other] arguments - } - -trait_selection_source_kind_subdiag_let = {$kind -> - [with_pattern] consider giving `{$name}` an explicit type - [closure] consider giving this closure parameter an explicit type - *[other] consider giving this pattern a type -}{$x_kind -> - [has_name] , where the {$prefix_kind -> - *[type] type for {$prefix} - [const_with_param] value of const parameter - [const] value of the constant - } `{$arg_name}` is specified - [underscore] , where the placeholders `_` are specified - *[empty] {""} -} - -trait_selection_srs_add = consider returning the local binding `{$ident}` -trait_selection_srs_add_one = consider returning one of these bindings - -trait_selection_srs_remove = consider removing this semicolon -trait_selection_srs_remove_and_box = consider removing this semicolon and boxing the expressions -trait_selection_stp_wrap_many = try wrapping the pattern in a variant of `{$path}` - -trait_selection_stp_wrap_one = try wrapping the pattern in `{$variant}` -trait_selection_subtype = ...so that the {$requirement -> - [method_compat] method type is compatible with trait - [type_compat] associated type is compatible with trait - [const_compat] const is compatible with trait - [expr_assignable] expression is assignable - [if_else_different] `if` and `else` have incompatible types - [no_else] `if` missing an `else` returns `()` - [fn_main_correct_type] `main` function has the correct type - [fn_lang_correct_type] lang item function has the correct type - [intrinsic_correct_type] intrinsic has the correct type - [method_correct_type] method receiver has the correct type - *[other] types are compatible -} -trait_selection_subtype_2 = ...so that {$requirement -> - [method_compat] method type is compatible with trait - [type_compat] associated type is compatible with trait - [const_compat] const is compatible with trait - [expr_assignable] expression is assignable - [if_else_different] `if` and `else` have incompatible types - [no_else] `if` missing an `else` returns `()` - [fn_main_correct_type] `main` function has the correct type - [fn_lang_correct_type] lang item function has the correct type - [intrinsic_correct_type] intrinsic has the correct type - [method_correct_type] method receiver has the correct type - *[other] types are compatible -} - -trait_selection_suggest_accessing_field = you might have meant to use field `{$name}` whose type is `{$ty}` - -trait_selection_suggest_add_let_for_letchains = consider adding `let` - -trait_selection_tid_consider_borrowing = consider borrowing this type parameter in the trait -trait_selection_tid_param_help = the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` - -trait_selection_tid_rel_help = verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output -trait_selection_trait_has_no_impls = this trait has no implementations, consider adding one - -trait_selection_trait_impl_diff = `impl` item signature doesn't match `trait` item signature - .found = found `{$found}` - .expected = expected `{$expected}` - .expected_found = expected signature `{$expected}` - {" "}found signature `{$found}` - -trait_selection_trait_placeholder_mismatch = implementation of `{$trait_def_id}` is not general enough - .label_satisfy = doesn't satisfy where-clause - .label_where = due to a where-clause on `{$def_id}`... - .label_dup = implementation of `{$trait_def_id}` is not general enough - -trait_selection_try_cannot_convert = `?` operator cannot convert from `{$found}` to `{$expected}` - -trait_selection_tuple_trailing_comma = use a trailing comma to create a tuple with one element - -trait_selection_ty_alias_overflow = in case this is a recursive type alias, consider using a struct, enum, or union instead -trait_selection_type_annotations_needed = {$source_kind -> - [closure] type annotations needed for the closure `{$source_name}` - [normal] type annotations needed for `{$source_name}` - *[other] type annotations needed -} - .label = type must be known at this point - -trait_selection_types_declared_different = these two types are declared with different lifetimes... - -trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated} - -trait_selection_unknown_format_parameter_for_on_unimplemented_attr = there is no parameter `{$argument_name}` on trait `{$trait_name}` - .help = expect either a generic argument name or {"`{Self}`"} as format argument - -trait_selection_warn_removing_apit_params_for_overcapture = you could use a `use<...>` bound to explicitly specify captures, but argument-position `impl Trait`s are not nameable - -trait_selection_warn_removing_apit_params_for_undercapture = you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable - -trait_selection_where_copy_predicates = copy the `where` clause predicates from the trait - -trait_selection_where_remove = remove the `where` clause -trait_selection_wrapped_parser_error = {$description} - .label = {$label} diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index ca846bca874e..80e97d36c23f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -1023,7 +1023,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { hir::ExprKind::MethodCall(segment, ..) => { if let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) { let generics = tcx.generics_of(def_id); - let insertable: Option<_> = try { + let insertable = try { if generics.has_impl_trait() { None? } @@ -1061,7 +1061,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // // FIXME: We deal with that one separately for now, // would be good to remove this special case. - let last_segment_using_path_data: Option<_> = try { + let last_segment_using_path_data = try { let generics_def_id = tcx.res_generics_def_id(path.res)?; let generics = tcx.generics_of(generics_def_id); if generics.has_impl_trait() { @@ -1117,19 +1117,18 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { }; let generics = tcx.generics_of(def_id); - let segment: Option<_> = try { - if !segment.infer_args || generics.has_impl_trait() { - do yeet (); - } + let segment = if !segment.infer_args || generics.has_impl_trait() { + None + } else { let span = tcx.hir_span(segment.hir_id); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); - InsertableGenericArgs { + Some(InsertableGenericArgs { insert_span, args, generics_def_id: def_id, def_id, have_turbofish: false, - } + }) }; let parent_def_id = generics.parent.unwrap(); @@ -1263,7 +1262,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { .iter() .position(|&arg| self.generic_arg_contains_target(arg)) { - if generics.parent.is_none() && generics.has_self { + if generics.has_own_self() { argument_index += 1; } let args = self.tecx.resolve_vars_if_possible(args); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs index 8fe4ffebd865..7d061e65df80 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -190,8 +190,8 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { } Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)) => { - debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,); - debug!("id={:?}", id); + debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {debruijn_index:?}"); + debug!("id={id:?}"); if debruijn_index == self.current_index && id.to_def_id() == self.region_def_id { return ControlFlow::Break(()); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 3ee6e6b739c0..3ed1f7c3481f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -239,14 +239,14 @@ pub fn suggest_new_region_bound( }; spans_suggs.push((fn_return.span.shrink_to_hi(), format!(" + {name} "))); err.multipart_suggestion_verbose( - format!("{declare} `{ty}` {captures}, {use_lt}",), + format!("{declare} `{ty}` {captures}, {use_lt}"), spans_suggs, Applicability::MaybeIncorrect, ); } else { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), - format!("{declare} `{ty}` {captures}, {explicit}",), + format!("{declare} `{ty}` {captures}, {explicit}"), &plus_lt, Applicability::MaybeIncorrect, ); @@ -257,7 +257,7 @@ pub fn suggest_new_region_bound( if let LifetimeKind::ImplicitObjectLifetimeDefault = lt.kind { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), - format!("{declare} the trait object {captures}, {explicit}",), + format!("{declare} the trait object {captures}, {explicit}"), &plus_lt, Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs index 2a3268d3339e..5a8dd0364bee 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs @@ -16,7 +16,7 @@ use tracing::debug; use crate::error_reporting::infer::nice_region_error::NiceRegionError; use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted; -use crate::errors::{ConsiderBorrowingParamHelp, RelationshipHelp, TraitImplDiff}; +use crate::errors::{ConsiderBorrowingParamHelp, TraitImplDiff}; use crate::infer::{RegionResolutionError, ValuePairs}; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { @@ -117,7 +117,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { trait_sp, note: (), param_help: ConsiderBorrowingParamHelp { spans: visitor.types.to_vec() }, - rel_help: visitor.types.is_empty().then_some(RelationshipHelp), + rel_help: visitor.types.is_empty(), expected, found, }; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index b11461cd0e32..81cca3dd67ac 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -2,7 +2,8 @@ use std::iter; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{ - Applicability, Diag, E0309, E0310, E0311, E0803, Subdiagnostic, struct_span_code_err, + Applicability, Diag, E0309, E0310, E0311, E0803, Subdiagnostic, inline_fluent, + struct_span_code_err, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -25,7 +26,6 @@ use crate::errors::{ self, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent, RefLongerThanData, RegionOriginNote, WhereClauseSuggestions, note_and_explain, }; -use crate::fluent_generated as fluent; use crate::infer::region_constraints::GenericKind; use crate::infer::{ BoundRegionConversionTime, InferCtxt, RegionResolutionError, RegionVariableOrigin, @@ -228,18 +228,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { expected_found: self.values_str(trace.values, &trace.cause, err.long_ty_path()), } .add_to_diag(err), - SubregionOrigin::Reborrow(span) => { - RegionOriginNote::Plain { span, msg: fluent::trait_selection_reborrow } - .add_to_diag(err) + SubregionOrigin::Reborrow(span) => RegionOriginNote::Plain { + span, + msg: inline_fluent!("...so that reference does not outlive borrowed content"), } + .add_to_diag(err), SubregionOrigin::RelateObjectBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::trait_selection_relate_object_bound } - .add_to_diag(err); + RegionOriginNote::Plain { + span, + msg: inline_fluent!("...so that it can be closed over into an object"), + } + .add_to_diag(err); } SubregionOrigin::ReferenceOutlivesReferent(ty, span) => { RegionOriginNote::WithName { span, - msg: fluent::trait_selection_reference_outlives_referent, + msg: inline_fluent!("...so that the reference type `{$name}` does not outlive the data it points at"), name: &self.ty_to_string(ty), continues: false, } @@ -248,7 +252,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { SubregionOrigin::RelateParamBound(span, ty, opt_span) => { RegionOriginNote::WithName { span, - msg: fluent::trait_selection_relate_param_bound, + msg: inline_fluent!("...so that the type `{$name}` will meet its required lifetime bounds{$continues -> +[true] ... +*[false] {\"\"} +}"), name: &self.ty_to_string(ty), continues: opt_span.is_some(), } @@ -256,7 +263,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(span) = opt_span { RegionOriginNote::Plain { span, - msg: fluent::trait_selection_relate_param_bound_2, + msg: inline_fluent!("...that is required by this bound"), } .add_to_diag(err); } @@ -264,14 +271,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { SubregionOrigin::RelateRegionParamBound(span, _) => { RegionOriginNote::Plain { span, - msg: fluent::trait_selection_relate_region_param_bound, + msg: inline_fluent!( + "...so that the declared lifetime parameter bounds are satisfied" + ), } .add_to_diag(err); } SubregionOrigin::CompareImplItemObligation { span, .. } => { RegionOriginNote::Plain { span, - msg: fluent::trait_selection_compare_impl_item_obligation, + msg: inline_fluent!( + "...so that the definition in impl matches the definition from the trait" + ), } .add_to_diag(err); } @@ -281,7 +292,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { SubregionOrigin::AscribeUserTypeProvePredicate(span) => { RegionOriginNote::Plain { span, - msg: fluent::trait_selection_ascribe_user_type_prove_predicate, + msg: inline_fluent!("...so that the where clause holds"), } .add_to_diag(err); } @@ -1009,7 +1020,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let Some((sup_expected, sup_found)) = self.values_str(sup_trace.values, &sup_trace.cause, err.long_ty_path()) && let Some((sub_expected, sub_found)) = - self.values_str(sub_trace.values, &sup_trace.cause, err.long_ty_path()) + self.values_str(sub_trace.values, &sub_trace.cause, err.long_ty_path()) && sub_expected == sup_expected && sub_found == sup_found { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index eb72f71382ef..16c4ac68cdf0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -710,7 +710,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { predicate ); let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { - format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::>().join("\n"),) + format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::>().join("\n")) } else if post.len() == 1 { format!(": `{}`", post[0]) } else { @@ -722,7 +722,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.note(format!("cannot satisfy `{predicate}`")); } (0, _, 1) => { - err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,)); + err.note(format!("{msg} in the `{}` crate{post}", crates[0])); } (0, _, _) => { err.note(format!( @@ -739,7 +739,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (_, 1, 1) => { let span: MultiSpan = spans.into(); err.span_note(span, msg); - err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,)); + err.note(format!("and another `impl` found in the `{}` crate{post}", crates[0])); } _ => { let span: MultiSpan = spans.into(); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 136f084fd2e9..68240f5ea599 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -12,7 +12,7 @@ use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions, - pluralize, struct_span_code_err, + inline_fluent, pluralize, struct_span_code_err, }; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::Visitor; @@ -2263,7 +2263,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.highlighted_span_help( self.tcx.def_span(def_id), vec![ - StringPart::normal(format!("the trait `{trait_}` ",)), + StringPart::normal(format!("the trait `{trait_}` ")), StringPart::highlighted("is"), StringPart::normal(desc), StringPart::highlighted(self_ty), @@ -3088,7 +3088,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { err.span_help( self.tcx.def_span(trait_def_id), - crate::fluent_generated::trait_selection_trait_has_no_impls, + inline_fluent!("this trait has no implementations, consider adding one"), ); } else if !suggested && trait_predicate.polarity() == ty::PredicatePolarity::Positive { // Can't show anything else useful, try to find similar impls. diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 0a6af42e122b..a98f952d55a3 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -360,10 +360,10 @@ pub enum AppendConstMessage { } #[derive(LintDiagnostic)] -#[diag(trait_selection_malformed_on_unimplemented_attr)] -#[help] +#[diag("malformed `on_unimplemented` attribute")] +#[help("only `message`, `note` and `label` are allowed as options")] pub struct MalformedOnUnimplementedAttrLint { - #[label] + #[label("invalid option found here")] pub span: Span, } @@ -374,17 +374,17 @@ impl MalformedOnUnimplementedAttrLint { } #[derive(LintDiagnostic)] -#[diag(trait_selection_missing_options_for_on_unimplemented_attr)] -#[help] +#[diag("missing options for `on_unimplemented` attribute")] +#[help("at least one of the `message`, `note` and `label` options are expected")] pub struct MissingOptionsForOnUnimplementedAttr; #[derive(LintDiagnostic)] -#[diag(trait_selection_ignored_diagnostic_option)] +#[diag("`{$option_name}` is ignored due to previous definition of `{$option_name}`")] pub struct IgnoredDiagnosticOption { pub option_name: &'static str, - #[label] + #[label("`{$option_name}` is already declared here")] pub span: Span, - #[label(trait_selection_other_label)] + #[label("`{$option_name}` is first declared here")] pub prev_span: Span, } @@ -410,7 +410,7 @@ impl IgnoredDiagnosticOption { } #[derive(LintDiagnostic)] -#[diag(trait_selection_wrapped_parser_error)] +#[diag("{$description}")] pub struct WrappedParserError { pub description: String, pub label: String, @@ -888,7 +888,7 @@ impl<'tcx> OnUnimplementedFormatString { } } else { let reported = - struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,) + struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description) .emit(); result = Err(reported); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs index 1954f8a1f639..8488d76e2c77 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs @@ -310,25 +310,27 @@ pub mod errors { use super::*; #[derive(LintDiagnostic)] - #[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)] - #[help] + #[diag("there is no parameter `{$argument_name}` on trait `{$trait_name}`")] + #[help("expect either a generic argument name or {\"`{Self}`\"} as format argument")] pub struct UnknownFormatParameterForOnUnimplementedAttr { pub argument_name: Symbol, pub trait_name: Ident, } #[derive(LintDiagnostic)] - #[diag(trait_selection_disallowed_positional_argument)] - #[help] + #[diag("positional format arguments are not allowed here")] + #[help( + "only named format arguments with the name of one of the generic types are allowed in this context" + )] pub struct DisallowedPositionalArgument; #[derive(LintDiagnostic)] - #[diag(trait_selection_invalid_format_specifier)] - #[help] + #[diag("invalid format specifier")] + #[help("no format specifier are supported in this position")] pub struct InvalidFormatSpecifier; #[derive(LintDiagnostic)] - #[diag(trait_selection_missing_options_for_on_unimplemented_attr)] - #[help] + #[diag("missing options for `on_unimplemented` attribute")] + #[help("at least one of the `message`, `note` and `label` options are expected")] pub struct MissingOptionsForOnUnimplementedAttr; } diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index af241099c014..0b19a470b726 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -3,7 +3,7 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, - EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, + EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, inline_fluent, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -17,12 +17,11 @@ use rustc_span::{BytePos, Ident, Span, Symbol, kw}; use crate::error_reporting::infer::ObligationCauseAsDiagArg; use crate::error_reporting::infer::need_type_info::UnderspecifiedArgKind; use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted; -use crate::fluent_generated as fluent; pub mod note_and_explain; #[derive(Diagnostic)] -#[diag(trait_selection_unable_to_construct_constant_value)] +#[diag("unable to construct a constant value for the unevaluated constant {$unevaluated}")] pub struct UnableToConstructConstantValue<'a> { #[primary_span] pub span: Span, @@ -31,60 +30,64 @@ pub struct UnableToConstructConstantValue<'a> { #[derive(Diagnostic)] pub enum InvalidOnClause { - #[diag(trait_selection_rustc_on_unimplemented_empty_on_clause, code = E0232)] + #[diag("empty `on`-clause in `#[rustc_on_unimplemented]`", code = E0232)] Empty { #[primary_span] - #[label] + #[label("empty `on`-clause here")] span: Span, }, - #[diag(trait_selection_rustc_on_unimplemented_expected_one_predicate_in_not, code = E0232)] + #[diag("expected a single predicate in `not(..)`", code = E0232)] ExpectedOnePredInNot { #[primary_span] - #[label] + #[label("unexpected quantity of predicates here")] span: Span, }, - #[diag(trait_selection_rustc_on_unimplemented_unsupported_literal_in_on, code = E0232)] + #[diag("literals inside `on`-clauses are not supported", code = E0232)] UnsupportedLiteral { #[primary_span] - #[label] + #[label("unexpected literal here")] span: Span, }, - #[diag(trait_selection_rustc_on_unimplemented_expected_identifier, code = E0232)] + #[diag("expected an identifier inside this `on`-clause", code = E0232)] ExpectedIdentifier { #[primary_span] - #[label] + #[label("expected an identifier here, not `{$path}`")] span: Span, path: Path, }, - #[diag(trait_selection_rustc_on_unimplemented_invalid_predicate, code = E0232)] + #[diag("this predicate is invalid", code = E0232)] InvalidPredicate { #[primary_span] - #[label] + #[label("expected one of `any`, `all` or `not` here, not `{$invalid_pred}`")] span: Span, invalid_pred: Symbol, }, - #[diag(trait_selection_rustc_on_unimplemented_invalid_flag, code = E0232)] + #[diag("invalid flag in `on`-clause", code = E0232)] InvalidFlag { #[primary_span] - #[label] + #[label( + "expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}`" + )] span: Span, invalid_flag: Symbol, }, - #[diag(trait_selection_rustc_on_unimplemented_invalid_name, code = E0232)] + #[diag("invalid name in `on`-clause", code = E0232)] InvalidName { #[primary_span] - #[label] + #[label( + "expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `{$invalid_name}`" + )] span: Span, invalid_name: Symbol, }, } #[derive(Diagnostic)] -#[diag(trait_selection_rustc_on_unimplemented_missing_value, code = E0232)] -#[note] +#[diag("this attribute must have a value", code = E0232)] +#[note("e.g. `#[rustc_on_unimplemented(message=\"foo\")]`")] pub struct NoValueInOnUnimplemented { #[primary_span] - #[label] + #[label("expected value here")] pub span: Span, } @@ -99,26 +102,33 @@ pub struct NegativePositiveConflict<'tcx> { impl Diagnostic<'_, G> for NegativePositiveConflict<'_> { #[track_caller] fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { - let mut diag = Diag::new(dcx, level, fluent::trait_selection_negative_positive_conflict); + let mut diag = Diag::new(dcx, level, inline_fluent!("found both positive and negative implementation of trait `{$trait_desc}`{$self_desc -> +[none] {\"\"} +*[default] {\" \"}for type `{$self_desc}` +}:")); diag.arg("trait_desc", self.trait_desc.print_only_trait_path().to_string()); diag.arg("self_desc", self.self_ty.map_or_else(|| "none".to_string(), |ty| ty.to_string())); diag.span(self.impl_span); diag.code(E0751); match self.negative_impl_span { Ok(span) => { - diag.span_label(span, fluent::trait_selection_negative_implementation_here); + diag.span_label(span, inline_fluent!("negative implementation here")); } Err(cname) => { - diag.note(fluent::trait_selection_negative_implementation_in_crate); + diag.note(inline_fluent!( + "negative implementation in crate `{$negative_impl_cname}`" + )); diag.arg("negative_impl_cname", cname.to_string()); } } match self.positive_impl_span { Ok(span) => { - diag.span_label(span, fluent::trait_selection_positive_implementation_here); + diag.span_label(span, inline_fluent!("positive implementation here")); } Err(cname) => { - diag.note(fluent::trait_selection_positive_implementation_in_crate); + diag.note(inline_fluent!( + "positive implementation in crate `{$positive_impl_cname}`" + )); diag.arg("positive_impl_cname", cname.to_string()); } } @@ -127,7 +137,7 @@ impl Diagnostic<'_, G> for NegativePositiveConflict<'_> { } #[derive(Diagnostic)] -#[diag(trait_selection_inherent_projection_normalization_overflow)] +#[diag("overflow evaluating associated type `{$ty}`")] pub struct InherentProjectionNormalizationOverflow { #[primary_span] pub span: Span, @@ -143,17 +153,27 @@ impl Subdiagnostic for AdjustSignatureBorrow { fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { AdjustSignatureBorrow::Borrow { to_borrow } => { - diag.arg("len", to_borrow.len()); + diag.arg("borrow_len", to_borrow.len()); diag.multipart_suggestion_verbose( - fluent::trait_selection_adjust_signature_borrow, + inline_fluent!( + "consider adjusting the signature so it borrows its {$borrow_len -> +[one] argument +*[other] arguments +}" + ), to_borrow, Applicability::MaybeIncorrect, ); } AdjustSignatureBorrow::RemoveBorrow { remove_borrow } => { - diag.arg("len", remove_borrow.len()); + diag.arg("remove_borrow_len", remove_borrow.len()); diag.multipart_suggestion_verbose( - fluent::trait_selection_adjust_signature_remove_borrow, + inline_fluent!( + "consider adjusting the signature so it does not borrow its {$remove_borrow_len -> +[one] argument +*[other] arguments +}" + ), remove_borrow, Applicability::MaybeIncorrect, ); @@ -163,14 +183,14 @@ impl Subdiagnostic for AdjustSignatureBorrow { } #[derive(Diagnostic)] -#[diag(trait_selection_closure_kind_mismatch, code = E0525)] +#[diag("expected a closure that implements the `{$trait_prefix}{$expected}` trait, but this closure only implements `{$trait_prefix}{$found}`", code = E0525)] pub struct ClosureKindMismatch { #[primary_span] - #[label] + #[label("this closure implements `{$trait_prefix}{$found}`, not `{$trait_prefix}{$expected}`")] pub closure_span: Span, pub expected: ClosureKind, pub found: ClosureKind, - #[label(trait_selection_closure_kind_requirement)] + #[label("the requirement to implement `{$trait_prefix}{$expected}` derives from here")] pub cause_span: Span, pub trait_prefix: &'static str, @@ -183,7 +203,9 @@ pub struct ClosureKindMismatch { } #[derive(Subdiagnostic)] -#[label(trait_selection_closure_fn_once_label)] +#[label( + "closure is `{$trait_prefix}FnOnce` because it moves the variable `{$place}` out of its environment" +)] pub struct ClosureFnOnceLabel { #[primary_span] pub span: Span, @@ -191,7 +213,7 @@ pub struct ClosureFnOnceLabel { } #[derive(Subdiagnostic)] -#[label(trait_selection_closure_fn_mut_label)] +#[label("closure is `{$trait_prefix}FnMut` because it mutates the variable `{$place}` here")] pub struct ClosureFnMutLabel { #[primary_span] pub span: Span, @@ -199,7 +221,9 @@ pub struct ClosureFnMutLabel { } #[derive(Diagnostic)] -#[diag(trait_selection_coro_closure_not_fn)] +#[diag( + "{$coro_kind}closure does not implement `{$kind}` because it captures state from its environment" +)] pub(crate) struct CoroClosureNotFn { #[primary_span] pub span: Span, @@ -208,13 +232,17 @@ pub(crate) struct CoroClosureNotFn { } #[derive(Diagnostic)] -#[diag(trait_selection_type_annotations_needed, code = E0282)] +#[diag("{$source_kind -> +[closure] type annotations needed for the closure `{$source_name}` +[normal] type annotations needed for `{$source_name}` +*[other] type annotations needed +}", code = E0282)] pub struct AnnotationRequired<'a> { #[primary_span] pub span: Span, pub source_kind: &'static str, pub source_name: &'a str, - #[label] + #[label("type must be known at this point")] pub failure_span: Option, #[subdiagnostic] pub bad_label: Option>, @@ -226,13 +254,17 @@ pub struct AnnotationRequired<'a> { // Copy of `AnnotationRequired` for E0283 #[derive(Diagnostic)] -#[diag(trait_selection_type_annotations_needed, code = E0283)] +#[diag("{$source_kind -> +[closure] type annotations needed for the closure `{$source_name}` +[normal] type annotations needed for `{$source_name}` +*[other] type annotations needed +}", code = E0283)] pub struct AmbiguousImpl<'a> { #[primary_span] pub span: Span, pub source_kind: &'static str, pub source_name: &'a str, - #[label] + #[label("type must be known at this point")] pub failure_span: Option, #[subdiagnostic] pub bad_label: Option>, @@ -244,13 +276,17 @@ pub struct AmbiguousImpl<'a> { // Copy of `AnnotationRequired` for E0284 #[derive(Diagnostic)] -#[diag(trait_selection_type_annotations_needed, code = E0284)] +#[diag("{$source_kind -> +[closure] type annotations needed for the closure `{$source_name}` +[normal] type annotations needed for `{$source_name}` +*[other] type annotations needed +}", code = E0284)] pub struct AmbiguousReturn<'a> { #[primary_span] pub span: Span, pub source_kind: &'static str, pub source_name: &'a str, - #[label] + #[label("type must be known at this point")] pub failure_span: Option, #[subdiagnostic] pub bad_label: Option>, @@ -262,7 +298,19 @@ pub struct AmbiguousReturn<'a> { // Used when a better one isn't available #[derive(Subdiagnostic)] -#[label(trait_selection_label_bad)] +#[label( + "{$bad_kind -> +*[other] cannot infer type +[more_info] cannot infer {$prefix_kind -> +*[type] type for {$prefix} +[const_with_param] the value of const parameter +[const] the value of the constant +} `{$name}`{$has_parent -> +[true] {\" \"}declared on the {$parent_prefix} `{$parent_name}` +*[false] {\"\"} +} +}" +)] pub struct InferenceBadError<'a> { #[primary_span] pub span: Span, @@ -278,7 +326,19 @@ pub struct InferenceBadError<'a> { #[derive(Subdiagnostic)] pub enum SourceKindSubdiag<'a> { #[suggestion( - trait_selection_source_kind_subdiag_let, + "{$kind -> + [with_pattern] consider giving `{$name}` an explicit type + [closure] consider giving this closure parameter an explicit type + *[other] consider giving this pattern a type + }{$x_kind -> + [has_name] , where the {$prefix_kind -> + *[type] type for {$prefix} + [const_with_param] value of const parameter + [const] value of the constant + } `{$arg_name}` is specified + [underscore] , where the placeholders `_` are specified + *[empty] {\"\"} + }", style = "verbose", code = ": {type_name}", applicability = "has-placeholders" @@ -294,7 +354,18 @@ pub enum SourceKindSubdiag<'a> { prefix: &'a str, arg_name: String, }, - #[label(trait_selection_source_kind_subdiag_generic_label)] + #[label( + "cannot infer {$is_type -> + [true] type + *[false] the value + } of the {$is_type -> + [true] type + *[false] const + } {$parent_exists -> + [true] parameter `{$param_name}` declared on the {$parent_prefix} `{$parent_name}` + *[false] parameter {$param_name} + }" + )] GenericLabel { #[primary_span] span: Span, @@ -305,7 +376,10 @@ pub enum SourceKindSubdiag<'a> { parent_name: String, }, #[suggestion( - trait_selection_source_kind_subdiag_generic_suggestion, + "consider specifying the generic {$arg_count -> + [one] argument + *[other] arguments + }", style = "verbose", code = "::<{args}>", applicability = "has-placeholders" @@ -321,7 +395,7 @@ pub enum SourceKindSubdiag<'a> { #[derive(Subdiagnostic)] pub enum SourceKindMultiSuggestion<'a> { #[multipart_suggestion( - trait_selection_source_kind_fully_qualified, + "try using a fully qualified path to specify the expected types", style = "verbose", applicability = "has-placeholders" )] @@ -335,7 +409,7 @@ pub enum SourceKindMultiSuggestion<'a> { successor_pos: &'a str, }, #[multipart_suggestion( - trait_selection_source_kind_closure_return, + "try giving this closure an explicit return type", style = "verbose", applicability = "has-placeholders" )] @@ -427,7 +501,24 @@ impl Subdiagnostic for RegionOriginNote<'_> { requirement, expected_found: Some((expected, found)), } => { - label_or_note(span, fluent::trait_selection_subtype); + label_or_note( + span, + inline_fluent!( + "...so that the {$requirement -> +[method_compat] method type is compatible with trait +[type_compat] associated type is compatible with trait +[const_compat] const is compatible with trait +[expr_assignable] expression is assignable +[if_else_different] `if` and `else` have incompatible types +[no_else] `if` missing an `else` returns `()` +[fn_main_correct_type] `main` function has the correct type +[fn_lang_correct_type] lang item function has the correct type +[intrinsic_correct_type] intrinsic has the correct type +[method_correct_type] method receiver has the correct type +*[other] types are compatible +}" + ), + ); diag.arg("requirement", requirement); diag.note_expected_found("", expected, "", found); @@ -436,7 +527,24 @@ impl Subdiagnostic for RegionOriginNote<'_> { // FIXME: this really should be handled at some earlier stage. Our // handling of region checking when type errors are present is // *terrible*. - label_or_note(span, fluent::trait_selection_subtype_2); + label_or_note( + span, + inline_fluent!( + "...so that {$requirement -> +[method_compat] method type is compatible with trait +[type_compat] associated type is compatible with trait +[const_compat] const is compatible with trait +[expr_assignable] expression is assignable +[if_else_different] `if` and `else` have incompatible types +[no_else] `if` missing an `else` returns `()` +[fn_main_correct_type] `main` function has the correct type +[fn_lang_correct_type] lang item function has the correct type +[intrinsic_correct_type] intrinsic has the correct type +[method_correct_type] method receiver has the correct type +*[other] types are compatible +}" + ), + ); diag.arg("requirement", requirement); } }; @@ -464,9 +572,17 @@ impl Subdiagnostic for LifetimeMismatchLabels { fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { - diag.span_label(param_span, fluent::trait_selection_declared_different); - diag.span_label(ret_span, fluent::trait_selection_nothing); - diag.span_label(span, fluent::trait_selection_data_returned); + diag.span_label(param_span, inline_fluent!("this parameter and the return type are declared with different lifetimes...")); + diag.span_label(ret_span, inline_fluent!("{\"\"}")); + diag.span_label( + span, + inline_fluent!( + "...but data{$label_var1_exists -> +[true] {\" \"}from `{$label_var1}` +*[false] {\"\"} +} is returned here" + ), + ); diag.arg("label_var1_exists", label_var1.is_some()); diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default()); } @@ -479,13 +595,33 @@ impl Subdiagnostic for LifetimeMismatchLabels { sub: label_var2, } => { if hir_equal { - diag.span_label(ty_sup, fluent::trait_selection_declared_multiple); - diag.span_label(ty_sub, fluent::trait_selection_nothing); - diag.span_label(span, fluent::trait_selection_data_lifetime_flow); + diag.span_label( + ty_sup, + inline_fluent!("this type is declared with multiple lifetimes..."), + ); + diag.span_label(ty_sub, inline_fluent!("{\"\"}")); + diag.span_label( + span, + inline_fluent!("...but data with one lifetime flows into the other here"), + ); } else { - diag.span_label(ty_sup, fluent::trait_selection_types_declared_different); - diag.span_label(ty_sub, fluent::trait_selection_nothing); - diag.span_label(span, fluent::trait_selection_data_flows); + diag.span_label( + ty_sup, + inline_fluent!("these two types are declared with different lifetimes..."), + ); + diag.span_label(ty_sub, inline_fluent!("{\"\"}")); + diag.span_label( + span, + inline_fluent!( + "...but data{$label_var1_exists -> +[true] {\" \"}from `{$label_var1}` +*[false] {\"\"} +} flows{$label_var2_exists -> +[true] {\" \"}into `{$label_var2}` +*[false] {\"\"} +} here" + ), + ); diag.arg("label_var1_exists", label_var1.is_some()); diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default()); diag.arg("label_var2_exists", label_var2.is_some()); @@ -651,7 +787,15 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { visitor.suggestions.push(new_param_suggestion); } diag.multipart_suggestion_verbose( - fluent::trait_selection_lifetime_param_suggestion, + inline_fluent!( + "consider {$is_reuse -> +[true] reusing +*[false] introducing +} a named lifetime parameter{$is_impl -> +[true] {\" \"}and update trait if needed +*[false] {\"\"} +}" + ), visitor.suggestions, Applicability::MaybeIncorrect, ); @@ -661,13 +805,15 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { true }; if mk_suggestion() && self.add_note { - diag.note(fluent::trait_selection_lifetime_param_suggestion_elided); + diag.note(inline_fluent!( + "each elided lifetime in input position becomes a distinct lifetime" + )); } } } #[derive(Diagnostic)] -#[diag(trait_selection_lifetime_mismatch, code = E0623)] +#[diag("lifetime mismatch", code = E0623)] pub struct LifetimeMismatch<'a> { #[primary_span] pub span: Span, @@ -684,33 +830,42 @@ pub struct IntroducesStaticBecauseUnmetLifetimeReq { impl Subdiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { fn add_to_diag(mut self, diag: &mut Diag<'_, G>) { - self.unmet_requirements - .push_span_label(self.binding_span, fluent::trait_selection_msl_introduces_static); - diag.span_note(self.unmet_requirements, fluent::trait_selection_msl_unmet_req); + self.unmet_requirements.push_span_label( + self.binding_span, + inline_fluent!("introduces a `'static` lifetime requirement"), + ); + diag.span_note( + self.unmet_requirements, + inline_fluent!("because this has an unmet lifetime requirement"), + ); } } // FIXME(#100717): replace with a `Option` when subdiagnostic supports that #[derive(Subdiagnostic)] pub enum DoesNotOutliveStaticFromImpl { - #[note(trait_selection_does_not_outlive_static_from_impl)] + #[note( + "...does not necessarily outlive the static lifetime introduced by the compatible `impl`" + )] Spanned { #[primary_span] span: Span, }, - #[note(trait_selection_does_not_outlive_static_from_impl)] + #[note( + "...does not necessarily outlive the static lifetime introduced by the compatible `impl`" + )] Unspanned, } #[derive(Subdiagnostic)] pub enum ImplicitStaticLifetimeSubdiag { - #[note(trait_selection_implicit_static_lifetime_note)] + #[note("this has an implicit `'static` lifetime requirement")] Note { #[primary_span] span: Span, }, #[suggestion( - trait_selection_implicit_static_lifetime_suggestion, + "consider relaxing the implicit `'static` requirement", style = "verbose", code = " + '_", applicability = "maybe-incorrect" @@ -722,7 +877,7 @@ pub enum ImplicitStaticLifetimeSubdiag { } #[derive(Diagnostic)] -#[diag(trait_selection_mismatched_static_lifetime)] +#[diag("incompatible lifetime on type")] pub struct MismatchedStaticLifetime<'a> { #[primary_span] pub cause_span: Span, @@ -738,15 +893,15 @@ pub struct MismatchedStaticLifetime<'a> { #[derive(Diagnostic)] pub enum ExplicitLifetimeRequired<'a> { - #[diag(trait_selection_explicit_lifetime_required_with_ident, code = E0621)] + #[diag("explicit lifetime required in the type of `{$simple_ident}`", code = E0621)] WithIdent { #[primary_span] - #[label] + #[label("lifetime `{$named}` required")] span: Span, simple_ident: Ident, named: String, #[suggestion( - trait_selection_explicit_lifetime_required_sugg_with_ident, + "add explicit lifetime `{$named}` to the type of `{$simple_ident}`", code = "{new_ty}", applicability = "unspecified", style = "verbose" @@ -755,14 +910,14 @@ pub enum ExplicitLifetimeRequired<'a> { #[skip_arg] new_ty: Ty<'a>, }, - #[diag(trait_selection_explicit_lifetime_required_with_param_type, code = E0621)] + #[diag("explicit lifetime required in parameter type", code = E0621)] WithParamType { #[primary_span] - #[label] + #[label("lifetime `{$named}` required")] span: Span, named: String, #[suggestion( - trait_selection_explicit_lifetime_required_sugg_with_param_type, + "add explicit lifetime `{$named}` to type", code = "{new_ty}", applicability = "unspecified", style = "verbose" @@ -789,7 +944,10 @@ impl IntoDiagArg for TyOrSig<'_> { #[derive(Subdiagnostic)] pub enum ActualImplExplNotes<'tcx> { - #[note(trait_selection_actual_impl_expl_expected_signature_two)] + #[note("{$leading_ellipsis -> + [true] ... + *[false] {\"\"} + }closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`...")] ExpectedSignatureTwo { leading_ellipsis: bool, ty_or_sig: TyOrSig<'tcx>, @@ -797,27 +955,41 @@ pub enum ActualImplExplNotes<'tcx> { lifetime_1: usize, lifetime_2: usize, }, - #[note(trait_selection_actual_impl_expl_expected_signature_any)] + #[note("{$leading_ellipsis -> + [true] ... + *[false] {\"\"} + }closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`...")] ExpectedSignatureAny { leading_ellipsis: bool, ty_or_sig: TyOrSig<'tcx>, trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, lifetime_1: usize, }, - #[note(trait_selection_actual_impl_expl_expected_signature_some)] + #[note("{$leading_ellipsis -> + [true] ... + *[false] {\"\"} + }closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`...")] ExpectedSignatureSome { leading_ellipsis: bool, ty_or_sig: TyOrSig<'tcx>, trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, lifetime_1: usize, }, - #[note(trait_selection_actual_impl_expl_expected_signature_nothing)] + #[note( + "{$leading_ellipsis -> + [true] ... + *[false] {\"\"} + }closure with signature `{$ty_or_sig}` must implement `{$trait_path}`" + )] ExpectedSignatureNothing { leading_ellipsis: bool, ty_or_sig: TyOrSig<'tcx>, trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, }, - #[note(trait_selection_actual_impl_expl_expected_passive_two)] + #[note("{$leading_ellipsis -> + [true] ... + *[false] {\"\"} + }`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`...")] ExpectedPassiveTwo { leading_ellipsis: bool, ty_or_sig: TyOrSig<'tcx>, @@ -825,27 +997,41 @@ pub enum ActualImplExplNotes<'tcx> { lifetime_1: usize, lifetime_2: usize, }, - #[note(trait_selection_actual_impl_expl_expected_passive_any)] + #[note("{$leading_ellipsis -> + [true] ... + *[false] {\"\"} + }`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any lifetime `'{$lifetime_1}`...")] ExpectedPassiveAny { leading_ellipsis: bool, ty_or_sig: TyOrSig<'tcx>, trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, lifetime_1: usize, }, - #[note(trait_selection_actual_impl_expl_expected_passive_some)] + #[note("{$leading_ellipsis -> + [true] ... + *[false] {\"\"} + }`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for some specific lifetime `'{$lifetime_1}`...")] ExpectedPassiveSome { leading_ellipsis: bool, ty_or_sig: TyOrSig<'tcx>, trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, lifetime_1: usize, }, - #[note(trait_selection_actual_impl_expl_expected_passive_nothing)] + #[note( + "{$leading_ellipsis -> + [true] ... + *[false] {\"\"} + }`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`" + )] ExpectedPassiveNothing { leading_ellipsis: bool, ty_or_sig: TyOrSig<'tcx>, trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, }, - #[note(trait_selection_actual_impl_expl_expected_other_two)] + #[note("{$leading_ellipsis -> + [true] ... + *[false] {\"\"} + }`{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`...")] ExpectedOtherTwo { leading_ellipsis: bool, ty_or_sig: TyOrSig<'tcx>, @@ -853,40 +1039,70 @@ pub enum ActualImplExplNotes<'tcx> { lifetime_1: usize, lifetime_2: usize, }, - #[note(trait_selection_actual_impl_expl_expected_other_any)] + #[note( + "{$leading_ellipsis -> + [true] ... + *[false] {\"\"} + }`{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`..." + )] ExpectedOtherAny { leading_ellipsis: bool, ty_or_sig: TyOrSig<'tcx>, trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, lifetime_1: usize, }, - #[note(trait_selection_actual_impl_expl_expected_other_some)] + #[note( + "{$leading_ellipsis -> + [true] ... + *[false] {\"\"} + }`{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`..." + )] ExpectedOtherSome { leading_ellipsis: bool, ty_or_sig: TyOrSig<'tcx>, trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, lifetime_1: usize, }, - #[note(trait_selection_actual_impl_expl_expected_other_nothing)] + #[note( + "{$leading_ellipsis -> + [true] ... + *[false] {\"\"} + }`{$ty_or_sig}` must implement `{$trait_path}`" + )] ExpectedOtherNothing { leading_ellipsis: bool, ty_or_sig: TyOrSig<'tcx>, trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, }, - #[note(trait_selection_actual_impl_expl_but_actually_implements_trait)] + #[note( + "...but it actually implements `{$trait_path}`{$has_lifetime -> + [true] , for some specific lifetime `'{$lifetime}` + *[false] {\"\"} + }" + )] ButActuallyImplementsTrait { trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, has_lifetime: bool, lifetime: usize, }, - #[note(trait_selection_actual_impl_expl_but_actually_implemented_for_ty)] + #[note( + "...but `{$trait_path}` is actually implemented for the type `{$ty}`{$has_lifetime -> + [true] , for some specific lifetime `'{$lifetime}` + *[false] {\"\"} + }" + )] ButActuallyImplementedForTy { trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, has_lifetime: bool, lifetime: usize, ty: String, }, - #[note(trait_selection_actual_impl_expl_but_actually_ty_implements)] + #[note( + "...but `{$ty}` actually implements `{$trait_path}`{$has_lifetime -> + [true] , for some specific lifetime `'{$lifetime}` + *[false] {\"\"} + }" + )] ButActuallyTyImplements { trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, has_lifetime: bool, @@ -978,15 +1194,15 @@ impl<'tcx> ActualImplExplNotes<'tcx> { } #[derive(Diagnostic)] -#[diag(trait_selection_trait_placeholder_mismatch)] +#[diag("implementation of `{$trait_def_id}` is not general enough")] pub struct TraitPlaceholderMismatch<'tcx> { #[primary_span] pub span: Span, - #[label(trait_selection_label_satisfy)] + #[label("doesn't satisfy where-clause")] pub satisfy_span: Option, - #[label(trait_selection_label_where)] + #[label("due to a where-clause on `{$def_id}`...")] pub where_span: Option, - #[label(trait_selection_label_dup)] + #[label("implementation of `{$trait_def_id}` is not general enough")] pub dup_span: Option, pub def_id: String, pub trait_def_id: String, @@ -1004,33 +1220,35 @@ impl Subdiagnostic for ConsiderBorrowingParamHelp { let mut type_param_span: MultiSpan = self.spans.clone().into(); for &span in &self.spans { // Seems like we can't call f() here as Into is required - type_param_span.push_span_label(span, fluent::trait_selection_tid_consider_borrowing); + type_param_span.push_span_label( + span, + inline_fluent!("consider borrowing this type parameter in the trait"), + ); } - let msg = diag.eagerly_translate(fluent::trait_selection_tid_param_help); + let msg = diag.eagerly_translate(inline_fluent!("the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`")); diag.span_help(type_param_span, msg); } } -#[derive(Subdiagnostic)] -#[help(trait_selection_tid_rel_help)] -pub struct RelationshipHelp; - #[derive(Diagnostic)] -#[diag(trait_selection_trait_impl_diff)] +#[diag("`impl` item signature doesn't match `trait` item signature")] pub struct TraitImplDiff { #[primary_span] - #[label(trait_selection_found)] + #[label("found `{$found}`")] pub sp: Span, - #[label(trait_selection_expected)] + #[label("expected `{$expected}`")] pub trait_sp: Span, - #[note(trait_selection_expected_found)] + #[note( + "expected signature `{$expected}` + {\" \"}found signature `{$found}`" + )] pub note: (), #[subdiagnostic] pub param_help: ConsiderBorrowingParamHelp, - #[subdiagnostic] - // Seems like subdiagnostics are always pushed to the end, so this one - // also has to be a subdiagnostic to maintain order. - pub rel_help: Option, + #[help( + "verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output" + )] + pub rel_help: bool, pub expected: String, pub found: String, } @@ -1043,12 +1261,20 @@ pub struct DynTraitConstraintSuggestion { impl Subdiagnostic for DynTraitConstraintSuggestion { fn add_to_diag(self, diag: &mut Diag<'_, G>) { let mut multi_span: MultiSpan = vec![self.span].into(); - multi_span.push_span_label(self.span, fluent::trait_selection_dtcs_has_lifetime_req_label); - multi_span - .push_span_label(self.ident.span, fluent::trait_selection_dtcs_introduces_requirement); - let msg = diag.eagerly_translate(fluent::trait_selection_dtcs_has_req_note); + multi_span.push_span_label( + self.span, + inline_fluent!("this has an implicit `'static` lifetime requirement"), + ); + multi_span.push_span_label( + self.ident.span, + inline_fluent!("calling this method introduces the `impl`'s `'static` requirement"), + ); + let msg = + diag.eagerly_translate(inline_fluent!("the used `impl` has a `'static` requirement")); diag.span_note(multi_span, msg); - let msg = diag.eagerly_translate(fluent::trait_selection_dtcs_suggestion); + let msg = diag.eagerly_translate(inline_fluent!( + "consider relaxing the implicit `'static` requirement" + )); diag.span_suggestion_verbose( self.span.shrink_to_hi(), msg, @@ -1059,12 +1285,26 @@ impl Subdiagnostic for DynTraitConstraintSuggestion { } #[derive(Diagnostic)] -#[diag(trait_selection_but_calling_introduces, code = E0772)] +#[diag("{$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$lifetime_kind -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` +} but calling `{$assoc_item}` introduces an implicit `'static` lifetime requirement", code = E0772)] pub struct ButCallingIntroduces { - #[label(trait_selection_label1)] + #[label( + "{$has_lifetime -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` + }" + )] pub param_ty_span: Span, #[primary_span] - #[label(trait_selection_label2)] + #[label("...is used and required to live as long as `'static` here because of an implicit lifetime bound on the {$has_impl_path -> + [true] `impl` of `{$impl_path}` + *[false] inherent `impl` + }")] pub cause_span: Span, pub has_param_name: bool, @@ -1087,32 +1327,61 @@ pub struct ReqIntroducedLocations { impl Subdiagnostic for ReqIntroducedLocations { fn add_to_diag(mut self, diag: &mut Diag<'_, G>) { for sp in self.spans { - self.span.push_span_label(sp, fluent::trait_selection_ril_introduced_here); + self.span.push_span_label(sp, inline_fluent!("`'static` requirement introduced here")); } if self.add_label { - self.span.push_span_label(self.fn_decl_span, fluent::trait_selection_ril_introduced_by); + self.span.push_span_label( + self.fn_decl_span, + inline_fluent!("requirement introduced by this return type"), + ); } - self.span.push_span_label(self.cause_span, fluent::trait_selection_ril_because_of); - let msg = diag.eagerly_translate(fluent::trait_selection_ril_static_introduced_by); + self.span.push_span_label( + self.cause_span, + inline_fluent!("because of this returned expression"), + ); + let msg = diag.eagerly_translate(inline_fluent!( + "\"`'static` lifetime requirement introduced by the return type" + )); diag.span_note(self.span, msg); } } #[derive(Diagnostic)] -#[diag(trait_selection_but_needs_to_satisfy, code = E0759)] +#[diag("{$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$has_lifetime -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` +} but it needs to satisfy a `'static` lifetime requirement", code = E0759)] pub struct ButNeedsToSatisfy { #[primary_span] pub sp: Span, - #[label(trait_selection_influencer)] + #[label( + "this data with {$has_lifetime -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` + }..." + )] pub influencer_point: Span, - #[label(trait_selection_used_here)] + #[label("...is used here...")] pub spans: Vec, - #[label(trait_selection_require)] + #[label( + "{$spans_empty -> + *[true] ...is used and required to live as long as `'static` here + [false] ...and is required to live as long as `'static` here + }" + )] pub require_span_as_label: Option, - #[note(trait_selection_require)] + #[note( + "{$spans_empty -> + *[true] ...is used and required to live as long as `'static` here + [false] ...and is required to live as long as `'static` here + }" + )] pub require_span_as_note: Option, - #[note(trait_selection_introduced_by_bound)] + #[note("`'static` lifetime requirement introduced by this bound")] pub bound: Option, pub has_param_name: bool, @@ -1123,7 +1392,7 @@ pub struct ButNeedsToSatisfy { } #[derive(Diagnostic)] -#[diag(trait_selection_outlives_content, code = E0312)] +#[diag("lifetime of reference outlives lifetime of borrowed content...", code = E0312)] pub struct OutlivesContent<'a> { #[primary_span] pub span: Span, @@ -1132,7 +1401,7 @@ pub struct OutlivesContent<'a> { } #[derive(Diagnostic)] -#[diag(trait_selection_outlives_bound, code = E0476)] +#[diag("lifetime of the source pointer does not outlive lifetime bound of the object type", code = E0476)] pub struct OutlivesBound<'a> { #[primary_span] pub span: Span, @@ -1141,7 +1410,7 @@ pub struct OutlivesBound<'a> { } #[derive(Diagnostic)] -#[diag(trait_selection_fulfill_req_lifetime, code = E0477)] +#[diag("the type `{$ty}` does not fulfill the required lifetime", code = E0477)] pub struct FulfillReqLifetime<'a> { #[primary_span] pub span: Span, @@ -1151,7 +1420,7 @@ pub struct FulfillReqLifetime<'a> { } #[derive(Diagnostic)] -#[diag(trait_selection_lf_bound_not_satisfied, code = E0478)] +#[diag("lifetime bound not satisfied", code = E0478)] pub struct LfBoundNotSatisfied<'a> { #[primary_span] pub span: Span, @@ -1160,7 +1429,7 @@ pub struct LfBoundNotSatisfied<'a> { } #[derive(Diagnostic)] -#[diag(trait_selection_ref_longer_than_data, code = E0491)] +#[diag("in type `{$ty}`, reference has a longer lifetime than the data it references", code = E0491)] pub struct RefLongerThanData<'a> { #[primary_span] pub span: Span, @@ -1172,7 +1441,7 @@ pub struct RefLongerThanData<'a> { #[derive(Subdiagnostic)] pub enum WhereClauseSuggestions { #[suggestion( - trait_selection_where_remove, + "remove the `where` clause", code = "", applicability = "machine-applicable", style = "verbose" @@ -1182,7 +1451,7 @@ pub enum WhereClauseSuggestions { span: Span, }, #[suggestion( - trait_selection_where_copy_predicates, + "copy the `where` clause predicates from the trait", code = "{space}where {trait_predicates}", applicability = "machine-applicable", style = "verbose" @@ -1198,7 +1467,7 @@ pub enum WhereClauseSuggestions { #[derive(Subdiagnostic)] pub enum SuggestRemoveSemiOrReturnBinding { #[multipart_suggestion( - trait_selection_srs_remove_and_box, + "consider removing this semicolon and boxing the expressions", applicability = "machine-applicable" )] RemoveAndBox { @@ -1214,7 +1483,7 @@ pub enum SuggestRemoveSemiOrReturnBinding { sp: Span, }, #[suggestion( - trait_selection_srs_remove, + "consider removing this semicolon", style = "short", code = "", applicability = "machine-applicable" @@ -1224,7 +1493,7 @@ pub enum SuggestRemoveSemiOrReturnBinding { sp: Span, }, #[suggestion( - trait_selection_srs_add, + "consider returning the local binding `{$ident}`", style = "verbose", code = "{code}", applicability = "maybe-incorrect" @@ -1235,7 +1504,7 @@ pub enum SuggestRemoveSemiOrReturnBinding { code: String, ident: Ident, }, - #[note(trait_selection_srs_add_one)] + #[note("consider returning one of these bindings")] AddOne { #[primary_span] spans: MultiSpan, @@ -1244,9 +1513,12 @@ pub enum SuggestRemoveSemiOrReturnBinding { #[derive(Subdiagnostic)] pub enum ConsiderAddingAwait { - #[help(trait_selection_await_both_futures)] + #[help("consider `await`ing on both `Future`s")] BothFuturesHelp, - #[multipart_suggestion(trait_selection_await_both_futures, applicability = "maybe-incorrect")] + #[multipart_suggestion( + "consider `await`ing on both `Future`s", + applicability = "maybe-incorrect" + )] BothFuturesSugg { #[suggestion_part(code = ".await")] first: Span, @@ -1254,7 +1526,7 @@ pub enum ConsiderAddingAwait { second: Span, }, #[suggestion( - trait_selection_await_future, + "consider `await`ing on the `Future`", code = ".await", style = "verbose", applicability = "maybe-incorrect" @@ -1263,13 +1535,13 @@ pub enum ConsiderAddingAwait { #[primary_span] span: Span, }, - #[note(trait_selection_await_note)] + #[note("calling an async function returns a future")] FutureSuggNote { #[primary_span] span: Span, }, #[multipart_suggestion( - trait_selection_await_future, + "consider `await`ing on the `Future`", style = "verbose", applicability = "maybe-incorrect" )] @@ -1281,69 +1553,79 @@ pub enum ConsiderAddingAwait { #[derive(Diagnostic)] pub enum PlaceholderRelationLfNotSatisfied { - #[diag(trait_selection_lf_bound_not_satisfied)] + #[diag("lifetime bound not satisfied")] HasBoth { #[primary_span] span: Span, - #[note(trait_selection_prlf_defined_with_sub)] + #[note("the lifetime `{$sub_symbol}` defined here...")] sub_span: Span, - #[note(trait_selection_prlf_must_outlive_with_sup)] + #[note("...must outlive the lifetime `{$sup_symbol}` defined here")] sup_span: Span, sub_symbol: Symbol, sup_symbol: Symbol, - #[note(trait_selection_prlf_known_limitation)] + #[note( + "this is a known limitation that will be removed in the future (see issue #100013 for more information)" + )] note: (), }, - #[diag(trait_selection_lf_bound_not_satisfied)] + #[diag("lifetime bound not satisfied")] HasSub { #[primary_span] span: Span, - #[note(trait_selection_prlf_defined_with_sub)] + #[note("the lifetime `{$sub_symbol}` defined here...")] sub_span: Span, - #[note(trait_selection_prlf_must_outlive_without_sup)] + #[note("...must outlive the lifetime defined here")] sup_span: Span, sub_symbol: Symbol, - #[note(trait_selection_prlf_known_limitation)] + #[note( + "this is a known limitation that will be removed in the future (see issue #100013 for more information)" + )] note: (), }, - #[diag(trait_selection_lf_bound_not_satisfied)] + #[diag("lifetime bound not satisfied")] HasSup { #[primary_span] span: Span, - #[note(trait_selection_prlf_defined_without_sub)] + #[note("the lifetime defined here...")] sub_span: Span, - #[note(trait_selection_prlf_must_outlive_with_sup)] + #[note("...must outlive the lifetime `{$sup_symbol}` defined here")] sup_span: Span, sup_symbol: Symbol, - #[note(trait_selection_prlf_known_limitation)] + #[note( + "this is a known limitation that will be removed in the future (see issue #100013 for more information)" + )] note: (), }, - #[diag(trait_selection_lf_bound_not_satisfied)] + #[diag("lifetime bound not satisfied")] HasNone { #[primary_span] span: Span, - #[note(trait_selection_prlf_defined_without_sub)] + #[note("the lifetime defined here...")] sub_span: Span, - #[note(trait_selection_prlf_must_outlive_without_sup)] + #[note("...must outlive the lifetime defined here")] sup_span: Span, - #[note(trait_selection_prlf_known_limitation)] + #[note( + "this is a known limitation that will be removed in the future (see issue #100013 for more information)" + )] note: (), }, - #[diag(trait_selection_lf_bound_not_satisfied)] + #[diag("lifetime bound not satisfied")] OnlyPrimarySpan { #[primary_span] span: Span, - #[note(trait_selection_prlf_known_limitation)] + #[note( + "this is a known limitation that will be removed in the future (see issue #100013 for more information)" + )] note: (), }, } #[derive(Diagnostic)] -#[diag(trait_selection_opaque_captures_lifetime, code = E0700)] +#[diag("hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds", code = E0700)] pub struct OpaqueCapturesLifetime<'tcx> { #[primary_span] pub span: Span, - #[label] + #[label("opaque type defined here")] pub opaque_ty_span: Span, pub opaque_ty: Ty<'tcx>, } @@ -1351,7 +1633,7 @@ pub struct OpaqueCapturesLifetime<'tcx> { #[derive(Subdiagnostic)] pub enum FunctionPointerSuggestion<'a> { #[suggestion( - trait_selection_fps_use_ref, + "consider using a reference", code = "&", style = "verbose", applicability = "maybe-incorrect" @@ -1361,7 +1643,7 @@ pub enum FunctionPointerSuggestion<'a> { span: Span, }, #[suggestion( - trait_selection_fps_remove_ref, + "consider removing the reference", code = "{fn_name}", style = "verbose", applicability = "maybe-incorrect" @@ -1373,7 +1655,7 @@ pub enum FunctionPointerSuggestion<'a> { fn_name: String, }, #[suggestion( - trait_selection_fps_cast, + "consider casting to a fn pointer", code = "&({fn_name} as {sig})", style = "verbose", applicability = "maybe-incorrect" @@ -1387,7 +1669,7 @@ pub enum FunctionPointerSuggestion<'a> { sig: Binder<'a, FnSig<'a>>, }, #[suggestion( - trait_selection_fps_cast, + "consider casting to a fn pointer", code = " as {sig}", style = "verbose", applicability = "maybe-incorrect" @@ -1399,7 +1681,7 @@ pub enum FunctionPointerSuggestion<'a> { sig: Binder<'a, FnSig<'a>>, }, #[suggestion( - trait_selection_fps_cast_both, + "consider casting both fn items to fn pointers using `as {$expected_sig}`", code = " as {found_sig}", style = "hidden", applicability = "maybe-incorrect" @@ -1412,7 +1694,7 @@ pub enum FunctionPointerSuggestion<'a> { expected_sig: Binder<'a, FnSig<'a>>, }, #[suggestion( - trait_selection_fps_cast_both, + "consider casting both fn items to fn pointers using `as {$expected_sig}`", code = "&({fn_name} as {found_sig})", style = "hidden", applicability = "maybe-incorrect" @@ -1429,21 +1711,21 @@ pub enum FunctionPointerSuggestion<'a> { } #[derive(Subdiagnostic)] -#[note(trait_selection_fps_items_are_distinct)] +#[note("fn items are distinct from fn pointers")] pub struct FnItemsAreDistinct; #[derive(Subdiagnostic)] -#[note(trait_selection_fn_uniq_types)] +#[note("different fn items have unique types, even if their signatures are the same")] pub struct FnUniqTypes; #[derive(Subdiagnostic)] -#[help(trait_selection_fn_consider_casting)] +#[help("consider casting the fn item to a fn pointer: `{$casting}`")] pub struct FnConsiderCasting { pub casting: String, } #[derive(Subdiagnostic)] -#[help(trait_selection_fn_consider_casting_both)] +#[help("consider casting both fn items to fn pointers using `as {$sig}`")] pub struct FnConsiderCastingBoth<'a> { pub sig: Binder<'a, FnSig<'a>>, } @@ -1451,7 +1733,7 @@ pub struct FnConsiderCastingBoth<'a> { #[derive(Subdiagnostic)] pub enum SuggestAccessingField<'a> { #[suggestion( - trait_selection_suggest_accessing_field, + "you might have meant to use field `{$name}` whose type is `{$ty}`", code = "{snippet}.{name}", applicability = "maybe-incorrect", style = "verbose" @@ -1464,7 +1746,7 @@ pub enum SuggestAccessingField<'a> { ty: Ty<'a>, }, #[suggestion( - trait_selection_suggest_accessing_field, + "you might have meant to use field `{$name}` whose type is `{$ty}`", code = "unsafe {{ {snippet}.{name} }}", applicability = "maybe-incorrect", style = "verbose" @@ -1479,7 +1761,10 @@ pub enum SuggestAccessingField<'a> { } #[derive(Subdiagnostic)] -#[multipart_suggestion(trait_selection_stp_wrap_one, applicability = "maybe-incorrect")] +#[multipart_suggestion( + "try wrapping the pattern in `{$variant}`", + applicability = "maybe-incorrect" +)] pub struct SuggestTuplePatternOne { pub variant: String, #[suggestion_part(code = "{variant}(")] @@ -1497,7 +1782,9 @@ pub struct SuggestTuplePatternMany { impl Subdiagnostic for SuggestTuplePatternMany { fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("path", self.path); - let message = diag.eagerly_translate(fluent::trait_selection_stp_wrap_many); + let message = diag.eagerly_translate(inline_fluent!( + "try wrapping the pattern in a variant of `{$path}`" + )); diag.multipart_suggestions( message, self.compatible_variants.into_iter().map(|variant| { @@ -1514,7 +1801,7 @@ impl Subdiagnostic for SuggestTuplePatternMany { #[derive(Subdiagnostic)] pub enum TypeErrorAdditionalDiags { #[suggestion( - trait_selection_meant_byte_literal, + "if you meant to write a byte literal, prefix with `b`", code = "b'{code}'", applicability = "machine-applicable" )] @@ -1524,7 +1811,7 @@ pub enum TypeErrorAdditionalDiags { code: String, }, #[suggestion( - trait_selection_meant_char_literal, + "if you meant to write a `char` literal, use single quotes", code = "'{code}'", applicability = "machine-applicable" )] @@ -1533,7 +1820,10 @@ pub enum TypeErrorAdditionalDiags { span: Span, code: String, }, - #[multipart_suggestion(trait_selection_meant_str_literal, applicability = "machine-applicable")] + #[multipart_suggestion( + "if you meant to write a string literal, use double quotes", + applicability = "machine-applicable" + )] MeantStrLiteral { #[suggestion_part(code = "\"")] start: Span, @@ -1541,7 +1831,7 @@ pub enum TypeErrorAdditionalDiags { end: Span, }, #[suggestion( - trait_selection_consider_specifying_length, + "consider specifying the actual array length", code = "{length}", applicability = "maybe-incorrect" )] @@ -1550,10 +1840,10 @@ pub enum TypeErrorAdditionalDiags { span: Span, length: u64, }, - #[note(trait_selection_try_cannot_convert)] + #[note("`?` operator cannot convert from `{$found}` to `{$expected}`")] TryCannotConvert { found: String, expected: String }, #[suggestion( - trait_selection_tuple_trailing_comma, + "use a trailing comma to create a tuple with one element", code = ",", applicability = "machine-applicable" )] @@ -1562,7 +1852,7 @@ pub enum TypeErrorAdditionalDiags { span: Span, }, #[multipart_suggestion( - trait_selection_tuple_trailing_comma, + "use a trailing comma to create a tuple with one element", applicability = "machine-applicable" )] TupleAlsoParentheses { @@ -1572,7 +1862,7 @@ pub enum TypeErrorAdditionalDiags { span_high: Span, }, #[suggestion( - trait_selection_suggest_add_let_for_letchains, + "consider adding `let`", style = "verbose", applicability = "machine-applicable", code = "let " @@ -1585,66 +1875,71 @@ pub enum TypeErrorAdditionalDiags { #[derive(Diagnostic)] pub enum ObligationCauseFailureCode { - #[diag(trait_selection_oc_method_compat, code = E0308)] + #[diag("method not compatible with trait", code = E0308)] MethodCompat { #[primary_span] span: Span, #[subdiagnostic] subdiags: Vec, }, - #[diag(trait_selection_oc_type_compat, code = E0308)] + #[diag("type not compatible with trait", code = E0308)] TypeCompat { #[primary_span] span: Span, #[subdiagnostic] subdiags: Vec, }, - #[diag(trait_selection_oc_const_compat, code = E0308)] + #[diag("const not compatible with trait", code = E0308)] ConstCompat { #[primary_span] span: Span, #[subdiagnostic] subdiags: Vec, }, - #[diag(trait_selection_oc_try_compat, code = E0308)] + #[diag("`?` operator has incompatible types", code = E0308)] TryCompat { #[primary_span] span: Span, #[subdiagnostic] subdiags: Vec, }, - #[diag(trait_selection_oc_match_compat, code = E0308)] + #[diag("`match` arms have incompatible types", code = E0308)] MatchCompat { #[primary_span] span: Span, #[subdiagnostic] subdiags: Vec, }, - #[diag(trait_selection_oc_if_else_different, code = E0308)] + #[diag("`if` and `else` have incompatible types", code = E0308)] IfElseDifferent { #[primary_span] span: Span, #[subdiagnostic] subdiags: Vec, }, - #[diag(trait_selection_oc_no_else, code = E0317)] + #[diag("`if` may be missing an `else` clause", code = E0317)] NoElse { #[primary_span] span: Span, }, - #[diag(trait_selection_oc_no_diverge, code = E0308)] + #[diag("`else` clause of `let...else` does not diverge", code = E0308)] NoDiverge { #[primary_span] span: Span, #[subdiagnostic] subdiags: Vec, }, - #[diag(trait_selection_oc_fn_main_correct_type, code = E0580)] + #[diag("`main` function has wrong type", code = E0580)] FnMainCorrectType { #[primary_span] span: Span, }, - #[diag(trait_selection_oc_fn_lang_correct_type, code = E0308)] + #[diag( + "{$lang_item_name -> + [panic_impl] `#[panic_handler]` + *[lang_item_name] lang item `{$lang_item_name}` + } function has wrong type" + , code = E0308)] FnLangCorrectType { #[primary_span] span: Span, @@ -1652,40 +1947,40 @@ pub enum ObligationCauseFailureCode { subdiags: Vec, lang_item_name: Symbol, }, - #[diag(trait_selection_oc_intrinsic_correct_type, code = E0308)] + #[diag("intrinsic has wrong type", code = E0308)] IntrinsicCorrectType { #[primary_span] span: Span, #[subdiagnostic] subdiags: Vec, }, - #[diag(trait_selection_oc_method_correct_type, code = E0308)] + #[diag("mismatched `self` parameter type", code = E0308)] MethodCorrectType { #[primary_span] span: Span, #[subdiagnostic] subdiags: Vec, }, - #[diag(trait_selection_oc_closure_selfref, code = E0644)] + #[diag("closure/coroutine type that references itself", code = E0644)] ClosureSelfref { #[primary_span] span: Span, }, - #[diag(trait_selection_oc_cant_coerce_force_inline, code = E0308)] + #[diag("cannot coerce functions which must be inlined to function pointers", code = E0308)] CantCoerceForceInline { #[primary_span] span: Span, #[subdiagnostic] subdiags: Vec, }, - #[diag(trait_selection_oc_cant_coerce_intrinsic, code = E0308)] + #[diag("cannot coerce intrinsics to function pointers", code = E0308)] CantCoerceIntrinsic { #[primary_span] span: Span, #[subdiagnostic] subdiags: Vec, }, - #[diag(trait_selection_oc_generic, code = E0308)] + #[diag("mismatched types", code = E0308)] Generic { #[primary_span] span: Span, @@ -1697,7 +1992,7 @@ pub enum ObligationCauseFailureCode { #[derive(Subdiagnostic)] pub enum AddPreciseCapturing { #[suggestion( - trait_selection_precise_capturing_new, + "add a `use<...>` bound to explicitly capture `{$new_lifetime}`", style = "verbose", code = " + use<{concatenated_bounds}>", applicability = "machine-applicable" @@ -1709,7 +2004,7 @@ pub enum AddPreciseCapturing { concatenated_bounds: String, }, #[suggestion( - trait_selection_precise_capturing_existing, + "add `{$new_lifetime}` to the `use<...>` bound to explicitly capture it", style = "verbose", code = "{pre}{new_lifetime}{post}", applicability = "machine-applicable" @@ -1733,13 +2028,13 @@ impl Subdiagnostic for AddPreciseCapturingAndParams { fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("new_lifetime", self.new_lifetime); diag.multipart_suggestion_verbose( - fluent::trait_selection_precise_capturing_new_but_apit, + inline_fluent!("add a `use<...>` bound to explicitly capture `{$new_lifetime}` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate"), self.suggs, Applicability::MaybeIncorrect, ); diag.span_note( self.apit_spans, - fluent::trait_selection_warn_removing_apit_params_for_undercapture, + inline_fluent!("you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable"), ); } } @@ -1880,26 +2175,31 @@ impl Subdiagnostic for AddPreciseCapturingForOvercapture { Applicability::MaybeIncorrect }; diag.multipart_suggestion_verbose( - fluent::trait_selection_precise_capturing_overcaptures, + inline_fluent!( + "use the precise capturing `use<...>` syntax to make the captures explicit" + ), self.suggs, applicability, ); if !self.apit_spans.is_empty() { diag.span_note( self.apit_spans, - fluent::trait_selection_warn_removing_apit_params_for_overcapture, + inline_fluent!("you could use a `use<...>` bound to explicitly specify captures, but argument-position `impl Trait`s are not nameable"), ); } } } #[derive(Diagnostic)] -#[diag(trait_selection_opaque_type_non_generic_param, code = E0792)] +#[diag("expected generic {$kind} parameter, found `{$arg}`", code = E0792)] pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> { pub arg: GenericArg<'tcx>, pub kind: &'a str, #[primary_span] pub span: Span, - #[label] + #[label("{STREQ($arg, \"'static\") -> + [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type + *[other] this generic parameter must be used with a generic {$kind} parameter + }")] pub param_span: Span, } diff --git a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs index 3471036256db..8e86b8fae157 100644 --- a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs @@ -1,11 +1,10 @@ -use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, Subdiagnostic}; +use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, Subdiagnostic, inline_fluent}; use rustc_hir::def_id::LocalDefId; use rustc_middle::bug; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{Span, kw}; use crate::error_reporting::infer::nice_region_error::find_anon_type; -use crate::fluent_generated as fluent; struct DescriptionCtx<'a> { span: Option, @@ -170,7 +169,40 @@ impl Subdiagnostic for RegionExplanation<'_> { diag.arg("desc_kind", self.desc.kind); diag.arg("desc_arg", self.desc.arg); - let msg = diag.eagerly_translate(fluent::trait_selection_region_explanation); + let msg = diag.eagerly_translate(inline_fluent!( + "{$pref_kind -> +*[should_not_happen] [{$pref_kind}] +[ref_valid_for] ...the reference is valid for +[content_valid_for] ...but the borrowed content is only valid for +[type_obj_valid_for] object type is valid for +[source_pointer_valid_for] source pointer is only valid for +[type_satisfy] type must satisfy +[type_outlive] type must outlive +[lf_param_instantiated_with] lifetime parameter instantiated with +[lf_param_must_outlive] but lifetime parameter must outlive +[lf_instantiated_with] lifetime instantiated with +[lf_must_outlive] but lifetime must outlive +[pointer_valid_for] the pointer is valid for +[data_valid_for] but the referenced data is only valid for +[empty] {\"\"} +}{$pref_kind -> +[empty] {\"\"} +*[other] {\" \"} +}{$desc_kind -> +*[should_not_happen] [{$desc_kind}] +[restatic] the static lifetime +[revar] lifetime {$desc_arg} +[as_defined] the lifetime `{$desc_arg}` as defined here +[as_defined_anon] the anonymous lifetime as defined here +[defined_here] the anonymous lifetime defined here +[defined_here_reg] the lifetime `{$desc_arg}` as defined here +}{$suff_kind -> +*[should_not_happen] [{$suff_kind}] +[empty]{\"\"} +[continues] ... +[req_by_binding] {\" \"}as required by this binding +}" + )); diag.restore_args(); if let Some(span) = self.desc.span { diag.span_note(span, msg); diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 561a95bad50c..708e0b9e8497 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -11,7 +11,7 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(default_field_values)] @@ -34,5 +34,3 @@ pub mod opaque_types; pub mod regions; pub mod solve; pub mod traits; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index ea8360c10b6f..b8da64b9729a 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -443,9 +443,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { pub(crate) fn visit_with>(&self, visitor: &mut V) -> V::Result { if self.depth < visitor.config().max_depth { try_visit!(visitor.visit_goal(self)); + V::Result::output() + } else { + visitor.on_recursion_limit() } - - V::Result::output() } } @@ -460,6 +461,10 @@ pub trait ProofTreeVisitor<'tcx> { } fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> Self::Result; + + fn on_recursion_limit(&mut self) -> Self::Result { + Self::Result::output() + } } #[extension(pub trait InferCtxtProofTreeExt<'tcx>)] diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 9f59f6c59250..fc628e78a3e2 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -8,8 +8,10 @@ use std::fmt::Debug; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::{Diag, EmissionGuarantee}; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; +use rustc_hir::find_attr; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::PredicateObligations; use rustc_macros::{TypeFoldable, TypeVisitable}; @@ -23,7 +25,7 @@ use rustc_middle::ty::{ }; pub use rustc_next_trait_solver::coherence::*; use rustc_next_trait_solver::solve::SolverDelegateEvalExt; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument, warn}; use super::ObligationCtxt; @@ -758,10 +760,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { } = cand.kind() && let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) { - let message = infcx - .tcx - .get_attr(def_id, sym::rustc_reservation_impl) - .and_then(|a| a.value_str()); + let message = find_attr!(infcx.tcx.get_all_attrs(def_id), AttributeKind::RustcReservationImpl(_, message) => *message); if let Some(message) = message { self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { message }); } diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 9371b55b6363..512973e45287 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -7,10 +7,9 @@ use std::ops::ControlFlow; use rustc_errors::FatalError; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem, find_attr}; +use rustc_hir::{self as hir, LangItem}; use rustc_middle::query::Providers; use rustc_middle::ty::{ self, EarlyBinder, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, @@ -333,7 +332,7 @@ pub fn dyn_compatibility_violations_for_assoc_item( if tcx.features().min_generic_const_args() { if !tcx.generics_of(item.def_id).is_own_empty() { errors.push(AssocConstViolation::Generic); - } else if !find_attr!(tcx.get_all_attrs(item.def_id), AttributeKind::TypeConst(_)) { + } else if !tcx.is_type_const(item.def_id) { errors.push(AssocConstViolation::NonType); } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 4e027a301cc8..08f1d947dfb5 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -681,6 +681,20 @@ pub fn try_evaluate_const<'tcx>( (args, typing_env) } + Some(ty::AnonConstKind::OGCA) => { + if infcx.typing_mode() != TypingMode::PostAnalysis { + // OGCA anon consts should be treated as always having generics + // during anything before codegen (or maybe MIR opts too). + return Err(EvaluateConstErr::HasGenericsOrInfers); + } + + if uv.args.has_non_region_param() || uv.args.has_non_region_infer() { + return Err(EvaluateConstErr::HasGenericsOrInfers); + } + + let typing_env = ty::TypingEnv::fully_monomorphized(); + (uv.args, typing_env) + } Some(ty::AnonConstKind::MCG) | Some(ty::AnonConstKind::NonTypeSystem) | None => { // We are only dealing with "truly" generic/uninferred constants here: // - GCEConsts have been handled separately diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 24854990fe71..3f4c75a5b133 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -1,6 +1,7 @@ //! Deeply normalize types using the old trait solver. use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::inline_fluent; use rustc_hir::def::DefKind; use rustc_infer::infer::at::At; use rustc_infer::infer::{InferCtxt, InferOk}; @@ -294,7 +295,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { self.cause.span, false, |diag| { - diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow); + diag.note(inline_fluent!("in case this is a recursive type alias, consider using a struct, enum, or union instead")); }, ); } @@ -452,7 +453,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx // been emitted earlier in compilation. // // That's because we can only end up with an Unevaluated ty::Const for a const item - // if it was marked with `#[type_const]`. Using this attribute without the mgca + // if it was marked with `type const`. Using this attribute without the mgca // feature gate causes a parse error. let ct = match tcx.def_kind(uv.def) { DefKind::AssocConst => match tcx.def_kind(tcx.parent(uv.def)) { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 78e7aef78f1b..91a025131855 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -1,3 +1,4 @@ +use rustc_hir::def::DefKind; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::traits::Obligation; use rustc_middle::traits::query::NoSolution; @@ -96,6 +97,26 @@ fn relate_mir_and_user_args<'tcx>( let tcx = ocx.infcx.tcx; let cause = ObligationCause::dummy_with_span(span); + // For IACs, the user args are in the format [SelfTy, GAT_args...] but type_of expects [impl_args..., GAT_args...]. + // We need to infer the impl args by equating the impl's self type with the user-provided self type. + let is_inherent_assoc_const = tcx.def_kind(def_id) == DefKind::AssocConst + && tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false } + && tcx.is_type_const(def_id); + + let args = if is_inherent_assoc_const { + let impl_def_id = tcx.parent(def_id); + let impl_args = ocx.infcx.fresh_args_for_item(span, impl_def_id); + let impl_self_ty = + ocx.normalize(&cause, param_env, tcx.type_of(impl_def_id).instantiate(tcx, impl_args)); + let user_self_ty = ocx.normalize(&cause, param_env, args[0].expect_ty()); + ocx.eq(&cause, param_env, impl_self_ty, user_self_ty)?; + + let gat_args = &args[1..]; + tcx.mk_args_from_iter(impl_args.iter().chain(gat_args.iter().copied())) + } else { + args + }; + let ty = tcx.type_of(def_id).instantiate(tcx, args); let ty = ocx.normalize(&cause, param_env, ty); debug!("relate_type_and_user_type: ty of def-id is {:?}", ty); diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index f5bf74a79919..935834b832f2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -312,7 +312,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `async`/`gen` constructs get lowered to a special kind of coroutine that // should *not* `impl Coroutine`. ty::Coroutine(did, ..) if self.tcx().is_general_coroutine(*did) => { - debug!(?self_ty, ?obligation, "assemble_coroutine_candidates",); + debug!(?self_ty, ?obligation, "assemble_coroutine_candidates"); candidates.vec.push(CoroutineCandidate); } @@ -334,7 +334,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // async constructs get lowered to a special kind of coroutine that // should directly `impl Future`. if self.tcx().coroutine_is_async(*did) { - debug!(?self_ty, ?obligation, "assemble_future_candidates",); + debug!(?self_ty, ?obligation, "assemble_future_candidates"); candidates.vec.push(FutureCandidate); } @@ -352,7 +352,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let ty::Coroutine(did, ..) = self_ty.kind() && self.tcx().coroutine_is_gen(*did) { - debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); + debug!(?self_ty, ?obligation, "assemble_iterator_candidates"); candidates.vec.push(IteratorCandidate); } @@ -378,7 +378,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // gen constructs get lowered to a special kind of coroutine that // should directly `impl AsyncIterator`. if self.tcx().coroutine_is_async_gen(did) { - debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); + debug!(?self_ty, ?obligation, "assemble_iterator_candidates"); // Can only confirm this candidate if we have constrained // the `Yield` type to at least `Poll>`.. diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 4f65b30775ed..b0e045274d0d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1246,6 +1246,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) } + /// This trait is indirectly exposed on stable, so do *not* extend the set of types that + /// implement the trait without FCP! fn confirm_bikeshed_guaranteed_no_drop_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 787dd4ea6254..3c3160bc533e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -12,9 +12,9 @@ use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Diag, EmissionGuarantee}; -use rustc_hir as hir; -use rustc_hir::LangItem; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_infer::infer::BoundRegionConversionTime::{self, HigherRankedType}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::at::ToTrace; @@ -33,7 +33,7 @@ use rustc_middle::ty::{ may_use_unstable_feature, }; use rustc_next_trait_solver::solve::AliasBoundKind; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use tracing::{debug, instrument, trace}; use self::EvaluationResult::*; @@ -1223,7 +1223,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { && self.match_fresh_trait_preds(stack.fresh_trait_pred, prev.fresh_trait_pred) }) { - debug!("evaluate_stack --> unbound argument, recursive --> giving up",); + debug!("evaluate_stack --> unbound argument, recursive --> giving up"); return Ok(EvaluatedToAmbigStackDependent); } @@ -1445,8 +1445,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { && let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) { if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes { - let message = - tcx.get_attr(def_id, sym::rustc_reservation_impl).and_then(|a| a.value_str()); + let message = find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcReservationImpl(_, message) => *message); if let Some(message) = message { debug!( "filter_reservation_impls: \ diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 584c8e2a27c8..bd48068eb579 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -12,7 +12,7 @@ use rustc_span::DUMMY_SP; use smallvec::{SmallVec, smallvec}; use tracing::debug; -use crate::traits::{impossible_predicates, is_vtable_safe_method}; +use crate::traits::is_vtable_safe_method; #[derive(Clone, Debug)] pub enum VtblSegment<'tcx> { @@ -271,11 +271,7 @@ fn vtable_entries<'tcx>( // do not hold for this particular set of type parameters. // Note that this method could then never be called, so we // do not want to try and codegen it, in that case (see #23435). - let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args); - if impossible_predicates( - tcx, - predicates.map(|(predicate, _)| predicate).collect(), - ) { + if tcx.instantiate_and_check_impossible_predicates((def_id, args)) { debug!("vtable_entries: predicates do not hold"); return VtblEntry::Vacant; } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index d383cb9d91dd..358f14221a0d 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -951,6 +951,34 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { ty::Binder::dummy(ty::PredicateKind::DynCompatible(principal)), )); } + + if !t.has_escaping_bound_vars() { + for projection in data.projection_bounds() { + let pred_binder = projection + .with_self_ty(tcx, t) + .map_bound(|p| { + p.term.as_const().map(|ct| { + let assoc_const_ty = tcx + .type_of(p.projection_term.def_id) + .instantiate(tcx, p.projection_term.args); + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType( + ct, + assoc_const_ty, + )) + }) + }) + .transpose(); + if let Some(pred_binder) = pred_binder { + self.out.push(traits::Obligation::with_depth( + tcx, + self.cause(ObligationCauseCode::WellFormed(None)), + self.recursion_depth, + self.param_env, + pred_binder, + )); + } + } + } } // Inference variables are the complicated case, since we don't diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 62f3667ad7f4..bebc0707e26d 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -764,14 +764,20 @@ fn layout_of_uncached<'tcx>( } ty::Alias(..) => { - // NOTE(eddyb) `layout_of` query should've normalized these away, - // if that was possible, so there's no reason to try again here. - let err = if ty.has_param() { + // In case we're still in a generic context, aliases might be rigid. E.g. + // if we've got a `T: Trait` where-bound, `T::Assoc` cannot be normalized + // in the current context. + // + // For some builtin traits, generic aliases can be rigid even in an empty environment, + // e.g. `::Metadata`. + // + // Due to trivial bounds, this can even be the case if the alias does not reference + // any generic parameters, e.g. a `for<'a> u32: Trait<'a>` where-bound means that + // `>::Assoc` is rigid. + let err = if ty.has_param() || !cx.typing_env.param_env.caller_bounds().is_empty() { LayoutError::TooGeneric(ty) } else { - // This is only reachable with unsatisfiable predicates. For example, if we have - // `u8: Iterator`, then we can't compute the layout of `::Item`. - LayoutError::Unknown(ty) + unreachable!("invalid rigid alias in layout_of after normalization: {ty:?}"); }; return Err(error(cx, err)); } diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 9f8f3b240890..b7f73fd5570e 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -5,7 +5,7 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(if_let_guard)] diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 0ef435b1a0e2..06eef7e95145 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -1,13 +1,14 @@ //! Check whether a type has (potentially) non-trivial drop glue. use rustc_data_structures::fx::FxHashSet; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; +use rustc_hir::find_attr; use rustc_hir::limit::Limit; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::util::{AlwaysRequiresDrop, needs_drop_components}; use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt}; -use rustc_span::sym; use tracing::{debug, instrument}; use crate::errors::NeedsDropOverflow; @@ -396,8 +397,7 @@ fn adt_consider_insignificant_dtor<'tcx>( tcx: TyCtxt<'tcx>, ) -> impl Fn(ty::AdtDef<'tcx>) -> Option { move |adt_def: ty::AdtDef<'tcx>| { - let is_marked_insig = tcx.has_attr(adt_def.did(), sym::rustc_insignificant_dtor); - if is_marked_insig { + if find_attr!(tcx.get_all_attrs(adt_def.did()), AttributeKind::RustcInsignificantDtor) { // In some cases like `std::collections::HashMap` where the struct is a wrapper around // a type that is a Drop type, and the wrapped type (eg: `hashbrown::HashMap`) lies // outside stdlib, we might choose to still annotate the wrapper (std HashMap) with diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index b215230ea443..1c39f31469b1 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -200,3 +200,23 @@ impl ValTreeKind { } } } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] +pub enum AnonConstKind { + /// `feature(generic_const_exprs)` anon consts are allowed to use arbitrary generic parameters in scope + GCE, + /// stable `min_const_generics` anon consts are not allowed to use any generic parameters + MCG, + /// `feature(opaque_generic_const_args)` anon consts are allowed to use arbitrary + /// generic parameters in scope, but only if they syntactically reference them. + OGCA, + /// anon consts used as the length of a repeat expr are syntactically allowed to use generic parameters + /// but must not depend on the actual instantiation. See #76200 for more information + RepeatExprCount, + /// anon consts outside of the type system, e.g. enum discriminants + NonTypeSystem, +} diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 89cb236d38c6..c9580d589d21 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -13,7 +13,7 @@ use crate::fold::{TypeFoldable, TypeSuperFoldable}; use crate::relate::Relate; use crate::solve::{AdtDestructorKind, SizedTraitKind}; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; -use crate::{self as ty, CollectAndApply, Interner, UpcastFrom}; +use crate::{self as ty, ClauseKind, CollectAndApply, Interner, PredicateKind, UpcastFrom}; pub trait Ty>: Copy @@ -478,8 +478,27 @@ pub trait Predicate>: } } - // FIXME: Eventually uplift the impl out of rustc and make this defaulted. - fn allow_normalization(self) -> bool; + fn allow_normalization(self) -> bool { + match self.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) => { + false + } + PredicateKind::Clause(ClauseKind::Trait(_)) + | PredicateKind::Clause(ClauseKind::HostEffect(..)) + | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) + | PredicateKind::Clause(ClauseKind::TypeOutlives(_)) + | PredicateKind::Clause(ClauseKind::Projection(_)) + | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) + | PredicateKind::DynCompatible(_) + | PredicateKind::Subtype(_) + | PredicateKind::Coerce(_) + | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) + | PredicateKind::ConstEquate(_, _) + | PredicateKind::NormalizesTo(..) + | PredicateKind::Ambiguous => true, + } + } } pub trait Clause>: diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 59ae6733fb84..8f446cdfba6d 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -205,6 +205,7 @@ pub trait Interner: fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) -> ty::EarlyBinder; fn const_of_item(self, def_id: Self::DefId) -> ty::EarlyBinder; + fn anon_const_kind(self, def_id: Self::DefId) -> ty::AnonConstKind; type AdtDef: AdtDef; fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef; diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 97aafbc7b699..4ddfcde57280 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -581,6 +581,40 @@ impl BinaryHeap { pub fn with_capacity_in(capacity: usize, alloc: A) -> BinaryHeap { BinaryHeap { data: Vec::with_capacity_in(capacity, alloc) } } + + /// Creates a `BinaryHeap` using the supplied `vec`. This does not rebuild the heap, + /// so `vec` must already be a max-heap. + /// + /// # Safety + /// + /// The supplied `vec` must be a max-heap, i.e. for all indices `0 < i < vec.len()`, + /// `vec[(i - 1) / 2] >= vec[i]`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(binary_heap_from_raw_vec)] + /// + /// use std::collections::BinaryHeap; + /// let heap = BinaryHeap::from([1, 2, 3]); + /// let vec = heap.into_vec(); + /// + /// // Safety: vec is the output of heap.from_vec(), so is a max-heap. + /// let mut new_heap = unsafe { + /// BinaryHeap::from_raw_vec(vec) + /// }; + /// assert_eq!(new_heap.pop(), Some(3)); + /// assert_eq!(new_heap.pop(), Some(2)); + /// assert_eq!(new_heap.pop(), Some(1)); + /// assert_eq!(new_heap.pop(), None); + /// ``` + #[unstable(feature = "binary_heap_from_raw_vec", issue = "152500")] + #[must_use] + pub unsafe fn from_raw_vec(vec: Vec) -> BinaryHeap { + BinaryHeap { data: vec } + } } impl BinaryHeap { diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index d6dcba7107a9..fba967c04895 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -103,6 +103,7 @@ use crate::vec::Vec; /// and other memory errors. #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)] #[rustc_diagnostic_item = "cstring_type"] +#[rustc_insignificant_dtor] #[stable(feature = "alloc_c_string", since = "1.64.0")] pub struct CString { // Invariant 1: the slice ends with a zero byte and has a length of at least one. @@ -694,7 +695,6 @@ impl CString { // memory-unsafe code from working by accident. Inline // to prevent LLVM from optimizing it away in debug builds. #[stable(feature = "cstring_drop", since = "1.13.0")] -#[rustc_insignificant_dtor] impl Drop for CString { #[inline] fn drop(&mut self) { diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index f7167650635d..0e0c2fcd8b99 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -89,7 +89,6 @@ #![feature(allocator_api)] #![feature(array_into_iter_constructors)] #![feature(ascii_char)] -#![feature(assert_matches)] #![feature(async_fn_traits)] #![feature(async_iterator)] #![feature(box_vec_non_null)] diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index e772ac25a95c..8a3326c7d76a 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -411,9 +411,8 @@ impl str { fn map_uppercase_sigma(from: &str, i: usize) -> char { // See https://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G33992 // for the definition of `Final_Sigma`. - debug_assert!('Σ'.len_utf8() == 2); let is_word_final = case_ignorable_then_cased(from[..i].chars().rev()) - && !case_ignorable_then_cased(from[i + 2..].chars()); + && !case_ignorable_then_cased(from[i + const { 'Σ'.len_utf8() }..].chars()); if is_word_final { 'ς' } else { 'σ' } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index a5e4fab916ab..dc82357dd146 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -3270,7 +3270,7 @@ impl Weak { // Acquire is necessary for the success case to synchronise with `Arc::new_cyclic`, when the inner // value can be initialized after `Weak` references have already been created. In that case, we // expect to observe the fully initialized value. - if self.inner()?.strong.fetch_update(Acquire, Relaxed, checked_increment).is_ok() { + if self.inner()?.strong.try_update(Acquire, Relaxed, checked_increment).is_ok() { // SAFETY: pointer is not null, verified in checked_increment unsafe { Some(Arc::from_inner_in(self.ptr, self.alloc.clone())) } } else { diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index fe14480102e3..296f76d7c073 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -16,7 +16,6 @@ // tidy-alphabetical-start #![feature(allocator_api)] #![feature(array_into_iter_constructors)] -#![feature(assert_matches)] #![feature(box_vec_non_null)] #![feature(char_internals)] #![feature(const_alloc_error)] diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index e15c86496cf1..b7b8336ee429 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -3,7 +3,6 @@ #![feature(const_heap)] #![feature(deque_extend_front)] #![feature(iter_array_chunks)] -#![feature(assert_matches)] #![feature(wtf8_internals)] #![feature(cow_is_borrowed)] #![feature(core_intrinsics)] diff --git a/library/compiler-builtins/.github/workflows/main.yaml b/library/compiler-builtins/.github/workflows/main.yaml index 699a9c417dde..3fed58f2a207 100644 --- a/library/compiler-builtins/.github/workflows/main.yaml +++ b/library/compiler-builtins/.github/workflows/main.yaml @@ -70,12 +70,14 @@ jobs: os: ubuntu-24.04 - target: powerpc64le-unknown-linux-gnu os: ubuntu-24.04 - - target: powerpc64le-unknown-linux-gnu - os: ubuntu-24.04-ppc64le + # - target: powerpc64le-unknown-linux-gnu + # os: ubuntu-24.04-ppc64le + # # FIXME(rust#151807): remove once PPC builds work again. + # channel: nightly-2026-01-23 - target: riscv64gc-unknown-linux-gnu os: ubuntu-24.04 - - target: s390x-unknown-linux-gnu - os: ubuntu-24.04-s390x + # - target: s390x-unknown-linux-gnu + # os: ubuntu-24.04-s390x - target: thumbv6m-none-eabi os: ubuntu-24.04 - target: thumbv7em-none-eabi @@ -228,7 +230,27 @@ jobs: # poorly with build scripts) cargo build -p compiler_builtins -p libm \ --target etc/thumbv7em-none-eabi-renamed.json \ - -Zbuild-std=core + -Zbuild-std=core \ + -Zjson-target-spec + + # FIXME: move this target to test job once https://github.com/rust-lang/rust/pull/150138 merged. + build-thumbv6k: + name: Build thumbv6k + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: | + rustup update nightly --no-self-update + rustup default nightly + rustup component add rust-src + - uses: Swatinem/rust-cache@v2 + - run: | + cargo build -p compiler_builtins -p libm \ + --target etc/thumbv6-none-eabi.json \ + -Zbuild-std=core \ + -Zjson-target-spec benchmarks: name: Benchmarks @@ -354,6 +376,7 @@ jobs: needs: - benchmarks - build-custom + - build-thumbv6k - clippy - extensive - miri diff --git a/library/compiler-builtins/.github/workflows/rustc-pull.yml b/library/compiler-builtins/.github/workflows/rustc-pull.yml index 617db14f46ee..8e88213332de 100644 --- a/library/compiler-builtins/.github/workflows/rustc-pull.yml +++ b/library/compiler-builtins/.github/workflows/rustc-pull.yml @@ -7,6 +7,9 @@ on: # Run at 04:00 UTC every Monday and Thursday - cron: '0 4 * * 1,4' +env: + JOSH_SYNC_VERBOSE: true + jobs: pull: if: github.repository == 'rust-lang/compiler-builtins' diff --git a/library/compiler-builtins/Cargo.toml b/library/compiler-builtins/Cargo.toml index 8501f4e630b5..26f67e02fc52 100644 --- a/library/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/Cargo.toml @@ -31,6 +31,39 @@ exclude = [ "compiler-builtins", ] +[workspace.dependencies] +anyhow = "1.0.101" +assert_cmd = "2.1.2" +cc = "1.2.55" +compiler_builtins = { path = "builtins-shim", default-features = false } +criterion = { version = "0.6.0", default-features = false, features = ["cargo_bench_support"] } +getrandom = "0.3.4" +gmp-mpfr-sys = { version = "1.6.8", default-features = false } +gungraun = "0.17.0" +heck = "0.5.0" +indicatif = { version = "0.18.3", default-features = false } +libm = { path = "libm", default-features = false } +libm-macros = { path = "crates/libm-macros" } +libm-test = { path = "libm-test", default-features = false } +libtest-mimic = "0.8.1" +musl-math-sys = { path = "crates/musl-math-sys" } +no-panic = "0.1.35" +object = { version = "0.37.3", features = ["wasm"] } +panic-handler = { path = "crates/panic-handler" } +paste = "1.0.15" +proc-macro2 = "1.0.106" +quote = "1.0.44" +rand = "0.9.2" +rand_chacha = "0.9.0" +rand_xoshiro = "0.7" +rayon = "1.11.0" +regex = "1.12.3" +rug = { version = "1.28.1", default-features = false, features = ["float", "integer", "std"] } +rustc_apfloat = "0.2.3" +serde_json = "1.0.149" +syn = "2.0.114" +tempfile = "3.24.0" + [profile.release] panic = "abort" @@ -51,6 +84,7 @@ codegen-units = 1 lto = "fat" [profile.bench] +codegen-units = 1 # Required for gungraun debug = true strip = false diff --git a/library/compiler-builtins/builtins-shim/Cargo.toml b/library/compiler-builtins/builtins-shim/Cargo.toml index 746d5b21dc3f..37d3407e9f66 100644 --- a/library/compiler-builtins/builtins-shim/Cargo.toml +++ b/library/compiler-builtins/builtins-shim/Cargo.toml @@ -7,6 +7,9 @@ # manifest that is identical except for the `core` dependency and forwards # to the same sources, which acts as the `compiler-builtins` Cargo entrypoint # for out of tree testing +# +# Ideally we can eventually replace this with a patch in the workspace +# manifest . [package] name = "compiler_builtins" @@ -33,7 +36,7 @@ doctest = false test = false [build-dependencies] -cc = { optional = true, version = "1.2" } +cc = { version = "1.2", optional = true } [features] default = ["compiler-builtins"] diff --git a/library/compiler-builtins/builtins-test/Cargo.toml b/library/compiler-builtins/builtins-test/Cargo.toml index 550f736a76db..9395ab1a985e 100644 --- a/library/compiler-builtins/builtins-test/Cargo.toml +++ b/library/compiler-builtins/builtins-test/Cargo.toml @@ -6,23 +6,22 @@ publish = false license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)" [dependencies] +compiler_builtins = { workspace = true, features = ["unstable-public-internals"] } + # For fuzzing tests we want a deterministic seedable RNG. We also eliminate potential # problems with system RNGs on the variety of platforms this crate is tested on. # `xoshiro128**` is used for its quality, size, and speed at generating `u32` shift amounts. -rand_xoshiro = "0.7" -# To compare float builtins against -rustc_apfloat = "0.2.3" -# Really a dev dependency, but dev dependencies can't be optional -gungraun = { version = "0.17.0", optional = true } +rand_xoshiro.workspace = true -[dependencies.compiler_builtins] -path = "../builtins-shim" -default-features = false -features = ["unstable-public-internals"] +# To compare float builtins against +rustc_apfloat.workspace = true + +# Really a dev dependency, but dev dependencies can't be optional +gungraun = { workspace = true, optional = true } [dev-dependencies] -criterion = { version = "0.6.0", default-features = false, features = ["cargo_bench_support"] } -paste = "1.0.15" +criterion.workspace = true +paste.workspace = true [target.'cfg(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")), target_os = "linux"))'.dev-dependencies] test = { git = "https://github.com/japaric/utest" } diff --git a/library/compiler-builtins/ci/docker/aarch64-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/aarch64-unknown-linux-gnu/Dockerfile index 69b99f5b6b32..683bd07fd47e 100644 --- a/library/compiler-builtins/ci/docker/aarch64-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/aarch64-unknown-linux-gnu/Dockerfile @@ -1,15 +1,15 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ apt-get install -y --no-install-recommends \ gcc libc6-dev ca-certificates \ gcc-aarch64-linux-gnu m4 make libc6-dev-arm64-cross \ - qemu-user-static + qemu-user ENV TOOLCHAIN_PREFIX=aarch64-linux-gnu- ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ - CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER=qemu-aarch64-static \ + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER=qemu-aarch64 \ AR_aarch64_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_aarch64_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ QEMU_LD_PREFIX=/usr/aarch64-linux-gnu \ diff --git a/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabi/Dockerfile b/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabi/Dockerfile index 2fa6f8520520..781abd1b6e88 100644 --- a/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabi/Dockerfile +++ b/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabi/Dockerfile @@ -1,14 +1,14 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ apt-get install -y --no-install-recommends \ gcc libc6-dev ca-certificates \ - gcc-arm-linux-gnueabi libc6-dev-armel-cross qemu-user-static + gcc-arm-linux-gnueabi libc6-dev-armel-cross qemu-user ENV TOOLCHAIN_PREFIX=arm-linux-gnueabi- ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_LINKER="$TOOLCHAIN_PREFIX"gcc \ - CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_RUNNER=qemu-arm-static \ + CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_RUNNER=qemu-arm \ AR_arm_unknown_linux_gnueabi="$TOOLCHAIN_PREFIX"ar \ CC_arm_unknown_linux_gnueabi="$TOOLCHAIN_PREFIX"gcc \ QEMU_LD_PREFIX=/usr/arm-linux-gnueabi \ diff --git a/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabihf/Dockerfile b/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabihf/Dockerfile index 85f7335f5a85..36ea4827dc52 100644 --- a/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabihf/Dockerfile +++ b/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabihf/Dockerfile @@ -1,14 +1,14 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ apt-get install -y --no-install-recommends \ gcc libc6-dev ca-certificates \ - gcc-arm-linux-gnueabihf libc6-dev-armhf-cross qemu-user-static + gcc-arm-linux-gnueabihf libc6-dev-armhf-cross qemu-user ENV TOOLCHAIN_PREFIX=arm-linux-gnueabihf- ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER="$TOOLCHAIN_PREFIX"gcc \ - CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_RUNNER=qemu-arm-static \ + CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_RUNNER=qemu-arm \ AR_arm_unknown_linux_gnueabihf="$TOOLCHAIN_PREFIX"ar \ CC_arm_unknown_linux_gnueabihf="$TOOLCHAIN_PREFIX"gcc \ QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \ diff --git a/library/compiler-builtins/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile b/library/compiler-builtins/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile index 42511479f36f..8b76693b2799 100644 --- a/library/compiler-builtins/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile +++ b/library/compiler-builtins/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile @@ -1,14 +1,14 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ apt-get install -y --no-install-recommends \ gcc libc6-dev ca-certificates \ - gcc-arm-linux-gnueabihf libc6-dev-armhf-cross qemu-user-static + gcc-arm-linux-gnueabihf libc6-dev-armhf-cross qemu-user ENV TOOLCHAIN_PREFIX=arm-linux-gnueabihf- ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER="$TOOLCHAIN_PREFIX"gcc \ - CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER=qemu-arm-static \ + CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER=qemu-arm \ AR_armv7_unknown_linux_gnueabihf="$TOOLCHAIN_PREFIX"ar \ CC_armv7_unknown_linux_gnueabihf="$TOOLCHAIN_PREFIX"gcc \ QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \ diff --git a/library/compiler-builtins/ci/docker/i586-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/i586-unknown-linux-gnu/Dockerfile index 35488c477493..9125038acbde 100644 --- a/library/compiler-builtins/ci/docker/i586-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/i586-unknown-linux-gnu/Dockerfile @@ -1,4 +1,4 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ diff --git a/library/compiler-builtins/ci/docker/i686-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/i686-unknown-linux-gnu/Dockerfile index 35488c477493..9125038acbde 100644 --- a/library/compiler-builtins/ci/docker/i686-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/i686-unknown-linux-gnu/Dockerfile @@ -1,4 +1,4 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ diff --git a/library/compiler-builtins/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile index e95a1b9163ff..a65223595877 100644 --- a/library/compiler-builtins/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile @@ -1,13 +1,13 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ apt-get install -y --no-install-recommends \ - gcc libc6-dev qemu-user-static ca-certificates \ + gcc libc6-dev qemu-user ca-certificates \ gcc-14-loongarch64-linux-gnu libc6-dev-loong64-cross ENV CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER=loongarch64-linux-gnu-gcc-14 \ - CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUNNER=qemu-loongarch64-static \ + CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUNNER=qemu-loongarch64 \ AR_loongarch64_unknown_linux_gnu=loongarch64-linux-gnu-ar \ CC_loongarch64_unknown_linux_gnu=loongarch64-linux-gnu-gcc-14 \ QEMU_LD_PREFIX=/usr/loongarch64-linux-gnu \ diff --git a/library/compiler-builtins/ci/docker/mips-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/mips-unknown-linux-gnu/Dockerfile index fd1877603100..0913f33c05ce 100644 --- a/library/compiler-builtins/ci/docker/mips-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/mips-unknown-linux-gnu/Dockerfile @@ -1,15 +1,15 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ apt-get install -y --no-install-recommends \ gcc libc6-dev ca-certificates \ gcc-mips-linux-gnu libc6-dev-mips-cross \ - binfmt-support qemu-user-static qemu-system-mips + binfmt-support qemu-user qemu-system-mips ENV TOOLCHAIN_PREFIX=mips-linux-gnu- ENV CARGO_TARGET_MIPS_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ - CARGO_TARGET_MIPS_UNKNOWN_LINUX_GNU_RUNNER=qemu-mips-static \ + CARGO_TARGET_MIPS_UNKNOWN_LINUX_GNU_RUNNER=qemu-mips \ AR_mips_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_mips_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ QEMU_LD_PREFIX=/usr/mips-linux-gnu \ diff --git a/library/compiler-builtins/ci/docker/mips64-unknown-linux-gnuabi64/Dockerfile b/library/compiler-builtins/ci/docker/mips64-unknown-linux-gnuabi64/Dockerfile index 4e542ce6858c..d2f4e484b1aa 100644 --- a/library/compiler-builtins/ci/docker/mips64-unknown-linux-gnuabi64/Dockerfile +++ b/library/compiler-builtins/ci/docker/mips64-unknown-linux-gnuabi64/Dockerfile @@ -1,4 +1,4 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ @@ -8,12 +8,12 @@ RUN apt-get update && \ gcc-mips64-linux-gnuabi64 \ libc6-dev \ libc6-dev-mips64-cross \ - qemu-user-static \ + qemu-user \ qemu-system-mips ENV TOOLCHAIN_PREFIX=mips64-linux-gnuabi64- ENV CARGO_TARGET_MIPS64_UNKNOWN_LINUX_GNUABI64_LINKER="$TOOLCHAIN_PREFIX"gcc \ - CARGO_TARGET_MIPS64_UNKNOWN_LINUX_GNUABI64_RUNNER=qemu-mips64-static \ + CARGO_TARGET_MIPS64_UNKNOWN_LINUX_GNUABI64_RUNNER=qemu-mips64 \ AR_mips64_unknown_linux_gnuabi64="$TOOLCHAIN_PREFIX"ar \ CC_mips64_unknown_linux_gnuabi64="$TOOLCHAIN_PREFIX"gcc \ QEMU_LD_PREFIX=/usr/mips64-linux-gnuabi64 \ diff --git a/library/compiler-builtins/ci/docker/mips64el-unknown-linux-gnuabi64/Dockerfile b/library/compiler-builtins/ci/docker/mips64el-unknown-linux-gnuabi64/Dockerfile index 528dfd8940d5..873754b2793e 100644 --- a/library/compiler-builtins/ci/docker/mips64el-unknown-linux-gnuabi64/Dockerfile +++ b/library/compiler-builtins/ci/docker/mips64el-unknown-linux-gnuabi64/Dockerfile @@ -1,4 +1,4 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ @@ -8,11 +8,11 @@ RUN apt-get update && \ gcc-mips64el-linux-gnuabi64 \ libc6-dev \ libc6-dev-mips64el-cross \ - qemu-user-static + qemu-user ENV TOOLCHAIN_PREFIX=mips64el-linux-gnuabi64- ENV CARGO_TARGET_MIPS64EL_UNKNOWN_LINUX_GNUABI64_LINKER="$TOOLCHAIN_PREFIX"gcc \ - CARGO_TARGET_MIPS64EL_UNKNOWN_LINUX_GNUABI64_RUNNER=qemu-mips64el-static \ + CARGO_TARGET_MIPS64EL_UNKNOWN_LINUX_GNUABI64_RUNNER=qemu-mips64el \ AR_mips64el_unknown_linux_gnuabi64="$TOOLCHAIN_PREFIX"ar \ CC_mips64el_unknown_linux_gnuabi64="$TOOLCHAIN_PREFIX"gcc \ QEMU_LD_PREFIX=/usr/mips64el-linux-gnuabi64 \ diff --git a/library/compiler-builtins/ci/docker/mipsel-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/mipsel-unknown-linux-gnu/Dockerfile index 2572180238e2..5768b68d6c95 100644 --- a/library/compiler-builtins/ci/docker/mipsel-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/mipsel-unknown-linux-gnu/Dockerfile @@ -1,15 +1,15 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ apt-get install -y --no-install-recommends \ gcc libc6-dev ca-certificates \ gcc-mipsel-linux-gnu libc6-dev-mipsel-cross \ - binfmt-support qemu-user-static + binfmt-support qemu-user ENV TOOLCHAIN_PREFIX=mipsel-linux-gnu- ENV CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ - CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_GNU_RUNNER=qemu-mipsel-static \ + CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_GNU_RUNNER=qemu-mipsel \ AR_mipsel_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_mipsel_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ QEMU_LD_PREFIX=/usr/mipsel-linux-gnu \ diff --git a/library/compiler-builtins/ci/docker/powerpc-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/powerpc-unknown-linux-gnu/Dockerfile index cac1f23610aa..c625a4bcd5d7 100644 --- a/library/compiler-builtins/ci/docker/powerpc-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/powerpc-unknown-linux-gnu/Dockerfile @@ -1,15 +1,15 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ apt-get install -y --no-install-recommends \ - gcc libc6-dev qemu-user-static ca-certificates \ + gcc libc6-dev qemu-user ca-certificates \ gcc-powerpc-linux-gnu libc6-dev-powerpc-cross \ qemu-system-ppc ENV TOOLCHAIN_PREFIX=powerpc-linux-gnu- ENV CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ - CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc-static \ + CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc \ AR_powerpc_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_powerpc_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ QEMU_LD_PREFIX=/usr/powerpc-linux-gnu \ diff --git a/library/compiler-builtins/ci/docker/powerpc64-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/powerpc64-unknown-linux-gnu/Dockerfile index 76127b7dbb8c..86a7a8cd46e4 100644 --- a/library/compiler-builtins/ci/docker/powerpc64-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/powerpc64-unknown-linux-gnu/Dockerfile @@ -1,15 +1,15 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ apt-get install -y --no-install-recommends \ gcc libc6-dev ca-certificates \ gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross \ - binfmt-support qemu-user-static qemu-system-ppc + binfmt-support qemu-user qemu-system-ppc ENV TOOLCHAIN_PREFIX=powerpc64-linux-gnu- ENV CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ - CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc64-static \ + CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc64 \ AR_powerpc64_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_powerpc64_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ QEMU_LD_PREFIX=/usr/powerpc64-linux-gnu \ diff --git a/library/compiler-builtins/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile index da1d56ca66f2..722b10b0a734 100644 --- a/library/compiler-builtins/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile @@ -1,15 +1,15 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ apt-get install -y --no-install-recommends \ - gcc libc6-dev qemu-user-static ca-certificates \ + gcc libc6-dev qemu-user ca-certificates \ gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross \ qemu-system-ppc ENV TOOLCHAIN_PREFIX=powerpc64le-linux-gnu- ENV CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ - CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc64le-static \ + CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc64le \ AR_powerpc64le_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_powerpc64le_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ QEMU_LD_PREFIX=/usr/powerpc64le-linux-gnu \ diff --git a/library/compiler-builtins/ci/docker/riscv64gc-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/riscv64gc-unknown-linux-gnu/Dockerfile index 513efacd6d96..7a721ba05416 100644 --- a/library/compiler-builtins/ci/docker/riscv64gc-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/riscv64gc-unknown-linux-gnu/Dockerfile @@ -1,15 +1,15 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ apt-get install -y --no-install-recommends \ - gcc libc6-dev qemu-user-static ca-certificates \ + gcc libc6-dev qemu-user ca-certificates \ gcc-riscv64-linux-gnu libc6-dev-riscv64-cross \ qemu-system-riscv64 ENV TOOLCHAIN_PREFIX=riscv64-linux-gnu- ENV CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ - CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_RUNNER=qemu-riscv64-static \ + CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_RUNNER=qemu-riscv64 \ AR_riscv64gc_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_riscv64gc_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ QEMU_LD_PREFIX=/usr/riscv64-linux-gnu \ diff --git a/library/compiler-builtins/ci/docker/thumbv6m-none-eabi/Dockerfile b/library/compiler-builtins/ci/docker/thumbv6m-none-eabi/Dockerfile index a9a172a21137..a1a6b3cf5cfd 100644 --- a/library/compiler-builtins/ci/docker/thumbv6m-none-eabi/Dockerfile +++ b/library/compiler-builtins/ci/docker/thumbv6m-none-eabi/Dockerfile @@ -1,4 +1,4 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ diff --git a/library/compiler-builtins/ci/docker/thumbv7em-none-eabi/Dockerfile b/library/compiler-builtins/ci/docker/thumbv7em-none-eabi/Dockerfile index a9a172a21137..a1a6b3cf5cfd 100644 --- a/library/compiler-builtins/ci/docker/thumbv7em-none-eabi/Dockerfile +++ b/library/compiler-builtins/ci/docker/thumbv7em-none-eabi/Dockerfile @@ -1,4 +1,4 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ diff --git a/library/compiler-builtins/ci/docker/thumbv7em-none-eabihf/Dockerfile b/library/compiler-builtins/ci/docker/thumbv7em-none-eabihf/Dockerfile index a9a172a21137..a1a6b3cf5cfd 100644 --- a/library/compiler-builtins/ci/docker/thumbv7em-none-eabihf/Dockerfile +++ b/library/compiler-builtins/ci/docker/thumbv7em-none-eabihf/Dockerfile @@ -1,4 +1,4 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ diff --git a/library/compiler-builtins/ci/docker/thumbv7m-none-eabi/Dockerfile b/library/compiler-builtins/ci/docker/thumbv7m-none-eabi/Dockerfile index a9a172a21137..a1a6b3cf5cfd 100644 --- a/library/compiler-builtins/ci/docker/thumbv7m-none-eabi/Dockerfile +++ b/library/compiler-builtins/ci/docker/thumbv7m-none-eabi/Dockerfile @@ -1,4 +1,4 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ diff --git a/library/compiler-builtins/ci/docker/wasm32-unknown-unknown/Dockerfile b/library/compiler-builtins/ci/docker/wasm32-unknown-unknown/Dockerfile index 2813d318670e..b646a72bb37c 100644 --- a/library/compiler-builtins/ci/docker/wasm32-unknown-unknown/Dockerfile +++ b/library/compiler-builtins/ci/docker/wasm32-unknown-unknown/Dockerfile @@ -1,4 +1,4 @@ -ARG IMAGE=ubuntu:20.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ diff --git a/library/compiler-builtins/ci/docker/x86_64-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/x86_64-unknown-linux-gnu/Dockerfile index 2ef800129d67..927515f90f32 100644 --- a/library/compiler-builtins/ci/docker/x86_64-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/x86_64-unknown-linux-gnu/Dockerfile @@ -1,4 +1,4 @@ -ARG IMAGE=ubuntu:25.04 +ARG IMAGE=ubuntu:25.10 FROM $IMAGE RUN apt-get update && \ diff --git a/library/compiler-builtins/ci/run-docker.sh b/library/compiler-builtins/ci/run-docker.sh index 4c1fe0fe2644..e65ada271904 100755 --- a/library/compiler-builtins/ci/run-docker.sh +++ b/library/compiler-builtins/ci/run-docker.sh @@ -97,7 +97,7 @@ if [ "${1:-}" = "--help" ] || [ "$#" -gt 1 ]; then usage: ./ci/run-docker.sh [target] you can also set DOCKER_BASE_IMAGE to use something other than the default - ubuntu:25.04 (or rustlang/rust:nightly). + ubuntu:25.10 (or rustlang/rust:nightly). " exit fi diff --git a/library/compiler-builtins/ci/run.sh b/library/compiler-builtins/ci/run.sh index 0c07b32c74b9..12b3f37889c9 100755 --- a/library/compiler-builtins/ci/run.sh +++ b/library/compiler-builtins/ci/run.sh @@ -13,11 +13,6 @@ if [ -z "$target" ]; then target="$host_target" fi -if [[ "$target" = *"wasm"* ]]; then - # Enable the random backend - export RUSTFLAGS="${RUSTFLAGS:-} --cfg getrandom_backend=\"wasm_js\"" -fi - if [ "${USING_CONTAINER_RUSTC:-}" = 1 ]; then # Install nonstandard components if we have control of the environment rustup target list --installed | @@ -51,8 +46,8 @@ fi # Ensure there are no duplicate symbols or references to `core` when # `compiler-builtins` is built with various features. Symcheck invokes Cargo to # build with the arguments we provide it, then validates the built artifacts. +SYMCHECK_TEST_TARGET="$target" cargo test -p symbol-check --release symcheck=(cargo run -p symbol-check --release) -[[ "$target" = "wasm"* ]] && symcheck+=(--features wasm) symcheck+=(-- build-and-check) "${symcheck[@]}" "$target" -- -p compiler_builtins @@ -157,7 +152,8 @@ if [ "${BUILD_ONLY:-}" = "1" ]; then echo "can't run tests on $target; skipping" else - mflags+=(--workspace --target "$target") + # symcheck tests need specific env setup, and is already tested above + mflags+=(--workspace --exclude symbol-check --target "$target") cmd=(cargo test "${mflags[@]}") profile_flag="--profile" diff --git a/library/compiler-builtins/compiler-builtins/Cargo.toml b/library/compiler-builtins/compiler-builtins/Cargo.toml index 496dde2d4cf2..a8b8920421b3 100644 --- a/library/compiler-builtins/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/compiler-builtins/Cargo.toml @@ -31,7 +31,7 @@ doc = false core = { path = "../../core", optional = true } [build-dependencies] -cc = { optional = true, version = "1.2" } +cc = { version = "1.2", optional = true } [features] default = ["compiler-builtins"] diff --git a/library/compiler-builtins/compiler-builtins/src/hexagon/dffma.s b/library/compiler-builtins/compiler-builtins/src/hexagon/dffma.s index 97d05eb1839e..6cd1f1b79f87 100644 --- a/library/compiler-builtins/compiler-builtins/src/hexagon/dffma.s +++ b/library/compiler-builtins/compiler-builtins/src/hexagon/dffma.s @@ -7,7 +7,7 @@ .p2align 5 __hexagon_fmadf4: __hexagon_fmadf5: -fma: +.Lfma: { p0 = dfclass(r1:0,#2) p0 = dfclass(r3:2,#2) @@ -400,7 +400,7 @@ fma: r3:2 = insert(r11:10,#63,#0) r1 -= asl(r28,#20) } - jump fma + jump .Lfma .Lfma_ab_tiny: r9:8 = combine(##0x00100000,#0) @@ -408,7 +408,7 @@ fma: r1:0 = insert(r9:8,#63,#0) r3:2 = insert(r9:8,#63,#0) } - jump fma + jump .Lfma .Lab_inf: { @@ -531,4 +531,3 @@ fma: r5 = insert(r28,#11,#20) jump .Lfma_abnormal_c_restart } -.size fma,.-fma diff --git a/library/compiler-builtins/compiler-builtins/src/lib.rs b/library/compiler-builtins/compiler-builtins/src/lib.rs index 342427aca9c7..80395a4738eb 100644 --- a/library/compiler-builtins/compiler-builtins/src/lib.rs +++ b/library/compiler-builtins/compiler-builtins/src/lib.rs @@ -11,11 +11,11 @@ #![feature(repr_simd)] #![feature(macro_metavar_expr_concat)] #![feature(rustc_attrs)] -#![feature(float_bits_const)] #![cfg_attr(f16_enabled, feature(f16))] #![cfg_attr(f128_enabled, feature(f128))] #![no_builtins] #![no_std] +#![allow(unstable_name_collisions)] // FIXME(float_bits_const): remove when stable #![allow(unused_features)] #![allow(internal_features)] // `mem::swap` cannot be used because it may generate references to memcpy in unoptimized code. @@ -46,6 +46,7 @@ pub mod float; pub mod int; pub mod math; pub mod mem; +pub mod sync; // `libm` expects its `support` module to be available in the crate root. use math::libm_math::support; @@ -59,13 +60,6 @@ pub mod aarch64; #[cfg(all(target_arch = "aarch64", target_feature = "outline-atomics"))] pub mod aarch64_outline_atomics; -#[cfg(all( - kernel_user_helpers, - any(target_os = "linux", target_os = "android"), - target_arch = "arm" -))] -pub mod arm_linux; - #[cfg(target_arch = "avr")] pub mod avr; diff --git a/library/compiler-builtins/compiler-builtins/src/arm_linux.rs b/library/compiler-builtins/compiler-builtins/src/sync/arm_linux.rs similarity index 59% rename from library/compiler-builtins/compiler-builtins/src/arm_linux.rs rename to library/compiler-builtins/compiler-builtins/src/sync/arm_linux.rs index ab9f86807390..7edd76c0b8b7 100644 --- a/library/compiler-builtins/compiler-builtins/src/arm_linux.rs +++ b/library/compiler-builtins/compiler-builtins/src/sync/arm_linux.rs @@ -125,14 +125,16 @@ unsafe fn atomic_cmpxchg(ptr: *mut T, oldval: u32, newval: u32) -> u32 { let (shift, mask) = get_shift_mask(ptr); loop { - // FIXME(safety): preconditions review needed + // SAFETY: the caller must guarantee that the pointer is valid for read and write + // and aligned to the element size. let curval_aligned = unsafe { atomic_load_aligned::(aligned_ptr) }; let curval = extract_aligned(curval_aligned, shift, mask); if curval != oldval { return curval; } let newval_aligned = insert_aligned(curval_aligned, newval, shift, mask); - // FIXME(safety): preconditions review needed + // SAFETY: the caller must guarantee that the pointer is valid for read and write + // and aligned to the element size. if unsafe { __kuser_cmpxchg(curval_aligned, newval_aligned, aligned_ptr) } { return oldval; } @@ -143,7 +145,8 @@ macro_rules! atomic_rmw { ($name:ident, $ty:ty, $op:expr, $fetch:expr) => { intrinsics! { pub unsafe extern "C" fn $name(ptr: *mut $ty, val: $ty) -> $ty { - // FIXME(safety): preconditions review needed + // SAFETY: the caller must guarantee that the pointer is valid for read and write + // and aligned to the element size. unsafe { atomic_rmw( ptr, @@ -167,140 +170,15 @@ macro_rules! atomic_cmpxchg { ($name:ident, $ty:ty) => { intrinsics! { pub unsafe extern "C" fn $name(ptr: *mut $ty, oldval: $ty, newval: $ty) -> $ty { - // FIXME(safety): preconditions review needed + // SAFETY: the caller must guarantee that the pointer is valid for read and write + // and aligned to the element size. unsafe { atomic_cmpxchg(ptr, oldval as u32, newval as u32) as $ty } } } }; } -atomic_rmw!(@old __sync_fetch_and_add_1, u8, |a: u8, b: u8| a.wrapping_add(b)); -atomic_rmw!(@old __sync_fetch_and_add_2, u16, |a: u16, b: u16| a - .wrapping_add(b)); -atomic_rmw!(@old __sync_fetch_and_add_4, u32, |a: u32, b: u32| a - .wrapping_add(b)); - -atomic_rmw!(@new __sync_add_and_fetch_1, u8, |a: u8, b: u8| a.wrapping_add(b)); -atomic_rmw!(@new __sync_add_and_fetch_2, u16, |a: u16, b: u16| a - .wrapping_add(b)); -atomic_rmw!(@new __sync_add_and_fetch_4, u32, |a: u32, b: u32| a - .wrapping_add(b)); - -atomic_rmw!(@old __sync_fetch_and_sub_1, u8, |a: u8, b: u8| a.wrapping_sub(b)); -atomic_rmw!(@old __sync_fetch_and_sub_2, u16, |a: u16, b: u16| a - .wrapping_sub(b)); -atomic_rmw!(@old __sync_fetch_and_sub_4, u32, |a: u32, b: u32| a - .wrapping_sub(b)); - -atomic_rmw!(@new __sync_sub_and_fetch_1, u8, |a: u8, b: u8| a.wrapping_sub(b)); -atomic_rmw!(@new __sync_sub_and_fetch_2, u16, |a: u16, b: u16| a - .wrapping_sub(b)); -atomic_rmw!(@new __sync_sub_and_fetch_4, u32, |a: u32, b: u32| a - .wrapping_sub(b)); - -atomic_rmw!(@old __sync_fetch_and_and_1, u8, |a: u8, b: u8| a & b); -atomic_rmw!(@old __sync_fetch_and_and_2, u16, |a: u16, b: u16| a & b); -atomic_rmw!(@old __sync_fetch_and_and_4, u32, |a: u32, b: u32| a & b); - -atomic_rmw!(@new __sync_and_and_fetch_1, u8, |a: u8, b: u8| a & b); -atomic_rmw!(@new __sync_and_and_fetch_2, u16, |a: u16, b: u16| a & b); -atomic_rmw!(@new __sync_and_and_fetch_4, u32, |a: u32, b: u32| a & b); - -atomic_rmw!(@old __sync_fetch_and_or_1, u8, |a: u8, b: u8| a | b); -atomic_rmw!(@old __sync_fetch_and_or_2, u16, |a: u16, b: u16| a | b); -atomic_rmw!(@old __sync_fetch_and_or_4, u32, |a: u32, b: u32| a | b); - -atomic_rmw!(@new __sync_or_and_fetch_1, u8, |a: u8, b: u8| a | b); -atomic_rmw!(@new __sync_or_and_fetch_2, u16, |a: u16, b: u16| a | b); -atomic_rmw!(@new __sync_or_and_fetch_4, u32, |a: u32, b: u32| a | b); - -atomic_rmw!(@old __sync_fetch_and_xor_1, u8, |a: u8, b: u8| a ^ b); -atomic_rmw!(@old __sync_fetch_and_xor_2, u16, |a: u16, b: u16| a ^ b); -atomic_rmw!(@old __sync_fetch_and_xor_4, u32, |a: u32, b: u32| a ^ b); - -atomic_rmw!(@new __sync_xor_and_fetch_1, u8, |a: u8, b: u8| a ^ b); -atomic_rmw!(@new __sync_xor_and_fetch_2, u16, |a: u16, b: u16| a ^ b); -atomic_rmw!(@new __sync_xor_and_fetch_4, u32, |a: u32, b: u32| a ^ b); - -atomic_rmw!(@old __sync_fetch_and_nand_1, u8, |a: u8, b: u8| !(a & b)); -atomic_rmw!(@old __sync_fetch_and_nand_2, u16, |a: u16, b: u16| !(a & b)); -atomic_rmw!(@old __sync_fetch_and_nand_4, u32, |a: u32, b: u32| !(a & b)); - -atomic_rmw!(@new __sync_nand_and_fetch_1, u8, |a: u8, b: u8| !(a & b)); -atomic_rmw!(@new __sync_nand_and_fetch_2, u16, |a: u16, b: u16| !(a & b)); -atomic_rmw!(@new __sync_nand_and_fetch_4, u32, |a: u32, b: u32| !(a & b)); - -atomic_rmw!(@old __sync_fetch_and_max_1, i8, |a: i8, b: i8| if a > b { - a -} else { - b -}); -atomic_rmw!(@old __sync_fetch_and_max_2, i16, |a: i16, b: i16| if a > b { - a -} else { - b -}); -atomic_rmw!(@old __sync_fetch_and_max_4, i32, |a: i32, b: i32| if a > b { - a -} else { - b -}); - -atomic_rmw!(@old __sync_fetch_and_umax_1, u8, |a: u8, b: u8| if a > b { - a -} else { - b -}); -atomic_rmw!(@old __sync_fetch_and_umax_2, u16, |a: u16, b: u16| if a > b { - a -} else { - b -}); -atomic_rmw!(@old __sync_fetch_and_umax_4, u32, |a: u32, b: u32| if a > b { - a -} else { - b -}); - -atomic_rmw!(@old __sync_fetch_and_min_1, i8, |a: i8, b: i8| if a < b { - a -} else { - b -}); -atomic_rmw!(@old __sync_fetch_and_min_2, i16, |a: i16, b: i16| if a < b { - a -} else { - b -}); -atomic_rmw!(@old __sync_fetch_and_min_4, i32, |a: i32, b: i32| if a < b { - a -} else { - b -}); - -atomic_rmw!(@old __sync_fetch_and_umin_1, u8, |a: u8, b: u8| if a < b { - a -} else { - b -}); -atomic_rmw!(@old __sync_fetch_and_umin_2, u16, |a: u16, b: u16| if a < b { - a -} else { - b -}); -atomic_rmw!(@old __sync_fetch_and_umin_4, u32, |a: u32, b: u32| if a < b { - a -} else { - b -}); - -atomic_rmw!(@old __sync_lock_test_and_set_1, u8, |_: u8, b: u8| b); -atomic_rmw!(@old __sync_lock_test_and_set_2, u16, |_: u16, b: u16| b); -atomic_rmw!(@old __sync_lock_test_and_set_4, u32, |_: u32, b: u32| b); - -atomic_cmpxchg!(__sync_val_compare_and_swap_1, u8); -atomic_cmpxchg!(__sync_val_compare_and_swap_2, u16); -atomic_cmpxchg!(__sync_val_compare_and_swap_4, u32); +include!("arm_thumb_shared.rs"); intrinsics! { pub unsafe extern "C" fn __sync_synchronize() { diff --git a/library/compiler-builtins/compiler-builtins/src/sync/arm_thumb_shared.rs b/library/compiler-builtins/compiler-builtins/src/sync/arm_thumb_shared.rs new file mode 100644 index 000000000000..812989c7bc85 --- /dev/null +++ b/library/compiler-builtins/compiler-builtins/src/sync/arm_thumb_shared.rs @@ -0,0 +1,134 @@ +// Used by both arm_linux.rs and thumbv6k.rs. + +// References: +// - https://llvm.org/docs/Atomics.html#libcalls-sync +// - https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html +// - https://refspecs.linuxfoundation.org/elf/IA64-SysV-psABI.pdf#page=58 + +atomic_rmw!(@old __sync_fetch_and_add_1, u8, |a: u8, b: u8| a.wrapping_add(b)); +atomic_rmw!(@old __sync_fetch_and_add_2, u16, |a: u16, b: u16| a + .wrapping_add(b)); +atomic_rmw!(@old __sync_fetch_and_add_4, u32, |a: u32, b: u32| a + .wrapping_add(b)); + +atomic_rmw!(@new __sync_add_and_fetch_1, u8, |a: u8, b: u8| a.wrapping_add(b)); +atomic_rmw!(@new __sync_add_and_fetch_2, u16, |a: u16, b: u16| a + .wrapping_add(b)); +atomic_rmw!(@new __sync_add_and_fetch_4, u32, |a: u32, b: u32| a + .wrapping_add(b)); + +atomic_rmw!(@old __sync_fetch_and_sub_1, u8, |a: u8, b: u8| a.wrapping_sub(b)); +atomic_rmw!(@old __sync_fetch_and_sub_2, u16, |a: u16, b: u16| a + .wrapping_sub(b)); +atomic_rmw!(@old __sync_fetch_and_sub_4, u32, |a: u32, b: u32| a + .wrapping_sub(b)); + +atomic_rmw!(@new __sync_sub_and_fetch_1, u8, |a: u8, b: u8| a.wrapping_sub(b)); +atomic_rmw!(@new __sync_sub_and_fetch_2, u16, |a: u16, b: u16| a + .wrapping_sub(b)); +atomic_rmw!(@new __sync_sub_and_fetch_4, u32, |a: u32, b: u32| a + .wrapping_sub(b)); + +atomic_rmw!(@old __sync_fetch_and_and_1, u8, |a: u8, b: u8| a & b); +atomic_rmw!(@old __sync_fetch_and_and_2, u16, |a: u16, b: u16| a & b); +atomic_rmw!(@old __sync_fetch_and_and_4, u32, |a: u32, b: u32| a & b); + +atomic_rmw!(@new __sync_and_and_fetch_1, u8, |a: u8, b: u8| a & b); +atomic_rmw!(@new __sync_and_and_fetch_2, u16, |a: u16, b: u16| a & b); +atomic_rmw!(@new __sync_and_and_fetch_4, u32, |a: u32, b: u32| a & b); + +atomic_rmw!(@old __sync_fetch_and_or_1, u8, |a: u8, b: u8| a | b); +atomic_rmw!(@old __sync_fetch_and_or_2, u16, |a: u16, b: u16| a | b); +atomic_rmw!(@old __sync_fetch_and_or_4, u32, |a: u32, b: u32| a | b); + +atomic_rmw!(@new __sync_or_and_fetch_1, u8, |a: u8, b: u8| a | b); +atomic_rmw!(@new __sync_or_and_fetch_2, u16, |a: u16, b: u16| a | b); +atomic_rmw!(@new __sync_or_and_fetch_4, u32, |a: u32, b: u32| a | b); + +atomic_rmw!(@old __sync_fetch_and_xor_1, u8, |a: u8, b: u8| a ^ b); +atomic_rmw!(@old __sync_fetch_and_xor_2, u16, |a: u16, b: u16| a ^ b); +atomic_rmw!(@old __sync_fetch_and_xor_4, u32, |a: u32, b: u32| a ^ b); + +atomic_rmw!(@new __sync_xor_and_fetch_1, u8, |a: u8, b: u8| a ^ b); +atomic_rmw!(@new __sync_xor_and_fetch_2, u16, |a: u16, b: u16| a ^ b); +atomic_rmw!(@new __sync_xor_and_fetch_4, u32, |a: u32, b: u32| a ^ b); + +atomic_rmw!(@old __sync_fetch_and_nand_1, u8, |a: u8, b: u8| !(a & b)); +atomic_rmw!(@old __sync_fetch_and_nand_2, u16, |a: u16, b: u16| !(a & b)); +atomic_rmw!(@old __sync_fetch_and_nand_4, u32, |a: u32, b: u32| !(a & b)); + +atomic_rmw!(@new __sync_nand_and_fetch_1, u8, |a: u8, b: u8| !(a & b)); +atomic_rmw!(@new __sync_nand_and_fetch_2, u16, |a: u16, b: u16| !(a & b)); +atomic_rmw!(@new __sync_nand_and_fetch_4, u32, |a: u32, b: u32| !(a & b)); + +atomic_rmw!(@old __sync_fetch_and_max_1, i8, |a: i8, b: i8| if a > b { + a +} else { + b +}); +atomic_rmw!(@old __sync_fetch_and_max_2, i16, |a: i16, b: i16| if a > b { + a +} else { + b +}); +atomic_rmw!(@old __sync_fetch_and_max_4, i32, |a: i32, b: i32| if a > b { + a +} else { + b +}); + +atomic_rmw!(@old __sync_fetch_and_umax_1, u8, |a: u8, b: u8| if a > b { + a +} else { + b +}); +atomic_rmw!(@old __sync_fetch_and_umax_2, u16, |a: u16, b: u16| if a > b { + a +} else { + b +}); +atomic_rmw!(@old __sync_fetch_and_umax_4, u32, |a: u32, b: u32| if a > b { + a +} else { + b +}); + +atomic_rmw!(@old __sync_fetch_and_min_1, i8, |a: i8, b: i8| if a < b { + a +} else { + b +}); +atomic_rmw!(@old __sync_fetch_and_min_2, i16, |a: i16, b: i16| if a < b { + a +} else { + b +}); +atomic_rmw!(@old __sync_fetch_and_min_4, i32, |a: i32, b: i32| if a < b { + a +} else { + b +}); + +atomic_rmw!(@old __sync_fetch_and_umin_1, u8, |a: u8, b: u8| if a < b { + a +} else { + b +}); +atomic_rmw!(@old __sync_fetch_and_umin_2, u16, |a: u16, b: u16| if a < b { + a +} else { + b +}); +atomic_rmw!(@old __sync_fetch_and_umin_4, u32, |a: u32, b: u32| if a < b { + a +} else { + b +}); + +atomic_rmw!(@old __sync_lock_test_and_set_1, u8, |_: u8, b: u8| b); +atomic_rmw!(@old __sync_lock_test_and_set_2, u16, |_: u16, b: u16| b); +atomic_rmw!(@old __sync_lock_test_and_set_4, u32, |_: u32, b: u32| b); + +atomic_cmpxchg!(__sync_val_compare_and_swap_1, u8); +atomic_cmpxchg!(__sync_val_compare_and_swap_2, u16); +atomic_cmpxchg!(__sync_val_compare_and_swap_4, u32); diff --git a/library/compiler-builtins/compiler-builtins/src/sync/mod.rs b/library/compiler-builtins/compiler-builtins/src/sync/mod.rs new file mode 100644 index 000000000000..590db14bb23b --- /dev/null +++ b/library/compiler-builtins/compiler-builtins/src/sync/mod.rs @@ -0,0 +1,20 @@ +#[cfg(all( + kernel_user_helpers, + any(target_os = "linux", target_os = "android"), + target_arch = "arm" +))] +pub mod arm_linux; + +// Armv6k supports atomic instructions, but they are unavailable in Thumb mode +// unless Thumb-2 instructions available (v6t2). +// Using Thumb interworking allows us to use these instructions even from Thumb mode +// without Thumb-2 instructions, but LLVM does not implement that processing (as of LLVM 21), +// so we implement it here at this time. +// (`not(target_feature = "mclass")` is unneeded because v6k is not set on thumbv6m.) +#[cfg(all( + target_arch = "arm", + target_feature = "thumb-mode", + target_feature = "v6k", + not(target_feature = "v6t2"), +))] +pub mod thumbv6k; diff --git a/library/compiler-builtins/compiler-builtins/src/sync/thumbv6k.rs b/library/compiler-builtins/compiler-builtins/src/sync/thumbv6k.rs new file mode 100644 index 000000000000..c47b4c2ec6b0 --- /dev/null +++ b/library/compiler-builtins/compiler-builtins/src/sync/thumbv6k.rs @@ -0,0 +1,213 @@ +// Armv6k supports atomic instructions, but they are unavailable in Thumb mode +// unless Thumb-2 instructions available (v6t2). +// Using Thumb interworking allows us to use these instructions even from Thumb mode +// without Thumb-2 instructions, but LLVM does not implement that processing (as of LLVM 21), +// so we implement it here at this time. + +use core::arch::asm; +use core::mem; + +// Data Memory Barrier (DMB) operation. +// +// Armv6 does not support DMB instruction, so use use special instruction equivalent to it. +// +// Refs: https://developer.arm.com/documentation/ddi0360/f/control-coprocessor-cp15/register-descriptions/c7--cache-operations-register +macro_rules! cp15_barrier { + () => { + "mcr p15, #0, {zero}, c7, c10, #5" + }; +} + +#[instruction_set(arm::a32)] +unsafe fn fence() { + unsafe { + asm!( + cp15_barrier!(), + // cp15_barrier! calls `mcr p15, 0, {zero}, c7, c10, 5`, and + // the value in the {zero} register should be zero (SBZ). + zero = inout(reg) 0_u32 => _, + options(nostack, preserves_flags), + ); + } +} + +trait Atomic: Copy + Eq { + unsafe fn load_relaxed(src: *const Self) -> Self; + unsafe fn cmpxchg(dst: *mut Self, current: Self, new: Self) -> Self; +} + +macro_rules! atomic { + ($ty:ident, $suffix:tt) => { + impl Atomic for $ty { + // #[instruction_set(arm::a32)] is unneeded for ldr. + #[inline] + unsafe fn load_relaxed( + src: *const Self, + ) -> Self { + let out: Self; + // SAFETY: the caller must guarantee that the pointer is valid for read and write + // and aligned to the element size. + unsafe { + asm!( + concat!("ldr", $suffix, " {out}, [{src}]"), // atomic { out = *src } + src = in(reg) src, + out = lateout(reg) out, + options(nostack, preserves_flags), + ); + } + out + } + #[inline] + #[instruction_set(arm::a32)] + unsafe fn cmpxchg( + dst: *mut Self, + old: Self, + new: Self, + ) -> Self { + let mut out: Self; + // SAFETY: the caller must guarantee that the pointer is valid for read and write + // and aligned to the element size. + // + // Instead of the common `fence; ll/sc loop; fence` form, we use the form used by + // LLVM, which omits the preceding fence if no write operation is performed. + unsafe { + asm!( + concat!("ldrex", $suffix, " {out}, [{dst}]"), // atomic { out = *dst; EXCLUSIVE = dst } + "cmp {out}, {old}", // if out == old { Z = 1 } else { Z = 0 } + "bne 3f", // if Z == 0 { jump 'cmp-fail } + cp15_barrier!(), // fence + "2:", // 'retry: + concat!("strex", $suffix, " {r}, {new}, [{dst}]"), // atomic { if EXCLUSIVE == dst { *dst = new; r = 0 } else { r = 1 }; EXCLUSIVE = None } + "cmp {r}, #0", // if r == 0 { Z = 1 } else { Z = 0 } + "beq 3f", // if Z == 1 { jump 'success } + concat!("ldrex", $suffix, " {out}, [{dst}]"), // atomic { out = *dst; EXCLUSIVE = dst } + "cmp {out}, {old}", // if out == old { Z = 1 } else { Z = 0 } + "beq 2b", // if Z == 1 { jump 'retry } + "3:", // 'cmp-fail | 'success: + cp15_barrier!(), // fence + dst = in(reg) dst, + // Note: this cast must be a zero-extend since loaded value + // which compared to it is zero-extended. + old = in(reg) u32::from(old), + new = in(reg) new, + out = out(reg) out, + r = out(reg) _, + // cp15_barrier! calls `mcr p15, 0, {zero}, c7, c10, 5`, and + // the value in the {zero} register should be zero (SBZ). + zero = inout(reg) 0_u32 => _, + // Do not use `preserves_flags` because CMP modifies the condition flags. + options(nostack), + ); + out + } + } + } + }; +} +atomic!(u8, "b"); +atomic!(u16, "h"); +atomic!(u32, ""); + +// To avoid the annoyance of sign extension, we implement signed CAS using +// unsigned CAS. (See note in cmpxchg impl in atomic! macro) +macro_rules! delegate_signed { + ($ty:ident, $base:ident) => { + const _: () = { + assert!(mem::size_of::<$ty>() == mem::size_of::<$base>()); + assert!(mem::align_of::<$ty>() == mem::align_of::<$base>()); + }; + impl Atomic for $ty { + #[inline] + unsafe fn load_relaxed(src: *const Self) -> Self { + // SAFETY: the caller must uphold the safety contract. + // casts are okay because $ty and $base implement the same layout. + unsafe { <$base as Atomic>::load_relaxed(src.cast::<$base>()).cast_signed() } + } + #[inline] + unsafe fn cmpxchg(dst: *mut Self, old: Self, new: Self) -> Self { + // SAFETY: the caller must uphold the safety contract. + // casts are okay because $ty and $base implement the same layout. + unsafe { + <$base as Atomic>::cmpxchg( + dst.cast::<$base>(), + old.cast_unsigned(), + new.cast_unsigned(), + ) + .cast_signed() + } + } + } + }; +} +delegate_signed!(i8, u8); +delegate_signed!(i16, u16); +delegate_signed!(i32, u32); + +// Generic atomic read-modify-write operation +// +// We could implement RMW more efficiently as an assembly LL/SC loop per operation, +// but we won't do that for now because it would make the implementation more complex. +// +// We also do not implement LL and SC as separate functions. This is because it +// is theoretically possible for the compiler to insert operations that might +// clear the reservation between LL and SC. See https://github.com/taiki-e/portable-atomic/blob/58ef7f27c9e20da4cc1ef0abf8b8ce9ac5219ec3/src/imp/atomic128/aarch64.rs#L44-L55 +// for more details. +unsafe fn atomic_rmw T, G: Fn(T, T) -> T>(ptr: *mut T, f: F, g: G) -> T { + loop { + // SAFETY: the caller must guarantee that the pointer is valid for read and write + // and aligned to the element size. + let curval = unsafe { T::load_relaxed(ptr) }; + let newval = f(curval); + // SAFETY: the caller must guarantee that the pointer is valid for read and write + // and aligned to the element size. + if unsafe { T::cmpxchg(ptr, curval, newval) } == curval { + return g(curval, newval); + } + } +} + +macro_rules! atomic_rmw { + ($name:ident, $ty:ty, $op:expr, $fetch:expr) => { + intrinsics! { + pub unsafe extern "C" fn $name(ptr: *mut $ty, val: $ty) -> $ty { + // SAFETY: the caller must guarantee that the pointer is valid for read and write + // and aligned to the element size. + unsafe { + atomic_rmw( + ptr, + |x| $op(x as $ty, val), + |old, new| $fetch(old, new) + ) as $ty + } + } + } + }; + + (@old $name:ident, $ty:ty, $op:expr) => { + atomic_rmw!($name, $ty, $op, |old, _| old); + }; + + (@new $name:ident, $ty:ty, $op:expr) => { + atomic_rmw!($name, $ty, $op, |_, new| new); + }; +} +macro_rules! atomic_cmpxchg { + ($name:ident, $ty:ty) => { + intrinsics! { + pub unsafe extern "C" fn $name(ptr: *mut $ty, oldval: $ty, newval: $ty) -> $ty { + // SAFETY: the caller must guarantee that the pointer is valid for read and write + // and aligned to the element size. + unsafe { <$ty as Atomic>::cmpxchg(ptr, oldval, newval) } + } + } + }; +} + +include!("arm_thumb_shared.rs"); + +intrinsics! { + pub unsafe extern "C" fn __sync_synchronize() { + // SAFETY: preconditions are the same as the calling function. + unsafe { fence() }; + } +} diff --git a/library/compiler-builtins/crates/libm-macros/Cargo.toml b/library/compiler-builtins/crates/libm-macros/Cargo.toml index 100a8d0ec30e..f99a92e21c70 100644 --- a/library/compiler-builtins/crates/libm-macros/Cargo.toml +++ b/library/compiler-builtins/crates/libm-macros/Cargo.toml @@ -9,10 +9,10 @@ license = "MIT OR Apache-2.0" proc-macro = true [dependencies] -heck = "0.5.0" -proc-macro2 = "1.0.95" -quote = "1.0.40" -syn = { version = "2.0.104", features = ["full", "extra-traits", "visit-mut"] } +heck.workspace = true +proc-macro2.workspace = true +quote.workspace = true +syn = { workspace = true, features = ["full", "extra-traits", "visit-mut"] } [lints.rust] # Values used during testing diff --git a/library/compiler-builtins/crates/musl-math-sys/Cargo.toml b/library/compiler-builtins/crates/musl-math-sys/Cargo.toml index 39f6fa9065bd..eb97ffbc8669 100644 --- a/library/compiler-builtins/crates/musl-math-sys/Cargo.toml +++ b/library/compiler-builtins/crates/musl-math-sys/Cargo.toml @@ -5,10 +5,8 @@ edition = "2024" publish = false license = "MIT OR Apache-2.0" -[dependencies] - [dev-dependencies] -libm = { path = "../../libm" } +libm.workspace = true [build-dependencies] -cc = "1.2.29" +cc.workspace = true diff --git a/library/compiler-builtins/crates/symbol-check/Cargo.toml b/library/compiler-builtins/crates/symbol-check/Cargo.toml index e2218b491720..5bc13d337c27 100644 --- a/library/compiler-builtins/crates/symbol-check/Cargo.toml +++ b/library/compiler-builtins/crates/symbol-check/Cargo.toml @@ -5,8 +5,11 @@ edition = "2024" publish = false [dependencies] -object = "0.37.1" -serde_json = "1.0.140" +object.workspace = true +regex.workspace = true +serde_json.workspace = true -[features] -wasm = ["object/wasm"] +[dev-dependencies] +assert_cmd.workspace = true +cc.workspace = true +tempfile.workspace = true diff --git a/library/compiler-builtins/crates/symbol-check/build.rs b/library/compiler-builtins/crates/symbol-check/build.rs new file mode 100644 index 000000000000..b3e53c38b0bd --- /dev/null +++ b/library/compiler-builtins/crates/symbol-check/build.rs @@ -0,0 +1,6 @@ +use std::env; + +fn main() { + println!("cargo::rustc-env=HOST={}", env::var("HOST").unwrap()); + println!("cargo::rustc-env=TARGET={}", env::var("TARGET").unwrap()); +} diff --git a/library/compiler-builtins/crates/symbol-check/src/main.rs b/library/compiler-builtins/crates/symbol-check/src/main.rs index 7d0b7e90addb..733d9f4e8bef 100644 --- a/library/compiler-builtins/crates/symbol-check/src/main.rs +++ b/library/compiler-builtins/crates/symbol-check/src/main.rs @@ -1,17 +1,22 @@ //! Tool used by CI to inspect compiler-builtins archives and help ensure we won't run into any //! linking errors. +//! +//! Note that symcheck is a "hostprog", i.e. is built and run on the host target even when the +//! actual target is cross compiled. -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::fs; use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; +use std::sync::LazyLock; use object::read::archive::ArchiveFile; use object::{ File as ObjFile, Object, ObjectSection, ObjectSymbol, Result as ObjResult, Symbol, SymbolKind, SymbolScope, }; +use regex::Regex; use serde_json::Value; const CHECK_LIBRARIES: &[&str] = &["compiler_builtins", "builtins_test_intrinsics"]; @@ -42,8 +47,7 @@ fn main() { run_build_and_check(target, args); } ["build-and-check", "--", args @ ..] if !args.is_empty() => { - let target = &host_target(); - run_build_and_check(target, args); + run_build_and_check(env!("HOST"), args); } ["check", paths @ ..] if !paths.is_empty() => { check_paths(paths); @@ -80,20 +84,6 @@ fn check_paths>(paths: &[P]) { } } -fn host_target() -> String { - let out = Command::new("rustc") - .arg("--version") - .arg("--verbose") - .output() - .unwrap(); - assert!(out.status.success()); - let out = String::from_utf8(out.stdout).unwrap(); - out.lines() - .find_map(|s| s.strip_prefix("host: ")) - .unwrap() - .to_owned() -} - /// Run `cargo build` with the provided additional arguments, collecting the list of created /// libraries. fn exec_cargo_with_args(target: &str, args: &[&str]) -> Vec { @@ -257,8 +247,9 @@ fn verify_no_duplicates(archive: &BinFile) { assert!(found_any, "no symbols found"); if !dups.is_empty() { + let count = dups.iter().map(|x| &x.name).collect::>().len(); dups.sort_unstable_by(|a, b| a.name.cmp(&b.name)); - panic!("found duplicate symbols: {dups:#?}"); + panic!("found {count} duplicate symbols: {dups:#?}"); } println!(" success: no duplicate symbols found"); @@ -266,6 +257,14 @@ fn verify_no_duplicates(archive: &BinFile) { /// Ensure that there are no references to symbols from `core` that aren't also (somehow) defined. fn verify_core_symbols(archive: &BinFile) { + // Match both mangling styles: + // + // * `_ZN4core3str8converts9from_utf817hd4454ac14cbbb790E` (old) + // * `_RNvNtNtCscK9O3IwVk7N_4core3str8converts9from_utf8` (v0) + // + // Also account for the Apple leading `_`. + static RE: LazyLock = LazyLock::new(|| Regex::new(r"^_?_[RZ].*4core").unwrap()); + let mut defined = BTreeSet::new(); let mut undefined = Vec::new(); let mut has_symbols = false; @@ -274,7 +273,7 @@ fn verify_core_symbols(archive: &BinFile) { has_symbols = true; // Find only symbols from `core` - if !symbol.name().unwrap().contains("_ZN4core") { + if !RE.is_match(symbol.name().unwrap()) { return; } @@ -293,7 +292,10 @@ fn verify_core_symbols(archive: &BinFile) { if !undefined.is_empty() { undefined.sort_unstable_by(|a, b| a.name.cmp(&b.name)); - panic!("found undefined symbols from core: {undefined:#?}"); + panic!( + "found {} undefined symbols from core: {undefined:#?}", + undefined.len() + ); } println!(" success: no undefined references to core found"); diff --git a/library/compiler-builtins/crates/symbol-check/tests/all.rs b/library/compiler-builtins/crates/symbol-check/tests/all.rs new file mode 100644 index 000000000000..400469a49e2a --- /dev/null +++ b/library/compiler-builtins/crates/symbol-check/tests/all.rs @@ -0,0 +1,126 @@ +use std::env; +use std::ffi::OsString; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; +use std::sync::LazyLock; + +use assert_cmd::assert::Assert; +use assert_cmd::cargo::cargo_bin_cmd; +use tempfile::tempdir; + +trait AssertExt { + fn stderr_contains(self, s: &str) -> Self; +} + +impl AssertExt for Assert { + fn stderr_contains(self, s: &str) -> Self { + let out = String::from_utf8_lossy(&self.get_output().stderr); + assert!(out.contains(s), "looking for: `{s}`\nout:\n```\n{out}\n```"); + self + } +} + +#[test] +fn test_duplicates() { + let dir = tempdir().unwrap(); + let dup_out = dir.path().join("dup.o"); + let lib_out = dir.path().join("libfoo.rlib"); + + // For the "bad" file, we need duplicate symbols from different object files in the archive. Do + // this reliably by building an archive and a separate object file then merging them. + rustc_build(&input_dir().join("duplicates.rs"), &lib_out, |cmd| cmd); + rustc_build(&input_dir().join("duplicates.rs"), &dup_out, |cmd| { + cmd.arg("--emit=obj") + }); + + let mut ar = cc_build().get_archiver(); + + if ar.get_program().to_string_lossy().contains("lib.exe") { + let mut out_arg = OsString::from("-out:"); + out_arg.push(&lib_out); + ar.arg(&out_arg); + // Repeating the same file as the first arg makes lib.exe append (taken from the + // `cc` implementation). + ar.arg(&lib_out); + } else { + ar.arg("rs") + // Eat an `libfoo.rlib(lib.rmeta) has no symbols` info message on MacOS + .stderr(Stdio::null()) + .arg(&lib_out); + } + let status = ar.arg(&dup_out).status().unwrap(); + assert!(status.success()); + + let assert = cargo_bin_cmd!().arg("check").arg(&lib_out).assert(); + assert + .failure() + .stderr_contains("duplicate symbols") + .stderr_contains("FDUP") + .stderr_contains("IDUP") + .stderr_contains("fndup"); +} + +#[test] +fn test_core_symbols() { + let dir = tempdir().unwrap(); + let lib_out = dir.path().join("libfoo.rlib"); + rustc_build(&input_dir().join("core_symbols.rs"), &lib_out, |cmd| cmd); + let assert = cargo_bin_cmd!().arg("check").arg(&lib_out).assert(); + assert + .failure() + .stderr_contains("found 1 undefined symbols from core") + .stderr_contains("from_utf8"); +} + +#[test] +fn test_good() { + let dir = tempdir().unwrap(); + let lib_out = dir.path().join("libfoo.rlib"); + rustc_build(&input_dir().join("good.rs"), &lib_out, |cmd| cmd); + let assert = cargo_bin_cmd!().arg("check").arg(&lib_out).assert(); + assert.success(); +} + +/// Build i -> o with optional additional configuration. +fn rustc_build(i: &Path, o: &Path, mut f: impl FnMut(&mut Command) -> &mut Command) { + let mut cmd = Command::new("rustc"); + cmd.arg(i) + .arg("--target") + .arg(target()) + .arg("--crate-type=lib") + .arg("-o") + .arg(o); + f(&mut cmd); + let status = cmd.status().unwrap(); + assert!(status.success()); +} + +/// Configure `cc` with the host and target. +fn cc_build() -> cc::Build { + let mut b = cc::Build::new(); + b.host(env!("HOST")).target(&target()); + b +} + +/// Symcheck runs on the host but we want to verify that we find issues on all targets, so +/// the cross target may be specified. +fn target() -> String { + static TARGET: LazyLock = LazyLock::new(|| { + let target = match env::var("SYMCHECK_TEST_TARGET") { + Ok(t) => t, + // Require on CI so we don't accidentally always test the native target + _ if env::var("CI").is_ok() => panic!("SYMCHECK_TEST_TARGET must be set in CI"), + // Fall back to native for local convenience. + Err(_) => env!("HOST").to_string(), + }; + + println!("using target {target}"); + target + }); + + TARGET.clone() +} + +fn input_dir() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/input") +} diff --git a/library/compiler-builtins/crates/symbol-check/tests/input/core_symbols.rs b/library/compiler-builtins/crates/symbol-check/tests/input/core_symbols.rs new file mode 100644 index 000000000000..cf74f9779661 --- /dev/null +++ b/library/compiler-builtins/crates/symbol-check/tests/input/core_symbols.rs @@ -0,0 +1,11 @@ +//! Ensure we catch calls to `core`. + +#![no_std] + +#[unsafe(no_mangle)] +pub fn call_from_core(s: &[u8]) -> &str { + match core::str::from_utf8(&s) { + Ok(s) => s, + Err(_) => "", + } +} diff --git a/library/compiler-builtins/crates/symbol-check/tests/input/duplicates.rs b/library/compiler-builtins/crates/symbol-check/tests/input/duplicates.rs new file mode 100644 index 000000000000..92623a0b2e1a --- /dev/null +++ b/library/compiler-builtins/crates/symbol-check/tests/input/duplicates.rs @@ -0,0 +1,12 @@ +//! Ensure we catch duplicate symbols (the duplicates are in the aux file). Gets built twice +//! as separate object files. + +#![no_std] + +#[unsafe(no_mangle)] +static IDUP: i32 = 0; +#[unsafe(no_mangle)] +static FDUP: f32 = 0.0; + +#[unsafe(no_mangle)] +pub fn fndup() {} diff --git a/library/compiler-builtins/crates/symbol-check/tests/input/good.rs b/library/compiler-builtins/crates/symbol-check/tests/input/good.rs new file mode 100644 index 000000000000..6679ee1dd30c --- /dev/null +++ b/library/compiler-builtins/crates/symbol-check/tests/input/good.rs @@ -0,0 +1,4 @@ +#![no_std] + +#[unsafe(no_mangle)] +pub fn good() {} diff --git a/library/compiler-builtins/crates/util/Cargo.toml b/library/compiler-builtins/crates/util/Cargo.toml index 614c54bd8355..c56e2cc12ea5 100644 --- a/library/compiler-builtins/crates/util/Cargo.toml +++ b/library/compiler-builtins/crates/util/Cargo.toml @@ -5,15 +5,15 @@ edition = "2024" publish = false license = "MIT OR Apache-2.0" +[dependencies] +libm.workspace = true +libm-macros.workspace = true +libm-test.workspace = true +musl-math-sys = { workspace = true, optional = true } +rug = { workspace = true, optional = true } + [features] default = ["build-musl", "build-mpfr", "unstable-float"] build-musl = ["libm-test/build-musl", "dep:musl-math-sys"] build-mpfr = ["libm-test/build-mpfr", "dep:rug"] unstable-float = ["libm/unstable-float", "libm-test/unstable-float", "rug?/nightly-float"] - -[dependencies] -libm = { path = "../../libm", default-features = false } -libm-macros = { path = "../libm-macros" } -libm-test = { path = "../../libm-test", default-features = false } -musl-math-sys = { path = "../musl-math-sys", optional = true } -rug = { version = "1.27.0", optional = true, default-features = false, features = ["float", "std"] } diff --git a/library/compiler-builtins/etc/thumbv6-none-eabi.json b/library/compiler-builtins/etc/thumbv6-none-eabi.json new file mode 100644 index 000000000000..4c1f760ac3e0 --- /dev/null +++ b/library/compiler-builtins/etc/thumbv6-none-eabi.json @@ -0,0 +1,20 @@ +{ + "abi": "eabi", + "arch": "arm", + "asm-args": ["-mthumb-interwork", "-march=armv6", "-mlittle-endian"], + "c-enum-min-bits": 8, + "crt-objects-fallback": "false", + "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", + "emit-debug-gdb-scripts": false, + "features": "+soft-float,+strict-align,+v6k", + "frame-pointer": "always", + "has-thumb-interworking": true, + "linker": "rust-lld", + "linker-flavor": "gnu-lld", + "llvm-floatabi": "soft", + "llvm-target": "thumbv6-none-eabi", + "max-atomic-width": 32, + "panic-strategy": "abort", + "relocation-model": "static", + "target-pointer-width": 32 +} diff --git a/library/compiler-builtins/libm-test/Cargo.toml b/library/compiler-builtins/libm-test/Cargo.toml index adecfc1af6b8..4f65504bd584 100644 --- a/library/compiler-builtins/libm-test/Cargo.toml +++ b/library/compiler-builtins/libm-test/Cargo.toml @@ -5,6 +5,31 @@ edition = "2024" publish = false license = "MIT OR Apache-2.0" +[dependencies] +anyhow.workspace = true +# This is not directly used but is required so we can enable `gmp-mpfr-sys/force-cross`. +gmp-mpfr-sys = { workspace = true, optional = true } +gungraun = { workspace = true, optional = true } +indicatif.workspace = true +libm = { workspace = true, default-features = true, features = ["unstable-public-internals"] } +libm-macros.workspace = true +musl-math-sys = { workspace = true, optional = true } +paste.workspace = true +rand.workspace = true +rand_chacha.workspace = true +rayon.workspace = true +rug = { workspace = true, optional = true } + +[target.'cfg(target_family = "wasm")'.dependencies] +getrandom = { workspace = true, features = ["wasm_js"] } + +[build-dependencies] +rand = { workspace = true, optional = true } + +[dev-dependencies] +criterion.workspace = true +libtest-mimic.workspace = true + [features] default = ["build-mpfr", "unstable-float"] @@ -27,31 +52,6 @@ icount = ["dep:gungraun"] # Run with a reduced set of benchmarks, such as for CI short-benchmarks = [] -[dependencies] -anyhow = "1.0.98" -# This is not directly used but is required so we can enable `gmp-mpfr-sys/force-cross`. -gmp-mpfr-sys = { version = "1.6.5", optional = true, default-features = false } -gungraun = { version = "0.17.0", optional = true } -indicatif = { version = "0.18.0", default-features = false } -libm = { path = "../libm", features = ["unstable-public-internals"] } -libm-macros = { path = "../crates/libm-macros" } -musl-math-sys = { path = "../crates/musl-math-sys", optional = true } -paste = "1.0.15" -rand = "0.9.1" -rand_chacha = "0.9.0" -rayon = "1.10.0" -rug = { version = "1.27.0", optional = true, default-features = false, features = ["float", "integer", "std"] } - -[target.'cfg(target_family = "wasm")'.dependencies] -getrandom = { version = "0.3.3", features = ["wasm_js"] } - -[build-dependencies] -rand = { version = "0.9.1", optional = true } - -[dev-dependencies] -criterion = { version = "0.6.0", default-features = false, features = ["cargo_bench_support"] } -libtest-mimic = "0.8.1" - [[bench]] name = "icount" harness = false diff --git a/library/compiler-builtins/libm-test/src/domain.rs b/library/compiler-builtins/libm-test/src/domain.rs index 94641be9b548..eb009bfa093f 100644 --- a/library/compiler-builtins/libm-test/src/domain.rs +++ b/library/compiler-builtins/libm-test/src/domain.rs @@ -207,7 +207,7 @@ impl EitherPrim, Domain> { .into_prim_float()]; /// Domain for `loggamma` - const LGAMMA: [Self; 1] = Self::STRICTLY_POSITIVE; + const LGAMMA: [Self; 1] = Self::UNBOUNDED1; /// Domain for `jn` and `yn`. // FIXME: the domain should provide some sort of "reasonable range" so we don't actually test diff --git a/library/compiler-builtins/libm-test/src/lib.rs b/library/compiler-builtins/libm-test/src/lib.rs index accb39654d15..60d96ae9bcee 100644 --- a/library/compiler-builtins/libm-test/src/lib.rs +++ b/library/compiler-builtins/libm-test/src/lib.rs @@ -1,6 +1,7 @@ #![cfg_attr(f16_enabled, feature(f16))] #![cfg_attr(f128_enabled, feature(f128))] #![allow(clippy::unusual_byte_groupings)] // sometimes we group by sign_exp_sig +#![allow(unstable_name_collisions)] // FIXME(float_bits_const): remove when stable pub mod domain; mod f8_impl; diff --git a/library/compiler-builtins/libm-test/src/mpfloat.rs b/library/compiler-builtins/libm-test/src/mpfloat.rs index 9b51dc6051d0..85f0a4da4a6e 100644 --- a/library/compiler-builtins/libm-test/src/mpfloat.rs +++ b/library/compiler-builtins/libm-test/src/mpfloat.rs @@ -170,7 +170,9 @@ libm_macros::for_each_function! { ldexpf, ldexpf128, ldexpf16, + lgamma, lgamma_r, + lgammaf, lgammaf_r, modf, modff, @@ -213,7 +215,6 @@ libm_macros::for_each_function! { fmaximum_num | fmaximum_numf | fmaximum_numf16 | fmaximum_numf128 => max, fmin | fminf | fminf16 | fminf128 | fminimum_num | fminimum_numf | fminimum_numf16 | fminimum_numf128 => min, - lgamma | lgammaf => ln_gamma, log | logf => ln, log1p | log1pf => ln_1p, tgamma | tgammaf => gamma, @@ -576,6 +577,30 @@ impl MpOp for crate::op::lgammaf_r::Routine { } } +impl MpOp for crate::op::lgamma::Routine { + type MpTy = MpFloat; + + fn new_mp() -> Self::MpTy { + new_mpfloat::() + } + + fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { + ::run(this, input).0 + } +} + +impl MpOp for crate::op::lgammaf::Routine { + type MpTy = MpFloat; + + fn new_mp() -> Self::MpTy { + new_mpfloat::() + } + + fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { + ::run(this, input).0 + } +} + /* stub implementations so we don't need to special case them */ impl MpOp for crate::op::nextafter::Routine { diff --git a/library/compiler-builtins/libm-test/src/precision.rs b/library/compiler-builtins/libm-test/src/precision.rs index 7887c032394b..5d52da168fe7 100644 --- a/library/compiler-builtins/libm-test/src/precision.rs +++ b/library/compiler-builtins/libm-test/src/precision.rs @@ -67,7 +67,7 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 { Bn::Exp2 => 1, Bn::Expm1 => 1, Bn::Hypot => 1, - Bn::Lgamma | Bn::LgammaR => 16, + Bn::Lgamma | Bn::LgammaR => 4, Bn::Log => 1, Bn::Log10 => 1, Bn::Log1p => 1, @@ -102,7 +102,6 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 { match ctx.base_name { Bn::Cosh => ulp = 2, Bn::Exp10 if usize::BITS < 64 => ulp = 4, - Bn::Lgamma | Bn::LgammaR => ulp = 400, Bn::Tanh => ulp = 4, _ => (), } @@ -218,17 +217,17 @@ impl MaybeOverride<(f16,)> for SpecialCase {} impl MaybeOverride<(f32,)> for SpecialCase { fn check_float(input: (f32,), actual: F, expected: F, ctx: &CheckCtx) -> CheckAction { - if (ctx.base_name == BaseName::Lgamma || ctx.base_name == BaseName::LgammaR) - && input.0 > 4e36 - && expected.is_infinite() - && !actual.is_infinite() - { - // This result should saturate but we return a finite value. + if ctx.base_name == BaseName::J0 && input.0 < -1e34 { + // Errors get huge close to -inf return XFAIL_NOCHECK; } - if ctx.base_name == BaseName::J0 && input.0 < -1e34 { - // Errors get huge close to -inf + // FIXME(correctness): lgammaf has high relative inaccuracy near its zeroes + if matches!(ctx.base_name, BaseName::Lgamma | BaseName::LgammaR) + && input.0 > -13.0625 + && input.0 < -2.0 + && (expected.abs() < F::ONE || (input.0 - input.0.round()).abs() < 0.02) + { return XFAIL_NOCHECK; } @@ -266,6 +265,24 @@ impl MaybeOverride<(f64,)> for SpecialCase { return XFAIL_NOCHECK; } + if ctx.base_name == BaseName::Acosh + && input.0 < 1.0 + && actual.is_nan() + && ctx.basis == CheckBasis::Musl + { + // Musl sometimes evaluates acosh(negative) to a numeric value + return XFAIL_NOCHECK; + } + + // FIXME(correctness): lgamma has high relative inaccuracy near its zeroes + if matches!(ctx.base_name, BaseName::Lgamma | BaseName::LgammaR) + && input.0 > -32.0 + && input.0 < -2.0 + && (expected.abs() < F::ONE || (input.0 - input.0.round()).abs() < 0.02) + { + return XFAIL_NOCHECK; + } + // maybe_check_nan_bits(actual, expected, ctx) unop_common(input, actual, expected, ctx) } @@ -295,27 +312,6 @@ fn unop_common( expected: F2, ctx: &CheckCtx, ) -> CheckAction { - if ctx.base_name == BaseName::Acosh - && input.0 < F1::NEG_ONE - && !(expected.is_nan() && actual.is_nan()) - { - // acoshf is undefined for x <= 1.0, but we return a random result at lower values. - - if ctx.basis == CheckBasis::Musl { - return XFAIL_NOCHECK; - } - - return XFAIL("acoshf undefined"); - } - - if (ctx.base_name == BaseName::Lgamma || ctx.base_name == BaseName::LgammaR) - && input.0 < F1::ZERO - && !input.0.is_infinite() - { - // loggamma should not be defined for x < 0, yet we both return results - return XFAIL_NOCHECK; - } - // fabs and copysign must leave NaNs untouched. if ctx.base_name == BaseName::Fabs && input.0.is_nan() { // LLVM currently uses x87 instructions which quieten signalling NaNs to handle the i686 @@ -401,14 +397,6 @@ fn binop_common( return SKIP; } - // FIXME(#939): this should not be skipped, there is a bug in our implementationi. - if ctx.base_name == BaseName::FmaximumNum - && ctx.basis == CheckBasis::Mpfr - && ((input.0.is_nan() && actual.is_nan() && expected.is_nan()) || input.1.is_nan()) - { - return XFAIL_NOCHECK; - } - /* FIXME(#439): our fmin and fmax do not compare signed zeros */ if ctx.base_name == BaseName::Fmin @@ -510,7 +498,7 @@ fn int_float_common( if input.0 > 4000 { return XFAIL_NOCHECK; } else if input.0 > 100 { - return CheckAction::AssertWithUlp(1_000_000); + return CheckAction::AssertWithUlp(2_000_000); } } DEFAULT diff --git a/library/compiler-builtins/libm/CHANGELOG.md b/library/compiler-builtins/libm/CHANGELOG.md index 33fec06aa237..037f79ef3ef1 100644 --- a/library/compiler-builtins/libm/CHANGELOG.md +++ b/library/compiler-builtins/libm/CHANGELOG.md @@ -8,6 +8,22 @@ and this project adheres to ## [Unreleased] +## [0.2.16](https://github.com/rust-lang/compiler-builtins/compare/libm-v0.2.15...libm-v0.2.16) - 2025-12-07 + +### Fixed + +- Fix an incorrect result for `fminimum` and `fmaximum` with the input (-0, NaN) +- Fix a typo in `libm::Libm::roundeven` +- Fix the `expm1f` overflow threshold +- Change `CmpResult` to use a pointer-sized return type +- Compare against `CARGO_CFG_TARGET_FAMILY` in a multi-valued fashion +- Implement `exp` and its variants for i586 with inline assembly +- Implement `floor` and `ceil` in assembly on `i586` + +### Other + +- Significantly optimize `fmod` worst case performance ([#1002](https://github.com/rust-lang/compiler-builtins/pull/1002)) + ## [0.2.15](https://github.com/rust-lang/compiler-builtins/compare/libm-v0.2.14...libm-v0.2.15) - 2025-05-06 ### Other diff --git a/library/compiler-builtins/libm/Cargo.toml b/library/compiler-builtins/libm/Cargo.toml index 5b5ca34fd2c9..98202d1977dc 100644 --- a/library/compiler-builtins/libm/Cargo.toml +++ b/library/compiler-builtins/libm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libm" -version = "0.2.15" +version = "0.2.16" authors = [ "Alex Crichton ", "Amanieu d'Antras ", @@ -13,7 +13,11 @@ keywords = ["libm", "math"] repository = "https://github.com/rust-lang/compiler-builtins" license = "MIT" edition = "2021" -rust-version = "1.63" +rust-version = "1.67" + +[dev-dependencies] +# FIXME(msrv): switch to `no-panic.workspace` when possible +no-panic = "0.1.35" [features] default = ["arch"] @@ -42,9 +46,6 @@ unstable-float = [] # hard float operations. force-soft-floats = [] -[dev-dependencies] -no-panic = "0.1.35" - [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = [ # compiler-builtins sets this feature, but we use it in `libm` diff --git a/library/compiler-builtins/libm/src/lib.rs b/library/compiler-builtins/libm/src/lib.rs index 31b12235314c..85ed5e2c9fc6 100644 --- a/library/compiler-builtins/libm/src/lib.rs +++ b/library/compiler-builtins/libm/src/lib.rs @@ -8,6 +8,7 @@ )] #![cfg_attr(f128_enabled, feature(f128))] #![cfg_attr(f16_enabled, feature(f16))] +#![allow(unstable_name_collisions)] // FIXME(float_bits_const): remove when stable #![allow(clippy::assign_op_pattern)] #![allow(clippy::deprecated_cfg_attr)] #![allow(clippy::eq_op)] diff --git a/library/compiler-builtins/libm/src/math/acosh.rs b/library/compiler-builtins/libm/src/math/acosh.rs index 8737bad012c8..2904fc0ed520 100644 --- a/library/compiler-builtins/libm/src/math/acosh.rs +++ b/library/compiler-builtins/libm/src/math/acosh.rs @@ -1,4 +1,4 @@ -use super::{log, log1p, sqrt}; +use super::{Float, log, log1p, sqrt}; const LN2: f64 = 0.693147180559945309417232121458176568; /* 0x3fe62e42, 0xfefa39ef*/ @@ -9,19 +9,19 @@ const LN2: f64 = 0.693147180559945309417232121458176568; /* 0x3fe62e42, 0xfefa3 /// `x` must be a number greater than or equal to 1. #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn acosh(x: f64) -> f64 { - let u = x.to_bits(); - let e = ((u >> 52) as usize) & 0x7ff; + let ux = x.to_bits(); /* x < 1 domain error is handled in the called functions */ - - if e < 0x3ff + 1 { - /* |x| < 2, up to 2ulp error in [1,1.125] */ - return log1p(x - 1.0 + sqrt((x - 1.0) * (x - 1.0) + 2.0 * (x - 1.0))); + if (ux & !f64::SIGN_MASK) < 2_f64.to_bits() { + /* |x| < 2, invalid if x < 1 */ + /* up to 2ulp error in [1,1.125] */ + let x_1 = x - 1.0; + log1p(x_1 + sqrt(x_1 * x_1 + 2.0 * x_1)) + } else if ux < ((1 << 26) as f64).to_bits() { + /* 2 <= x < 0x1p26 */ + log(2.0 * x - 1.0 / (x + sqrt(x * x - 1.0))) + } else { + /* x >= 0x1p26 or x <= -2 or nan */ + log(x) + LN2 } - if e < 0x3ff + 26 { - /* |x| < 0x1p26 */ - return log(2.0 * x - 1.0 / (x + sqrt(x * x - 1.0))); - } - /* |x| >= 0x1p26 or nan */ - return log(x) + LN2; } diff --git a/library/compiler-builtins/libm/src/math/acoshf.rs b/library/compiler-builtins/libm/src/math/acoshf.rs index 432fa03f1163..d9aafaabdef4 100644 --- a/library/compiler-builtins/libm/src/math/acoshf.rs +++ b/library/compiler-builtins/libm/src/math/acoshf.rs @@ -1,4 +1,4 @@ -use super::{log1pf, logf, sqrtf}; +use super::{Float, log1pf, logf, sqrtf}; const LN2: f32 = 0.693147180559945309417232121458176568; @@ -9,18 +9,19 @@ const LN2: f32 = 0.693147180559945309417232121458176568; /// `x` must be a number greater than or equal to 1. #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn acoshf(x: f32) -> f32 { - let u = x.to_bits(); - let a = u & 0x7fffffff; + let ux = x.to_bits(); - if a < 0x3f800000 + (1 << 23) { - /* |x| < 2, invalid if x < 1 or nan */ + /* x < 1 domain error is handled in the called functions */ + if (ux & !f32::SIGN_MASK) < 2_f32.to_bits() { + /* |x| < 2, invalid if x < 1 */ /* up to 2ulp error in [1,1.125] */ - return log1pf(x - 1.0 + sqrtf((x - 1.0) * (x - 1.0) + 2.0 * (x - 1.0))); + let x_1 = x - 1.0; + log1pf(x_1 + sqrtf(x_1 * x_1 + 2.0 * x_1)) + } else if ux < ((1 << 12) as f32).to_bits() { + /* 2 <= x < 0x1p12 */ + logf(2.0 * x - 1.0 / (x + sqrtf(x * x - 1.0))) + } else { + /* x >= 0x1p12 or x <= -2 or nan */ + logf(x) + LN2 } - if a < 0x3f800000 + (12 << 23) { - /* |x| < 0x1p12 */ - return logf(2.0 * x - 1.0 / (x + sqrtf(x * x - 1.0))); - } - /* x >= 0x1p12 */ - return logf(x) + LN2; } diff --git a/library/compiler-builtins/libm/src/math/generic/sqrt.rs b/library/compiler-builtins/libm/src/math/generic/sqrt.rs index 9481c4cdb7bb..e97a43d34956 100644 --- a/library/compiler-builtins/libm/src/math/generic/sqrt.rs +++ b/library/compiler-builtins/libm/src/math/generic/sqrt.rs @@ -419,9 +419,7 @@ mod tests { fn conformance_tests_f16() { let cases = [ (f16::PI, 0x3f17_u16), - // 10_000.0, using a hex literal for MSRV hack (Rust < 1.67 checks literal widths as - // part of the AST, so the `cfg` is irrelevant here). - (f16::from_bits(0x70e2), 0x5640_u16), + (10000.0_f16, 0x5640_u16), (f16::from_bits(0x0000000f), 0x13bf_u16), (f16::INFINITY, f16::INFINITY.to_bits()), ]; diff --git a/library/compiler-builtins/libm/src/math/support/float_traits.rs b/library/compiler-builtins/libm/src/math/support/float_traits.rs index 4e5011f62e0f..60c8bfca5165 100644 --- a/library/compiler-builtins/libm/src/math/support/float_traits.rs +++ b/library/compiler-builtins/libm/src/math/support/float_traits.rs @@ -1,5 +1,3 @@ -#![allow(unknown_lints)] // FIXME(msrv) we shouldn't need this - use core::{fmt, mem, ops}; use super::int_traits::{CastFrom, Int, MinInt}; @@ -289,10 +287,7 @@ macro_rules! float_impl { cfg_if! { // fma is not yet available in `core` if #[cfg(intrinsics_enabled)] { - // FIXME(msrv,bench): once our benchmark rustc version is above the - // 2022-09-23 nightly, this can be removed. - #[allow(unused_unsafe)] - unsafe { core::intrinsics::$fma_intrinsic(self, y, z) } + core::intrinsics::$fma_intrinsic(self, y, z) } else { super::super::$fma_fn(self, y, z) } diff --git a/library/compiler-builtins/libm/src/math/support/hex_float.rs b/library/compiler-builtins/libm/src/math/support/hex_float.rs index c8558b90053d..2f9369e50441 100644 --- a/library/compiler-builtins/libm/src/math/support/hex_float.rs +++ b/library/compiler-builtins/libm/src/math/support/hex_float.rs @@ -121,7 +121,7 @@ const fn parse_finite( Ok(Parsed { sig, exp }) => (sig, exp), }; - let mut round_bits = u128_ilog2(sig) as i32 - sig_bits as i32; + let mut round_bits = sig.ilog2() as i32 - sig_bits as i32; // Round at least up to min_lsb if exp < min_lsb - round_bits { @@ -299,29 +299,11 @@ const fn parse_hex(mut b: &[u8]) -> Result { )); }; - { - let e; - if negate_exp { - e = (exp as i64) - (pexp as i64); - } else { - e = (exp as i64) + (pexp as i64); - }; - - exp = if e < i32::MIN as i64 { - i32::MIN - } else if e > i32::MAX as i64 { - i32::MAX - } else { - e as i32 - }; - } - /* FIXME(msrv): once MSRV >= 1.66, replace the above workaround block with: if negate_exp { exp = exp.saturating_sub_unsigned(pexp); } else { exp = exp.saturating_add_unsigned(pexp); }; - */ Ok(Parsed { sig, exp }) } @@ -342,14 +324,6 @@ const fn hex_digit(c: u8) -> Option { } } -/* FIXME(msrv): vendor some things that are not const stable at our MSRV */ - -/// `u128::ilog2` -const fn u128_ilog2(v: u128) -> u32 { - assert!(v != 0); - u128::BITS - 1 - v.leading_zeros() -} - #[cfg(any(test, feature = "unstable-public-internals"))] mod hex_fmt { use core::fmt; diff --git a/library/compiler-builtins/rust-version b/library/compiler-builtins/rust-version index 6a2835bc2d9e..aa3876b14a22 100644 --- a/library/compiler-builtins/rust-version +++ b/library/compiler-builtins/rust-version @@ -1 +1 @@ -23d01cd2412583491621ab1ca4f1b01e37d11e39 +db3e99bbab28c6ca778b13222becdea54533d908 diff --git a/library/compiler-builtins/triagebot.toml b/library/compiler-builtins/triagebot.toml index b210a5fb5256..d0cdb497edba 100644 --- a/library/compiler-builtins/triagebot.toml +++ b/library/compiler-builtins/triagebot.toml @@ -10,7 +10,7 @@ exclude_titles = ["Rustc pull update"] # when commits are included in subtrees, as well as warning links in commits. # Documentation at: https://forge.rust-lang.org/triagebot/issue-links.html [issue-links] -check-commits = false +check-commits = "uncanonicalized" # Enable issue transfers within the org # Documentation at: https://forge.rust-lang.org/triagebot/transfer.html diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index 9b80e3b70fa2..bf3d3e0a5aca 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -57,7 +57,7 @@ use crate::{cmp, ptr}; /// let mut allocated = 0; /// if self /// .remaining -/// .fetch_update(Relaxed, Relaxed, |mut remaining| { +/// .try_update(Relaxed, Relaxed, |mut remaining| { /// if size > remaining { /// return None; /// } @@ -284,9 +284,10 @@ pub unsafe trait GlobalAlloc { /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - // SAFETY: the caller must ensure that the `new_size` does not overflow. - // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid. - let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; + let alignment = layout.alignment(); + // SAFETY: the caller must ensure that the `new_size` does not overflow + // when rounded up to the next multiple of `alignment`. + let new_layout = unsafe { Layout::from_size_alignment_unchecked(new_size, alignment) }; // SAFETY: the caller must ensure that `new_layout` is greater than zero. let new_ptr = unsafe { self.alloc(new_layout) }; if !new_ptr.is_null() { diff --git a/library/core/src/array/drain.rs b/library/core/src/array/drain.rs index 1c6137191324..17792dca583d 100644 --- a/library/core/src/array/drain.rs +++ b/library/core/src/array/drain.rs @@ -31,7 +31,6 @@ impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { } /// See [`Drain::new`]; this is our fake iterator. -#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] pub(super) struct Drain<'l, 'f, T, const N: usize, F> { // FIXME(const-hack): This is essentially a slice::IterMut<'static>, replace when possible. diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 6cca2e6358b6..4a2cf1527744 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -516,20 +516,47 @@ impl [T; N] { /// /// # Note on performance and stack usage /// - /// Unfortunately, usages of this method are currently not always optimized - /// as well as they could be. This mainly concerns large arrays, as mapping - /// over small arrays seem to be optimized just fine. Also note that in - /// debug mode (i.e. without any optimizations), this method can use a lot - /// of stack space (a few times the size of the array or more). + /// Note that this method is *eager*. It evaluates `f` all `N` times before + /// returning the new array. /// - /// Therefore, in performance-critical code, try to avoid using this method - /// on large arrays or check the emitted code. Also try to avoid chained - /// maps (e.g. `arr.map(...).map(...)`). + /// That means that `arr.map(f).map(g)` is, in general, *not* equivalent to + /// `array.map(|x| g(f(x)))`, as the former calls `f` 4 times then `g` 4 times, + /// whereas the latter interleaves the calls (`fgfgfgfg`). /// - /// In many cases, you can instead use [`Iterator::map`] by calling `.iter()` - /// or `.into_iter()` on your array. `[T; N]::map` is only necessary if you - /// really need a new array of the same size as the result. Rust's lazy - /// iterators tend to get optimized very well. + /// A consequence of this is that it can have fairly-high stack usage, especially + /// in debug mode or for long arrays. The backend may be able to optimize it + /// away, but especially for complicated mappings it might not be able to. + /// + /// If you're doing a one-step `map` and really want an array as the result, + /// then absolutely use this method. Its implementation uses a bunch of tricks + /// to help the optimizer handle it well. Particularly for simple arrays, + /// like `[u8; 3]` or `[f32; 4]`, there's nothing to be concerned about. + /// + /// However, if you don't actually need an *array* of the results specifically, + /// just to process them, then you likely want [`Iterator::map`] instead. + /// + /// For example, rather than doing an array-to-array map of all the elements + /// in the array up-front and only iterating after that completes, + /// + /// ``` + /// # let my_array = [1, 2, 3]; + /// # let f = |x: i32| x + 1; + /// for x in my_array.map(f) { + /// // ... + /// } + /// ``` + /// + /// It's often better to use an iterator along the lines of + /// + /// ``` + /// # let my_array = [1, 2, 3]; + /// # let f = |x: i32| x + 1; + /// for x in my_array.into_iter().map(f) { + /// // ... + /// } + /// ``` + /// + /// as that's more likely to avoid large temporaries. /// /// /// # Examples diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 661ea4ab6a27..a9e7c49515c7 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -689,6 +689,30 @@ impl, U> CoerceUnsized> for Cell {} #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U> DispatchFromDyn> for Cell {} +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsRef<[Cell; N]> for Cell<[T; N]> { + #[inline] + fn as_ref(&self) -> &[Cell; N] { + self.as_array_of_cells() + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsRef<[Cell]> for Cell<[T; N]> { + #[inline] + fn as_ref(&self) -> &[Cell] { + &*self.as_array_of_cells() + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsRef<[Cell]> for Cell<[T]> { + #[inline] + fn as_ref(&self) -> &[Cell] { + self.as_slice_of_cells() + } +} + impl Cell<[T]> { /// Returns a `&[Cell]` from a `&Cell<[T]>` /// diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index dccac26e07e6..3692420be8fc 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -724,7 +724,6 @@ pub const fn unlikely(b: bool) -> bool { /// # Examples /// /// ``` -/// #![feature(cold_path)] /// use core::hint::cold_path; /// /// fn foo(x: &[i32]) { @@ -750,7 +749,6 @@ pub const fn unlikely(b: bool) -> bool { /// than the branch: /// /// ``` -/// #![feature(cold_path)] /// use core::hint::cold_path; /// /// #[inline(always)] @@ -777,7 +775,8 @@ pub const fn unlikely(b: bool) -> bool { /// } /// } /// ``` -#[unstable(feature = "cold_path", issue = "136873")] +#[stable(feature = "cold_path", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "cold_path", since = "CURRENT_RUSTC_VERSION")] #[inline(always)] pub const fn cold_path() { crate::intrinsics::cold_path() diff --git a/library/core/src/index.rs b/library/core/src/index.rs index 3baefdf10cec..70372163c6e1 100644 --- a/library/core/src/index.rs +++ b/library/core/src/index.rs @@ -315,7 +315,7 @@ unsafe impl SliceIndex<[T]> for Clamp> { } #[unstable(feature = "sliceindex_wrappers", issue = "146179")] -unsafe impl SliceIndex<[T]> for Clamp> { +unsafe impl SliceIndex<[T]> for Clamp> { type Output = [T]; fn get(self, slice: &[T]) -> Option<&Self::Output> { @@ -408,7 +408,7 @@ unsafe impl SliceIndex<[T]> for Clamp> { } #[unstable(feature = "sliceindex_wrappers", issue = "146179")] -unsafe impl SliceIndex<[T]> for Clamp { +unsafe impl SliceIndex<[T]> for Clamp { type Output = [T]; fn get(self, slice: &[T]) -> Option<&Self::Output> { diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 051dda731881..3ddea90652d1 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -409,8 +409,7 @@ pub const unsafe fn assume(b: bool) { /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// This intrinsic does not have a stable counterpart. -#[unstable(feature = "core_intrinsics", issue = "none")] +/// The stabilized version of this intrinsic is [`core::hint::cold_path`]. #[rustc_intrinsic] #[rustc_nounwind] #[miri::intrinsic_fallback_is_spec] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 432ca50b3361..aaa919ece6a5 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -225,7 +225,7 @@ use prelude::rust_2024::*; #[macro_use] mod macros; -#[unstable(feature = "assert_matches", issue = "82775")] +#[stable(feature = "assert_matches", since = "CURRENT_RUSTC_VERSION")] pub use crate::macros::{assert_matches, debug_assert_matches}; #[unstable(feature = "derive_from", issue = "144889")] @@ -324,7 +324,7 @@ pub mod pat; pub mod pin; #[unstable(feature = "random", issue = "130703")] pub mod random; -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] pub mod range; pub mod result; pub mod sync; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 3176f3c06709..79eab552303e 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -147,8 +147,6 @@ macro_rules! assert_ne { /// # Examples /// /// ``` -/// #![feature(assert_matches)] -/// /// use std::assert_matches; /// /// let a = Some(345); @@ -166,7 +164,7 @@ macro_rules! assert_ne { /// assert_matches!(a, Some(x) if x > 100); /// // assert_matches!(a, Some(x) if x < 100); // panics /// ``` -#[unstable(feature = "assert_matches", issue = "82775")] +#[stable(feature = "assert_matches", since = "CURRENT_RUSTC_VERSION")] #[allow_internal_unstable(panic_internals)] #[rustc_macro_transparency = "semiopaque"] pub macro assert_matches { @@ -380,8 +378,6 @@ macro_rules! debug_assert_ne { /// # Examples /// /// ``` -/// #![feature(assert_matches)] -/// /// use std::debug_assert_matches; /// /// let a = Some(345); @@ -399,7 +395,7 @@ macro_rules! debug_assert_ne { /// debug_assert_matches!(a, Some(x) if x > 100); /// // debug_assert_matches!(a, Some(x) if x < 100); // panics /// ``` -#[unstable(feature = "assert_matches", issue = "82775")] +#[stable(feature = "assert_matches", since = "CURRENT_RUSTC_VERSION")] #[allow_internal_unstable(assert_matches)] #[rustc_macro_transparency = "semiopaque"] pub macro debug_assert_matches($($arg:tt)*) { diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 320eb97f83a4..594147720193 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1532,6 +1532,56 @@ impl MaybeUninit<[T; N]> { } } +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl From<[MaybeUninit; N]> for MaybeUninit<[T; N]> { + #[inline] + fn from(arr: [MaybeUninit; N]) -> Self { + arr.transpose() + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsRef<[MaybeUninit; N]> for MaybeUninit<[T; N]> { + #[inline] + fn as_ref(&self) -> &[MaybeUninit; N] { + // SAFETY: T and MaybeUninit have the same layout + unsafe { &*ptr::from_ref(self).cast() } + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsRef<[MaybeUninit]> for MaybeUninit<[T; N]> { + #[inline] + fn as_ref(&self) -> &[MaybeUninit] { + &*AsRef::<[MaybeUninit; N]>::as_ref(self) + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsMut<[MaybeUninit; N]> for MaybeUninit<[T; N]> { + #[inline] + fn as_mut(&mut self) -> &mut [MaybeUninit; N] { + // SAFETY: T and MaybeUninit have the same layout + unsafe { &mut *ptr::from_mut(self).cast() } + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsMut<[MaybeUninit]> for MaybeUninit<[T; N]> { + #[inline] + fn as_mut(&mut self) -> &mut [MaybeUninit] { + &mut *AsMut::<[MaybeUninit; N]>::as_mut(self) + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl From> for [MaybeUninit; N] { + #[inline] + fn from(arr: MaybeUninit<[T; N]>) -> Self { + arr.transpose() + } +} + impl [MaybeUninit; N] { /// Transposes a `[MaybeUninit; N]` into a `MaybeUninit<[T; N]>`. /// diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 7c486875a826..eb6f8f975721 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1260,7 +1260,10 @@ pub trait SizedTypeProperties: Sized { #[doc(hidden)] #[unstable(feature = "ptr_alignment_type", issue = "102070")] - const ALIGNMENT: Alignment = Alignment::of::(); + const ALIGNMENT: Alignment = { + // This can't panic since type alignment is always a power of two. + Alignment::new(Self::ALIGN).unwrap() + }; /// `true` if this type requires no storage. /// `false` if its [size](size_of) is greater than zero. diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 8b30803c97c9..c2b2cf2f270d 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -49,6 +49,12 @@ pub enum TypeKind { Slice(Slice), /// Dynamic Traits. DynTrait(DynTrait), + /// Structs. + Struct(Struct), + /// Enums. + Enum(Enum), + /// Unions. + Union(Union), /// Primitive boolean type. Bool(Bool), /// Primitive character type. @@ -81,6 +87,8 @@ pub struct Tuple { #[non_exhaustive] #[unstable(feature = "type_info", issue = "146922")] pub struct Field { + /// The name of the field. + pub name: &'static str, /// The field's type. pub ty: TypeId, /// Offset in bytes from the parent type @@ -137,6 +145,95 @@ pub struct Trait { pub is_auto: bool, } +/// Compile-time type information about arrays. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Struct { + /// Instantiated generics of the struct. + pub generics: &'static [Generic], + /// All fields of the struct. + pub fields: &'static [Field], + /// Whether the struct field list is non-exhaustive. + pub non_exhaustive: bool, +} + +/// Compile-time type information about unions. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Union { + /// Instantiated generics of the union. + pub generics: &'static [Generic], + /// All fields of the union. + pub fields: &'static [Field], +} + +/// Compile-time type information about enums. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Enum { + /// Instantiated generics of the enum. + pub generics: &'static [Generic], + /// All variants of the enum. + pub variants: &'static [Variant], + /// Whether the enum variant list is non-exhaustive. + pub non_exhaustive: bool, +} + +/// Compile-time type information about variants of enums. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Variant { + /// The name of the variant. + pub name: &'static str, + /// All fields of the variant. + pub fields: &'static [Field], + /// Whether the enum variant fields is non-exhaustive. + pub non_exhaustive: bool, +} + +/// Compile-time type information about instantiated generics of structs, enum and union variants. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub enum Generic { + /// Lifetimes. + Lifetime(Lifetime), + /// Types. + Type(GenericType), + /// Const parameters. + Const(Const), +} + +/// Compile-time type information about generic lifetimes. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Lifetime { + // No additional information to provide for now. +} + +/// Compile-time type information about instantiated generic types. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct GenericType { + /// The type itself. + pub ty: TypeId, +} + +/// Compile-time type information about generic const parameters. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Const { + /// The const's type. + pub ty: TypeId, +} + /// Compile-time type information about `bool`. #[derive(Debug)] #[non_exhaustive] diff --git a/library/core/src/num/niche_types.rs b/library/core/src/num/niche_types.rs index 9ac0eb72bdcb..33b2a6741abd 100644 --- a/library/core/src/num/niche_types.rs +++ b/library/core/src/num/niche_types.rs @@ -5,45 +5,33 @@ )] use crate::cmp::Ordering; -use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::marker::StructuralPartialEq; +use crate::{fmt, pattern_type}; macro_rules! define_valid_range_type { ($( $(#[$m:meta])* - $vis:vis struct $name:ident($int:ident as $uint:ident in $low:literal..=$high:literal); + $vis:vis struct $name:ident($int:ident is $pat:pat); )+) => {$( - #[derive(Clone, Copy, Eq)] + #[derive(Clone, Copy)] #[repr(transparent)] - #[rustc_layout_scalar_valid_range_start($low)] - #[rustc_layout_scalar_valid_range_end($high)] $(#[$m])* - $vis struct $name($int); - - const _: () = { - // With the `valid_range` attributes, it's always specified as unsigned - assert!(<$uint>::MIN == 0); - let ulow: $uint = $low; - let uhigh: $uint = $high; - assert!(ulow <= uhigh); - - assert!(size_of::<$int>() == size_of::<$uint>()); - }; - + $vis struct $name(pattern_type!($int is $pat)); impl $name { #[inline] pub const fn new(val: $int) -> Option { - if (val as $uint) >= ($low as $uint) && (val as $uint) <= ($high as $uint) { - // SAFETY: just checked the inclusive range - Some(unsafe { $name(val) }) + #[allow(non_contiguous_range_endpoints)] + if let $pat = val { + // SAFETY: just checked that the value matches the pattern + Some(unsafe { $name(crate::mem::transmute(val)) }) } else { None } } /// Constructs an instance of this type from the underlying integer - /// primitive without checking whether its zero. + /// primitive without checking whether its valid. /// /// # Safety /// Immediate language UB if `val` is not within the valid range for this @@ -51,13 +39,13 @@ macro_rules! define_valid_range_type { #[inline] pub const unsafe fn new_unchecked(val: $int) -> Self { // SAFETY: Caller promised that `val` is within the valid range. - unsafe { $name(val) } + unsafe { crate::mem::transmute(val) } } #[inline] pub const fn as_inner(self) -> $int { - // SAFETY: This is a transparent wrapper, so unwrapping it is sound - // (Not using `.0` due to MCP#807.) + // SAFETY: pattern types are always legal values of their base type + // (Not using `.0` because that has perf regressions.) unsafe { crate::mem::transmute(self) } } } @@ -67,6 +55,8 @@ macro_rules! define_valid_range_type { // by . impl StructuralPartialEq for $name {} + impl Eq for $name {} + impl PartialEq for $name { #[inline] fn eq(&self, other: &Self) -> bool { @@ -104,7 +94,7 @@ macro_rules! define_valid_range_type { } define_valid_range_type! { - pub struct Nanoseconds(u32 as u32 in 0..=999_999_999); + pub struct Nanoseconds(u32 is 0..=999_999_999); } impl Nanoseconds { @@ -120,47 +110,32 @@ impl const Default for Nanoseconds { } } -define_valid_range_type! { - pub struct NonZeroU8Inner(u8 as u8 in 1..=0xff); - pub struct NonZeroU16Inner(u16 as u16 in 1..=0xff_ff); - pub struct NonZeroU32Inner(u32 as u32 in 1..=0xffff_ffff); - pub struct NonZeroU64Inner(u64 as u64 in 1..=0xffffffff_ffffffff); - pub struct NonZeroU128Inner(u128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); - - pub struct NonZeroI8Inner(i8 as u8 in 1..=0xff); - pub struct NonZeroI16Inner(i16 as u16 in 1..=0xff_ff); - pub struct NonZeroI32Inner(i32 as u32 in 1..=0xffff_ffff); - pub struct NonZeroI64Inner(i64 as u64 in 1..=0xffffffff_ffffffff); - pub struct NonZeroI128Inner(i128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); - - pub struct NonZeroCharInner(char as u32 in 1..=0x10ffff); -} - -#[cfg(target_pointer_width = "16")] -define_valid_range_type! { - pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff); - pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff); - pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff); -} -#[cfg(target_pointer_width = "32")] -define_valid_range_type! { - pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff); - pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff); - pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff); -} -#[cfg(target_pointer_width = "64")] -define_valid_range_type! { - pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff_ffff_ffff); - pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff_ffff_ffff); - pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff_ffff_ffff); -} +const HALF_USIZE: usize = usize::MAX >> 1; define_valid_range_type! { - pub struct U32NotAllOnes(u32 as u32 in 0..=0xffff_fffe); - pub struct I32NotAllOnes(i32 as u32 in 0..=0xffff_fffe); + pub struct NonZeroU8Inner(u8 is 1..); + pub struct NonZeroU16Inner(u16 is 1..); + pub struct NonZeroU32Inner(u32 is 1..); + pub struct NonZeroU64Inner(u64 is 1..); + pub struct NonZeroU128Inner(u128 is 1..); - pub struct U64NotAllOnes(u64 as u64 in 0..=0xffff_ffff_ffff_fffe); - pub struct I64NotAllOnes(i64 as u64 in 0..=0xffff_ffff_ffff_fffe); + pub struct NonZeroI8Inner(i8 is ..0 | 1..); + pub struct NonZeroI16Inner(i16 is ..0 | 1..); + pub struct NonZeroI32Inner(i32 is ..0 | 1..); + pub struct NonZeroI64Inner(i64 is ..0 | 1..); + pub struct NonZeroI128Inner(i128 is ..0 | 1..); + + pub struct UsizeNoHighBit(usize is 0..=HALF_USIZE); + pub struct NonZeroUsizeInner(usize is 1..); + pub struct NonZeroIsizeInner(isize is ..0 | 1..); + + pub struct U32NotAllOnes(u32 is 0..u32::MAX); + pub struct I32NotAllOnes(i32 is ..-1 | 0..); + + pub struct U64NotAllOnes(u64 is 0..u64::MAX); + pub struct I64NotAllOnes(i64 is ..-1 | 0..); + + pub struct NonZeroCharInner(char is '\u{1}' ..= '\u{10ffff}'); } pub trait NotAllOnesHelper { @@ -181,7 +156,7 @@ impl NotAllOnesHelper for i64 { } define_valid_range_type! { - pub struct CodePointInner(u32 as u32 in 0..=0x10ffff); + pub struct CodePointInner(u32 is 0..=0x10ffff); } impl CodePointInner { diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 2b5279efb7f7..7876fced1c98 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1240,16 +1240,176 @@ macro_rules! nonzero_integer { // So the result cannot be zero. unsafe { Self::new_unchecked(self.get().saturating_pow(other)) } } + + /// Parses a non-zero integer from an ASCII-byte slice with decimal digits. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_from_ascii)] + /// + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_ascii(b\"+10\"), Ok(NonZero::new(10)?));")] + /// # Some(()) + /// # } + /// ``` + /// + /// Trailing space returns error: + /// + /// ``` + /// #![feature(int_from_ascii)] + /// + /// # use std::num::NonZero; + /// # + #[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_ascii(b\"1 \").is_err());")] + /// ``` + #[unstable(feature = "int_from_ascii", issue = "134821")] + #[inline] + pub const fn from_ascii(src: &[u8]) -> Result { + Self::from_ascii_radix(src, 10) + } + + /// Parses a non-zero integer from an ASCII-byte slice with digits in a given base. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// Digits are a subset of these characters, depending on `radix`: + /// + /// - `0-9` + /// - `a-z` + /// - `A-Z` + /// + /// # Panics + /// + /// This method panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_from_ascii)] + /// + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_ascii_radix(b\"A\", 16), Ok(NonZero::new(10)?));")] + /// # Some(()) + /// # } + /// ``` + /// + /// Trailing space returns error: + /// + /// ``` + /// #![feature(int_from_ascii)] + /// + /// # use std::num::NonZero; + /// # + #[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_ascii_radix(b\"1 \", 10).is_err());")] + /// ``` + #[unstable(feature = "int_from_ascii", issue = "134821")] + #[inline] + pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result { + let n = match <$Int>::from_ascii_radix(src, radix) { + Ok(n) => n, + Err(err) => return Err(err), + }; + if let Some(n) = Self::new(n) { + Ok(n) + } else { + Err(ParseIntError { kind: IntErrorKind::Zero }) + } + } + + /// Parses a non-zero integer from a string slice with digits in a given base. + /// + /// The string is expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// Digits are a subset of these characters, depending on `radix`: + /// + /// - `0-9` + /// - `a-z` + /// - `A-Z` + /// + /// # Panics + /// + /// This method panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonzero_from_str_radix)] + /// + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_str_radix(\"A\", 16), Ok(NonZero::new(10)?));")] + /// # Some(()) + /// # } + /// ``` + /// + /// Trailing space returns error: + /// + /// ``` + /// #![feature(nonzero_from_str_radix)] + /// + /// # use std::num::NonZero; + /// # + #[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_str_radix(\"1 \", 10).is_err());")] + /// ``` + #[unstable(feature = "nonzero_from_str_radix", issue = "152193")] + #[inline] + pub const fn from_str_radix(src: &str, radix: u32) -> Result { + Self::from_ascii_radix(src.as_bytes(), radix) + } } #[stable(feature = "nonzero_parse", since = "1.35.0")] impl FromStr for NonZero<$Int> { type Err = ParseIntError; fn from_str(src: &str) -> Result { - Self::new(<$Int>::from_str_radix(src, 10)?) - .ok_or(ParseIntError { - kind: IntErrorKind::Zero - }) + Self::from_str_radix(src, 10) } } diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index 3cc184f0ab75..84fc98cf73f1 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -151,7 +151,7 @@ impl ControlFlow { /// ``` #[inline] #[stable(feature = "control_flow_enum_is", since = "1.59.0")] - #[rustc_const_unstable(feature = "min_const_control_flow", issue = "148738")] + #[rustc_const_stable(feature = "min_const_control_flow", since = "CURRENT_RUSTC_VERSION")] pub const fn is_break(&self) -> bool { matches!(*self, ControlFlow::Break(_)) } @@ -168,7 +168,7 @@ impl ControlFlow { /// ``` #[inline] #[stable(feature = "control_flow_enum_is", since = "1.59.0")] - #[rustc_const_unstable(feature = "min_const_control_flow", issue = "148738")] + #[rustc_const_stable(feature = "min_const_control_flow", since = "CURRENT_RUSTC_VERSION")] pub const fn is_continue(&self) -> bool { matches!(*self, ControlFlow::Continue(_)) } @@ -264,7 +264,7 @@ impl ControlFlow { /// ``` #[inline] #[unstable(feature = "control_flow_ok", issue = "140266")] - #[rustc_const_unstable(feature = "min_const_control_flow", issue = "148738")] + #[rustc_const_unstable(feature = "control_flow_ok", issue = "140266")] pub const fn break_ok(self) -> Result { match self { ControlFlow::Continue(c) => Err(c), @@ -377,7 +377,7 @@ impl ControlFlow { /// ``` #[inline] #[unstable(feature = "control_flow_ok", issue = "140266")] - #[rustc_const_unstable(feature = "min_const_control_flow", issue = "148738")] + #[rustc_const_unstable(feature = "control_flow_ok", issue = "140266")] pub const fn continue_ok(self) -> Result { match self { ControlFlow::Continue(c) => Ok(c), diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 7c34b026e14b..b27930de4e66 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -52,8 +52,7 @@ impl Alignment { #[inline] #[must_use] pub const fn of() -> Self { - // This can't actually panic since type alignment is always a power of two. - const { Alignment::new(align_of::()).unwrap() } + ::ALIGNMENT } /// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. diff --git a/library/core/src/random.rs b/library/core/src/random.rs index 8a51fb289d8f..06f4f30efe2b 100644 --- a/library/core/src/random.rs +++ b/library/core/src/random.rs @@ -1,6 +1,6 @@ //! Random value generation. -use crate::range::RangeFull; +use crate::ops::RangeFull; /// A source of randomness. #[unstable(feature = "random", issue = "130703")] diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 4b87d426bda7..fe488355ad15 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -24,14 +24,26 @@ mod iter; #[unstable(feature = "new_range_api", issue = "125687")] pub mod legacy; -use Bound::{Excluded, Included, Unbounded}; #[doc(inline)] -pub use iter::{RangeFromIter, RangeInclusiveIter, RangeIter}; +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +pub use iter::RangeInclusiveIter; +#[doc(inline)] +#[unstable(feature = "new_range_api", issue = "125687")] +pub use iter::{RangeFromIter, RangeIter}; -#[doc(inline)] -pub use crate::iter::Step; -#[doc(inline)] -pub use crate::ops::{Bound, IntoBounds, OneSidedRange, RangeBounds, RangeFull, RangeTo}; +// FIXME(#125687): re-exports temporarily removed +// Because re-exports of stable items (Bound, RangeBounds, RangeFull, RangeTo) +// can't be made unstable. +// +// #[doc(inline)] +// #[unstable(feature = "new_range_api", issue = "125687")] +// pub use crate::iter::Step; +// #[doc(inline)] +// #[unstable(feature = "new_range_api", issue = "125687")] +// pub use crate::ops::{Bound, IntoBounds, OneSidedRange, RangeBounds, RangeFull, RangeTo}; +use crate::iter::Step; +use crate::ops::Bound::{self, Excluded, Included, Unbounded}; +use crate::ops::{IntoBounds, RangeBounds}; /// A (half-open) range bounded inclusively below and exclusively above /// (`start..end` in a future edition). @@ -226,7 +238,6 @@ impl const From> for Range { /// The `start..=last` syntax is a `RangeInclusive`: /// /// ``` -/// #![feature(new_range_api)] /// use core::range::RangeInclusive; /// /// assert_eq!(RangeInclusive::from(3..=5), RangeInclusive { start: 3, last: 5 }); @@ -234,17 +245,17 @@ impl const From> for Range { /// ``` #[lang = "RangeInclusiveCopy"] #[derive(Clone, Copy, PartialEq, Eq, Hash)] -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] pub struct RangeInclusive { /// The lower bound of the range (inclusive). - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] pub start: Idx, /// The upper bound of the range (inclusive). - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] pub last: Idx, } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] impl fmt::Debug for RangeInclusive { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { self.start.fmt(fmt)?; @@ -260,7 +271,6 @@ impl> RangeInclusive { /// # Examples /// /// ``` - /// #![feature(new_range_api)] /// use core::range::RangeInclusive; /// /// assert!(!RangeInclusive::from(3..=5).contains(&2)); @@ -278,7 +288,7 @@ impl> RangeInclusive { /// assert!(!RangeInclusive::from(f32::NAN..=1.0).contains(&1.0)); /// ``` #[inline] - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_range", issue = "none")] pub const fn contains(&self, item: &U) -> bool where @@ -293,7 +303,6 @@ impl> RangeInclusive { /// # Examples /// /// ``` - /// #![feature(new_range_api)] /// use core::range::RangeInclusive; /// /// assert!(!RangeInclusive::from(3..=5).is_empty()); @@ -304,14 +313,13 @@ impl> RangeInclusive { /// The range is empty if either side is incomparable: /// /// ``` - /// #![feature(new_range_api)] /// use core::range::RangeInclusive; /// /// assert!(!RangeInclusive::from(3.0..=5.0).is_empty()); /// assert!( RangeInclusive::from(3.0..=f32::NAN).is_empty()); /// assert!( RangeInclusive::from(f32::NAN..=5.0).is_empty()); /// ``` - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[inline] #[rustc_const_unstable(feature = "const_range", issue = "none")] pub const fn is_empty(&self) -> bool @@ -330,7 +338,6 @@ impl RangeInclusive { /// # Examples /// /// ``` - /// #![feature(new_range_api)] /// use core::range::RangeInclusive; /// /// let mut i = RangeInclusive::from(3..=8).iter().map(|n| n*n); @@ -338,7 +345,7 @@ impl RangeInclusive { /// assert_eq!(i.next(), Some(16)); /// assert_eq!(i.next(), Some(25)); /// ``` - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[inline] pub fn iter(&self) -> RangeInclusiveIter { self.clone().into_iter() @@ -354,7 +361,7 @@ impl RangeInclusive { } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const RangeBounds for RangeInclusive { fn start_bound(&self) -> Bound<&T> { @@ -371,7 +378,7 @@ impl const RangeBounds for RangeInclusive { /// If you need to use this implementation where `T` is unsized, /// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], /// i.e. replace `start..=end` with `(Bound::Included(start), Bound::Included(end))`. -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const RangeBounds for RangeInclusive<&T> { fn start_bound(&self) -> Bound<&T> { @@ -382,8 +389,8 @@ impl const RangeBounds for RangeInclusive<&T> { } } -// #[unstable(feature = "range_into_bounds", issue = "136903")] -#[unstable(feature = "new_range_api", issue = "125687")] +// #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] +#[unstable(feature = "range_into_bounds", issue = "136903")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const IntoBounds for RangeInclusive { fn into_bounds(self) -> (Bound, Bound) { @@ -391,7 +398,7 @@ impl const IntoBounds for RangeInclusive { } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From> for legacy::RangeInclusive { #[inline] @@ -399,7 +406,7 @@ impl const From> for legacy::RangeInclusive { Self::new(value.start, value.last) } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const From> for RangeInclusive { #[inline] @@ -650,12 +657,13 @@ impl> RangeToInclusive { } } +#[unstable(feature = "new_range_api", issue = "125687")] impl From> for RangeToInclusive { fn from(value: legacy::RangeToInclusive) -> Self { Self { last: value.end } } } - +#[unstable(feature = "new_range_api", issue = "125687")] impl From> for legacy::RangeToInclusive { fn from(value: RangeToInclusive) -> Self { Self { end: value.last } diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index 6fe5d9b34361..e722b9fa33c5 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -11,6 +11,7 @@ use crate::{intrinsics, mem}; pub struct RangeIter(legacy::Range); impl RangeIter { + #[unstable(feature = "new_range_api", issue = "125687")] /// Returns the remainder of the range being iterated over. pub fn remainder(self) -> Range { Range { start: self.0.start, end: self.0.end } @@ -152,7 +153,7 @@ impl IntoIterator for Range { } /// By-value [`RangeInclusive`] iterator. -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[derive(Debug, Clone)] pub struct RangeInclusiveIter(legacy::RangeInclusive); @@ -160,6 +161,7 @@ impl RangeInclusiveIter { /// Returns the remainder of the range being iterated over. /// /// If the iterator is exhausted or empty, returns `None`. + #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] pub fn remainder(self) -> Option> { if self.0.is_empty() { return None; @@ -169,7 +171,7 @@ impl RangeInclusiveIter { } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] impl Iterator for RangeInclusiveIter { type Item = A; @@ -225,7 +227,7 @@ impl Iterator for RangeInclusiveIter { } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] impl DoubleEndedIterator for RangeInclusiveIter { #[inline] fn next_back(&mut self) -> Option { @@ -246,10 +248,10 @@ impl DoubleEndedIterator for RangeInclusiveIter { #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for RangeInclusiveIter {} -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] impl FusedIterator for RangeInclusiveIter {} -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] impl IntoIterator for RangeInclusive { type Item = A; type IntoIter = RangeInclusiveIter; @@ -276,7 +278,7 @@ macro_rules! range_exact_iter_impl { macro_rules! range_incl_exact_iter_impl { ($($t:ty)*) => ($( - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] impl ExactSizeIterator for RangeInclusiveIter<$t> { } )*) } @@ -305,6 +307,7 @@ impl RangeFromIter { /// Returns the remainder of the range being iterated over. #[inline] #[rustc_inherit_overflow_checks] + #[unstable(feature = "new_range_api", issue = "125687")] pub fn remainder(self) -> RangeFrom { if intrinsics::overflow_checks() { if !self.first { diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 59802989c18f..31d9931e474a 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -127,7 +127,7 @@ mod private_slice_index { #[unstable(feature = "new_range_api", issue = "125687")] impl Sealed for range::Range {} - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] impl Sealed for range::RangeInclusive {} #[unstable(feature = "new_range_api", issue = "125687")] impl Sealed for range::RangeToInclusive {} @@ -724,7 +724,7 @@ unsafe impl const SliceIndex<[T]> for ops::RangeInclusive { } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_index", issue = "143775")] unsafe impl const SliceIndex<[T]> for range::RangeInclusive { type Output = [T]; diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index a289b0d6df40..ac096afb38af 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -2175,7 +2175,7 @@ unsafe impl Sync for ChunksExactMut<'_, T> where T: Sync {} /// /// [`array_windows`]: slice::array_windows /// [slices]: slice -#[derive(Debug, Clone, Copy)] +#[derive(Debug)] #[stable(feature = "array_windows", since = "1.94.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ArrayWindows<'a, T: 'a, const N: usize> { @@ -2189,6 +2189,14 @@ impl<'a, T: 'a, const N: usize> ArrayWindows<'a, T, N> { } } +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "array_windows", since = "1.94.0")] +impl Clone for ArrayWindows<'_, T, N> { + fn clone(&self) -> Self { + Self { v: self.v } + } +} + #[stable(feature = "array_windows", since = "1.94.0")] impl<'a, T, const N: usize> Iterator for ArrayWindows<'a, T, N> { type Item = &'a [T; N]; @@ -2224,6 +2232,14 @@ impl<'a, T, const N: usize> Iterator for ArrayWindows<'a, T, N> { fn last(self) -> Option { self.v.last_chunk() } + + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { + // SAFETY: since the caller guarantees that `idx` is in bounds, + // which means that `idx` cannot overflow an `isize`, and the + // "slice" created by `cast_array` is a subslice of `self.v` + // thus is guaranteed to be valid for the lifetime `'a` of `self.v`. + unsafe { &*self.v.as_ptr().add(idx).cast_array() } + } } #[stable(feature = "array_windows", since = "1.94.0")] @@ -2252,6 +2268,22 @@ impl ExactSizeIterator for ArrayWindows<'_, T, N> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ArrayWindows<'_, T, N> {} + +#[stable(feature = "array_windows", since = "1.94.0")] +impl FusedIterator for ArrayWindows<'_, T, N> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for ArrayWindows<'_, T, N> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNoCoerce for ArrayWindows<'_, T, N> { + const MAY_HAVE_SIDE_EFFECT: bool = false; +} + /// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a /// time), starting at the end of the slice. /// diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index a7cc943994c5..b63fe96ea99d 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -672,7 +672,7 @@ unsafe impl const SliceIndex for ops::RangeInclusive { } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_index", issue = "143775")] unsafe impl const SliceIndex for range::RangeInclusive { type Output = str; diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 22f46ec385ce..adc2bbcde51b 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1287,73 +1287,27 @@ impl AtomicBool { self.v.get().cast() } - /// Fetches the value, and applies a function to it that returns an optional - /// new value. Returns a `Result` of `Ok(previous_value)` if the function - /// returned `Some(_)`, else `Err(previous_value)`. - /// - /// Note: This may call the function multiple times if the value has been - /// changed from other threads in the meantime, as long as the function - /// returns `Some(_)`, but the function will have been applied only once to - /// the stored value. - /// - /// `fetch_update` takes two [`Ordering`] arguments to describe the memory - /// ordering of this operation. The first describes the required ordering for - /// when the operation finally succeeds while the second describes the - /// required ordering for loads. These correspond to the success and failure - /// orderings of [`AtomicBool::compare_exchange`] respectively. - /// - /// Using [`Acquire`] as success ordering makes the store part of this - /// operation [`Relaxed`], and using [`Release`] makes the final successful - /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], - /// [`Acquire`] or [`Relaxed`]. - /// - /// **Note:** This method is only available on platforms that support atomic - /// operations on `u8`. - /// - /// # Considerations - /// - /// This method is not magic; it is not provided by the hardware, and does not act like a - /// critical section or mutex. - /// - /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to - /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem]. - /// - /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem - /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap - /// - /// # Examples - /// - /// ```rust - /// use std::sync::atomic::{AtomicBool, Ordering}; - /// - /// let x = AtomicBool::new(false); - /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(false)); - /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(false)); - /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(true)); - /// assert_eq!(x.load(Ordering::SeqCst), false); - /// ``` + /// An alias for [`AtomicBool::try_update`]. #[inline] #[stable(feature = "atomic_fetch_update", since = "1.53.0")] #[cfg(target_has_atomic = "8")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] + #[deprecated( + since = "1.99.0", + note = "renamed to `try_update` for consistency", + suggestion = "try_update" + )] pub fn fetch_update( &self, set_order: Ordering, fetch_order: Ordering, - mut f: F, + f: F, ) -> Result where F: FnMut(bool) -> Option, { - let mut prev = self.load(fetch_order); - while let Some(next) = f(prev) { - match self.compare_exchange_weak(prev, next, set_order, fetch_order) { - x @ Ok(_) => return x, - Err(next_prev) => prev = next_prev, - } - } - Err(prev) + self.try_update(set_order, fetch_order, f) } /// Fetches the value, and applies a function to it that returns an optional @@ -1395,7 +1349,6 @@ impl AtomicBool { /// # Examples /// /// ```rust - /// #![feature(atomic_try_update)] /// use std::sync::atomic::{AtomicBool, Ordering}; /// /// let x = AtomicBool::new(false); @@ -1405,7 +1358,7 @@ impl AtomicBool { /// assert_eq!(x.load(Ordering::SeqCst), false); /// ``` #[inline] - #[unstable(feature = "atomic_try_update", issue = "135894")] + #[stable(feature = "atomic_try_update", since = "CURRENT_RUSTC_VERSION")] #[cfg(target_has_atomic = "8")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] @@ -1413,11 +1366,16 @@ impl AtomicBool { &self, set_order: Ordering, fetch_order: Ordering, - f: impl FnMut(bool) -> Option, + mut f: impl FnMut(bool) -> Option, ) -> Result { - // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; - // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. - self.fetch_update(set_order, fetch_order, f) + let mut prev = self.load(fetch_order); + while let Some(next) = f(prev) { + match self.compare_exchange_weak(prev, next, set_order, fetch_order) { + x @ Ok(_) => return x, + Err(next_prev) => prev = next_prev, + } + } + Err(prev) } /// Fetches the value, applies a function to it that it return a new value. @@ -1454,7 +1412,6 @@ impl AtomicBool { /// # Examples /// /// ```rust - /// #![feature(atomic_try_update)] /// /// use std::sync::atomic::{AtomicBool, Ordering}; /// @@ -1464,7 +1421,7 @@ impl AtomicBool { /// assert_eq!(x.load(Ordering::SeqCst), false); /// ``` #[inline] - #[unstable(feature = "atomic_try_update", issue = "135894")] + #[stable(feature = "atomic_try_update", since = "CURRENT_RUSTC_VERSION")] #[cfg(target_has_atomic = "8")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] @@ -2000,83 +1957,27 @@ impl AtomicPtr { unsafe { atomic_compare_exchange_weak(self.p.get(), current, new, success, failure) } } - /// Fetches the value, and applies a function to it that returns an optional - /// new value. Returns a `Result` of `Ok(previous_value)` if the function - /// returned `Some(_)`, else `Err(previous_value)`. - /// - /// Note: This may call the function multiple times if the value has been - /// changed from other threads in the meantime, as long as the function - /// returns `Some(_)`, but the function will have been applied only once to - /// the stored value. - /// - /// `fetch_update` takes two [`Ordering`] arguments to describe the memory - /// ordering of this operation. The first describes the required ordering for - /// when the operation finally succeeds while the second describes the - /// required ordering for loads. These correspond to the success and failure - /// orderings of [`AtomicPtr::compare_exchange`] respectively. - /// - /// Using [`Acquire`] as success ordering makes the store part of this - /// operation [`Relaxed`], and using [`Release`] makes the final successful - /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], - /// [`Acquire`] or [`Relaxed`]. - /// - /// **Note:** This method is only available on platforms that support atomic - /// operations on pointers. - /// - /// # Considerations - /// - /// This method is not magic; it is not provided by the hardware, and does not act like a - /// critical section or mutex. - /// - /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to - /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem], - /// which is a particularly common pitfall for pointers! - /// - /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem - /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap - /// - /// # Examples - /// - /// ```rust - /// use std::sync::atomic::{AtomicPtr, Ordering}; - /// - /// let ptr: *mut _ = &mut 5; - /// let some_ptr = AtomicPtr::new(ptr); - /// - /// let new: *mut _ = &mut 10; - /// assert_eq!(some_ptr.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(ptr)); - /// let result = some_ptr.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { - /// if x == ptr { - /// Some(new) - /// } else { - /// None - /// } - /// }); - /// assert_eq!(result, Ok(ptr)); - /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); - /// ``` + /// An alias for [`AtomicPtr::try_update`]. #[inline] #[stable(feature = "atomic_fetch_update", since = "1.53.0")] #[cfg(target_has_atomic = "ptr")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] + #[deprecated( + since = "1.99.0", + note = "renamed to `try_update` for consistency", + suggestion = "try_update" + )] pub fn fetch_update( &self, set_order: Ordering, fetch_order: Ordering, - mut f: F, + f: F, ) -> Result<*mut T, *mut T> where F: FnMut(*mut T) -> Option<*mut T>, { - let mut prev = self.load(fetch_order); - while let Some(next) = f(prev) { - match self.compare_exchange_weak(prev, next, set_order, fetch_order) { - x @ Ok(_) => return x, - Err(next_prev) => prev = next_prev, - } - } - Err(prev) + self.try_update(set_order, fetch_order, f) } /// Fetches the value, and applies a function to it that returns an optional /// new value. Returns a `Result` of `Ok(previous_value)` if the function @@ -2118,7 +2019,6 @@ impl AtomicPtr { /// # Examples /// /// ```rust - /// #![feature(atomic_try_update)] /// use std::sync::atomic::{AtomicPtr, Ordering}; /// /// let ptr: *mut _ = &mut 5; @@ -2137,7 +2037,7 @@ impl AtomicPtr { /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); /// ``` #[inline] - #[unstable(feature = "atomic_try_update", issue = "135894")] + #[stable(feature = "atomic_try_update", since = "CURRENT_RUSTC_VERSION")] #[cfg(target_has_atomic = "ptr")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] @@ -2145,11 +2045,16 @@ impl AtomicPtr { &self, set_order: Ordering, fetch_order: Ordering, - f: impl FnMut(*mut T) -> Option<*mut T>, + mut f: impl FnMut(*mut T) -> Option<*mut T>, ) -> Result<*mut T, *mut T> { - // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; - // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. - self.fetch_update(set_order, fetch_order, f) + let mut prev = self.load(fetch_order); + while let Some(next) = f(prev) { + match self.compare_exchange_weak(prev, next, set_order, fetch_order) { + x @ Ok(_) => return x, + Err(next_prev) => prev = next_prev, + } + } + Err(prev) } /// Fetches the value, applies a function to it that it return a new value. @@ -2188,7 +2093,6 @@ impl AtomicPtr { /// # Examples /// /// ```rust - /// #![feature(atomic_try_update)] /// /// use std::sync::atomic::{AtomicPtr, Ordering}; /// @@ -2201,7 +2105,7 @@ impl AtomicPtr { /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); /// ``` #[inline] - #[unstable(feature = "atomic_try_update", issue = "135894")] + #[stable(feature = "atomic_try_update", since = "CURRENT_RUSTC_VERSION")] #[cfg(target_has_atomic = "8")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] @@ -3399,69 +3303,25 @@ macro_rules! atomic_int { unsafe { atomic_xor(self.v.get(), val, order) } } - /// Fetches the value, and applies a function to it that returns an optional - /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else - /// `Err(previous_value)`. - /// - /// Note: This may call the function multiple times if the value has been changed from other threads in - /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied - /// only once to the stored value. - /// - /// `fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. - /// The first describes the required ordering for when the operation finally succeeds while the second - /// describes the required ordering for loads. These correspond to the success and failure orderings of - #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] - /// respectively. - /// - /// Using [`Acquire`] as success ordering makes the store part - /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load - /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. - /// - /// **Note**: This method is only available on platforms that support atomic operations on - #[doc = concat!("[`", $s_int_type, "`].")] - /// - /// # Considerations - /// - /// This method is not magic; it is not provided by the hardware, and does not act like a - /// critical section or mutex. - /// - /// It is implemented on top of an atomic [compare-and-swap operation], and thus is subject to - /// the usual drawbacks of CAS operations. In particular, be careful of the [ABA problem] - /// if this atomic integer is an index or more generally if knowledge of only the *bitwise value* - /// of the atomic is not in and of itself sufficient to ensure any required preconditions. - /// - /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem - /// [compare-and-swap operation]: https://en.wikipedia.org/wiki/Compare-and-swap - /// - /// # Examples - /// - /// ```rust - #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] - /// - #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] - /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7)); - /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7)); - /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8)); - /// assert_eq!(x.load(Ordering::SeqCst), 9); - /// ``` + /// An alias for + #[doc = concat!("[`", stringify!($atomic_type), "::try_update`]")] + /// . #[inline] #[stable(feature = "no_more_cas", since = "1.45.0")] #[$cfg_cas] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] + #[deprecated( + since = "1.99.0", + note = "renamed to `try_update` for consistency", + suggestion = "try_update" + )] pub fn fetch_update(&self, set_order: Ordering, fetch_order: Ordering, - mut f: F) -> Result<$int_type, $int_type> + f: F) -> Result<$int_type, $int_type> where F: FnMut($int_type) -> Option<$int_type> { - let mut prev = self.load(fetch_order); - while let Some(next) = f(prev) { - match self.compare_exchange_weak(prev, next, set_order, fetch_order) { - x @ Ok(_) => return x, - Err(next_prev) => prev = next_prev - } - } - Err(prev) + self.try_update(set_order, fetch_order, f) } /// Fetches the value, and applies a function to it that returns an optional @@ -3503,7 +3363,6 @@ macro_rules! atomic_int { /// # Examples /// /// ```rust - /// #![feature(atomic_try_update)] #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] /// #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] @@ -3513,7 +3372,7 @@ macro_rules! atomic_int { /// assert_eq!(x.load(Ordering::SeqCst), 9); /// ``` #[inline] - #[unstable(feature = "atomic_try_update", issue = "135894")] + #[stable(feature = "atomic_try_update", since = "CURRENT_RUSTC_VERSION")] #[$cfg_cas] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] @@ -3521,11 +3380,16 @@ macro_rules! atomic_int { &self, set_order: Ordering, fetch_order: Ordering, - f: impl FnMut($int_type) -> Option<$int_type>, + mut f: impl FnMut($int_type) -> Option<$int_type>, ) -> Result<$int_type, $int_type> { - // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; - // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. - self.fetch_update(set_order, fetch_order, f) + let mut prev = self.load(fetch_order); + while let Some(next) = f(prev) { + match self.compare_exchange_weak(prev, next, set_order, fetch_order) { + x @ Ok(_) => return x, + Err(next_prev) => prev = next_prev + } + } + Err(prev) } /// Fetches the value, applies a function to it that it return a new value. @@ -3566,7 +3430,6 @@ macro_rules! atomic_int { /// # Examples /// /// ```rust - /// #![feature(atomic_try_update)] #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] /// #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] @@ -3575,7 +3438,7 @@ macro_rules! atomic_int { /// assert_eq!(x.load(Ordering::SeqCst), 9); /// ``` #[inline] - #[unstable(feature = "atomic_try_update", issue = "135894")] + #[stable(feature = "atomic_try_update", since = "CURRENT_RUSTC_VERSION")] #[$cfg_cas] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_should_not_be_called_on_const_items] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index d085e4ad1a8f..592332865552 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -66,6 +66,7 @@ #![feature(generic_assert_internals)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] +#![feature(int_from_ascii)] #![feature(int_lowest_highest_one)] #![feature(int_roundings)] #![feature(ip)] @@ -90,6 +91,7 @@ #![feature(new_range_api)] #![feature(next_index)] #![feature(non_exhaustive_omitted_patterns_lint)] +#![feature(nonzero_from_str_radix)] #![feature(numfmt)] #![feature(one_sided_range)] #![feature(option_reduce)] diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 87f2d5dd8289..2483b4c2aacd 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -1,5 +1,8 @@ +#![allow(dead_code)] + use std::any::{Any, TypeId}; -use std::mem::type_info::{Type, TypeKind}; +use std::mem::offset_of; +use std::mem::type_info::{Const, Generic, GenericType, Type, TypeKind}; #[test] fn test_arrays() { @@ -66,6 +69,155 @@ fn test_tuples() { } } +#[test] +fn test_structs() { + use TypeKind::*; + + const { + struct TestStruct { + first: u8, + second: u16, + reference: &'static u16, + } + + let Type { kind: Struct(ty), size, .. } = Type::of::() else { panic!() }; + assert!(size == Some(size_of::())); + assert!(!ty.non_exhaustive); + assert!(ty.fields.len() == 3); + assert!(ty.fields[0].name == "first"); + assert!(ty.fields[0].ty == TypeId::of::()); + assert!(ty.fields[0].offset == offset_of!(TestStruct, first)); + assert!(ty.fields[1].name == "second"); + assert!(ty.fields[1].ty == TypeId::of::()); + assert!(ty.fields[1].offset == offset_of!(TestStruct, second)); + assert!(ty.fields[2].name == "reference"); + assert!(ty.fields[2].ty == TypeId::of::<&'static u16>()); + assert!(ty.fields[2].offset == offset_of!(TestStruct, reference)); + } + + const { + #[non_exhaustive] + struct NonExhaustive { + a: u8, + } + + let Type { kind: Struct(ty), .. } = Type::of::() else { panic!() }; + assert!(ty.non_exhaustive); + } + + const { + struct TupleStruct(u8, u16); + + let Type { kind: Struct(ty), .. } = Type::of::() else { panic!() }; + assert!(ty.fields.len() == 2); + assert!(ty.fields[0].name == "0"); + assert!(ty.fields[0].ty == TypeId::of::()); + assert!(ty.fields[1].name == "1"); + assert!(ty.fields[1].ty == TypeId::of::()); + } + + const { + struct Generics<'a, T, const C: u64> { + a: &'a T, + } + + let Type { kind: Struct(ty), .. } = Type::of::>() else { + panic!() + }; + assert!(ty.fields.len() == 1); + assert!(ty.generics.len() == 3); + + let Generic::Lifetime(_) = ty.generics[0] else { panic!() }; + let Generic::Type(GenericType { ty: generic_ty, .. }) = ty.generics[1] else { panic!() }; + assert!(generic_ty == TypeId::of::()); + let Generic::Const(Const { ty: const_ty, .. }) = ty.generics[2] else { panic!() }; + assert!(const_ty == TypeId::of::()); + } +} + +#[test] +fn test_unions() { + use TypeKind::*; + + const { + union TestUnion { + first: i16, + second: u16, + } + + let Type { kind: Union(ty), size, .. } = Type::of::() else { panic!() }; + assert!(size == Some(size_of::())); + assert!(ty.fields.len() == 2); + assert!(ty.fields[0].name == "first"); + assert!(ty.fields[0].offset == offset_of!(TestUnion, first)); + assert!(ty.fields[1].name == "second"); + assert!(ty.fields[1].offset == offset_of!(TestUnion, second)); + } + + const { + union Generics<'a, T: Copy, const C: u64> { + a: T, + z: &'a (), + } + + let Type { kind: Union(ty), .. } = Type::of::>() else { + panic!() + }; + assert!(ty.fields.len() == 2); + assert!(ty.fields[0].offset == offset_of!(Generics<'static, i32, 1_u64>, a)); + assert!(ty.fields[1].offset == offset_of!(Generics<'static, i32, 1_u64>, z)); + + assert!(ty.generics.len() == 3); + let Generic::Lifetime(_) = ty.generics[0] else { panic!() }; + let Generic::Type(GenericType { ty: generic_ty, .. }) = ty.generics[1] else { panic!() }; + assert!(generic_ty == TypeId::of::()); + let Generic::Const(Const { ty: const_ty, .. }) = ty.generics[2] else { panic!() }; + assert!(const_ty == TypeId::of::()); + } +} + +#[test] +fn test_enums() { + use TypeKind::*; + + const { + enum E { + Some(u32), + None, + #[non_exhaustive] + Foomp { + a: (), + b: &'static str, + }, + } + + let Type { kind: Enum(ty), size, .. } = Type::of::() else { panic!() }; + assert!(size == Some(size_of::())); + assert!(ty.variants.len() == 3); + + assert!(ty.variants[0].name == "Some"); + assert!(!ty.variants[0].non_exhaustive); + assert!(ty.variants[0].fields.len() == 1); + + assert!(ty.variants[1].name == "None"); + assert!(!ty.variants[1].non_exhaustive); + assert!(ty.variants[1].fields.len() == 0); + + assert!(ty.variants[2].name == "Foomp"); + assert!(ty.variants[2].non_exhaustive); + assert!(ty.variants[2].fields.len() == 2); + } + + const { + let Type { kind: Enum(ty), size, .. } = Type::of::>() else { panic!() }; + assert!(size == Some(size_of::>())); + assert!(ty.variants.len() == 2); + assert!(ty.generics.len() == 1); + let Generic::Type(GenericType { ty: generic_ty, .. }) = ty.generics[0] else { panic!() }; + assert!(generic_ty == TypeId::of::()); + } +} + #[test] fn test_primitives() { use TypeKind::*; diff --git a/library/coretests/tests/nonzero.rs b/library/coretests/tests/nonzero.rs index c368a2621740..861e9e05081f 100644 --- a/library/coretests/tests/nonzero.rs +++ b/library/coretests/tests/nonzero.rs @@ -124,6 +124,97 @@ fn test_from_signed_nonzero() { assert_eq!(num, 1i32); } +#[test] +fn test_from_ascii_radix() { + assert_eq!(NonZero::::from_ascii_radix(b"123", 10), Ok(NonZero::new(123).unwrap())); + assert_eq!(NonZero::::from_ascii_radix(b"1001", 2), Ok(NonZero::new(9).unwrap())); + assert_eq!(NonZero::::from_ascii_radix(b"123", 8), Ok(NonZero::new(83).unwrap())); + assert_eq!(NonZero::::from_ascii_radix(b"123", 16), Ok(NonZero::new(291).unwrap())); + assert_eq!(NonZero::::from_ascii_radix(b"ffff", 16), Ok(NonZero::new(65535).unwrap())); + assert_eq!(NonZero::::from_ascii_radix(b"z", 36), Ok(NonZero::new(35).unwrap())); + assert_eq!( + NonZero::::from_ascii_radix(b"0", 10).err().map(|e| e.kind().clone()), + Some(IntErrorKind::Zero) + ); + assert_eq!( + NonZero::::from_ascii_radix(b"-1", 10).err().map(|e| e.kind().clone()), + Some(IntErrorKind::InvalidDigit) + ); + assert_eq!( + NonZero::::from_ascii_radix(b"-129", 10).err().map(|e| e.kind().clone()), + Some(IntErrorKind::NegOverflow) + ); + assert_eq!( + NonZero::::from_ascii_radix(b"257", 10).err().map(|e| e.kind().clone()), + Some(IntErrorKind::PosOverflow) + ); + + assert_eq!( + NonZero::::from_ascii_radix(b"Z", 10).err().map(|e| e.kind().clone()), + Some(IntErrorKind::InvalidDigit) + ); + assert_eq!( + NonZero::::from_ascii_radix(b"_", 2).err().map(|e| e.kind().clone()), + Some(IntErrorKind::InvalidDigit) + ); +} + +#[test] +fn test_from_ascii() { + assert_eq!(NonZero::::from_ascii(b"123"), Ok(NonZero::new(123).unwrap())); + assert_eq!( + NonZero::::from_ascii(b"0").err().map(|e| e.kind().clone()), + Some(IntErrorKind::Zero) + ); + assert_eq!( + NonZero::::from_ascii(b"-1").err().map(|e| e.kind().clone()), + Some(IntErrorKind::InvalidDigit) + ); + assert_eq!( + NonZero::::from_ascii(b"-129").err().map(|e| e.kind().clone()), + Some(IntErrorKind::NegOverflow) + ); + assert_eq!( + NonZero::::from_ascii(b"257").err().map(|e| e.kind().clone()), + Some(IntErrorKind::PosOverflow) + ); +} + +#[test] +fn test_from_str_radix() { + assert_eq!(NonZero::::from_str_radix("123", 10), Ok(NonZero::new(123).unwrap())); + assert_eq!(NonZero::::from_str_radix("1001", 2), Ok(NonZero::new(9).unwrap())); + assert_eq!(NonZero::::from_str_radix("123", 8), Ok(NonZero::new(83).unwrap())); + assert_eq!(NonZero::::from_str_radix("123", 16), Ok(NonZero::new(291).unwrap())); + assert_eq!(NonZero::::from_str_radix("ffff", 16), Ok(NonZero::new(65535).unwrap())); + assert_eq!(NonZero::::from_str_radix("z", 36), Ok(NonZero::new(35).unwrap())); + assert_eq!( + NonZero::::from_str_radix("0", 10).err().map(|e| e.kind().clone()), + Some(IntErrorKind::Zero) + ); + assert_eq!( + NonZero::::from_str_radix("-1", 10).err().map(|e| e.kind().clone()), + Some(IntErrorKind::InvalidDigit) + ); + assert_eq!( + NonZero::::from_str_radix("-129", 10).err().map(|e| e.kind().clone()), + Some(IntErrorKind::NegOverflow) + ); + assert_eq!( + NonZero::::from_str_radix("257", 10).err().map(|e| e.kind().clone()), + Some(IntErrorKind::PosOverflow) + ); + + assert_eq!( + NonZero::::from_str_radix("Z", 10).err().map(|e| e.kind().clone()), + Some(IntErrorKind::InvalidDigit) + ); + assert_eq!( + NonZero::::from_str_radix("_", 2).err().map(|e| e.kind().clone()), + Some(IntErrorKind::InvalidDigit) + ); +} + #[test] fn test_from_str() { assert_eq!("123".parse::>(), Ok(NonZero::new(123).unwrap())); diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 8f4a79b389f6..02a408802b6f 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -121,7 +121,7 @@ macro_rules! define_client_side { } } } -with_api!(self, define_client_side); +with_api!(define_client_side, TokenStream, Span, Symbol); struct Bridge<'a> { /// Reusable buffer (only `clear`-ed, never shrunk), primarily @@ -129,7 +129,7 @@ struct Bridge<'a> { cached_buffer: Buffer, /// Server-side function that the client uses to make requests. - dispatch: closure::Closure<'a, Buffer, Buffer>, + dispatch: closure::Closure<'a>, /// Provided globals for this macro expansion. globals: ExpnGlobals, diff --git a/library/proc_macro/src/bridge/closure.rs b/library/proc_macro/src/bridge/closure.rs index e5133907854b..88c4dd6630b1 100644 --- a/library/proc_macro/src/bridge/closure.rs +++ b/library/proc_macro/src/bridge/closure.rs @@ -1,10 +1,12 @@ -//! Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`. +//! Closure type (equivalent to `&mut dyn FnMut(Buffer) -> Buffer`) that's `repr(C)`. use std::marker::PhantomData; +use super::Buffer; + #[repr(C)] -pub(super) struct Closure<'a, A, R> { - call: unsafe extern "C" fn(*mut Env, A) -> R, +pub(super) struct Closure<'a> { + call: extern "C" fn(*mut Env, Buffer) -> Buffer, env: *mut Env, // Prevent Send and Sync impls. // @@ -14,17 +16,17 @@ pub(super) struct Closure<'a, A, R> { struct Env; -impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> { +impl<'a, F: FnMut(Buffer) -> Buffer> From<&'a mut F> for Closure<'a> { fn from(f: &'a mut F) -> Self { - unsafe extern "C" fn call R>(env: *mut Env, arg: A) -> R { + extern "C" fn call Buffer>(env: *mut Env, arg: Buffer) -> Buffer { unsafe { (*(env as *mut _ as *mut F))(arg) } } - Closure { call: call::, env: f as *mut _ as *mut Env, _marker: PhantomData } + Closure { call: call::, env: f as *mut _ as *mut Env, _marker: PhantomData } } } -impl<'a, A, R> Closure<'a, A, R> { - pub(super) fn call(&mut self, arg: A) -> R { - unsafe { (self.call)(self.env, arg) } +impl<'a> Closure<'a> { + pub(super) fn call(&mut self, arg: Buffer) -> Buffer { + (self.call)(self.env, arg) } } diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 244ab7d81b02..6a9027046af0 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -18,71 +18,67 @@ use crate::{Delimiter, Level}; /// Higher-order macro describing the server RPC API, allowing automatic /// generation of type-safe Rust APIs, both client-side and server-side. /// -/// `with_api!(MySelf, my_macro)` expands to: +/// `with_api!(my_macro, MyTokenStream, MySpan, MySymbol)` expands to: /// ```rust,ignore (pseudo-code) /// my_macro! { -/// fn lit_character(ch: char) -> MySelf::Literal; -/// fn lit_span(lit: &MySelf::Literal) -> MySelf::Span; -/// fn lit_set_span(lit: &mut MySelf::Literal, span: MySelf::Span); +/// fn ts_clone(stream: &MyTokenStream) -> MyTokenStream; +/// fn span_debug(span: &MySpan) -> String; /// // ... /// } /// ``` /// -/// The first argument serves to customize the argument/return types, -/// to enable several different usecases: -/// -/// If `MySelf` is just `Self`, then the types are only valid inside -/// a trait or a trait impl, where the trait has associated types -/// for each of the API types. If non-associated types are desired, -/// a module name (`self` in practice) can be used instead of `Self`. +/// The second (`TokenStream`), third (`Span`) and fourth (`Symbol`) +/// argument serve to customize the argument/return types that need +/// special handling, to enable several different representations of +/// these types. macro_rules! with_api { - ($S:ident, $m:ident) => { + ($m:ident, $TokenStream: path, $Span: path, $Symbol: path) => { $m! { fn injected_env_var(var: &str) -> Option; fn track_env_var(var: &str, value: Option<&str>); fn track_path(path: &str); - fn literal_from_str(s: &str) -> Result, ()>; - fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>); + fn literal_from_str(s: &str) -> Result, ()>; + fn emit_diagnostic(diagnostic: Diagnostic<$Span>); - fn ts_drop(stream: $S::TokenStream); - fn ts_clone(stream: &$S::TokenStream) -> $S::TokenStream; - fn ts_is_empty(stream: &$S::TokenStream) -> bool; - fn ts_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>; - fn ts_from_str(src: &str) -> $S::TokenStream; - fn ts_to_string(stream: &$S::TokenStream) -> String; + fn ts_drop(stream: $TokenStream); + fn ts_clone(stream: &$TokenStream) -> $TokenStream; + fn ts_is_empty(stream: &$TokenStream) -> bool; + fn ts_expand_expr(stream: &$TokenStream) -> Result<$TokenStream, ()>; + fn ts_from_str(src: &str) -> $TokenStream; + fn ts_to_string(stream: &$TokenStream) -> String; fn ts_from_token_tree( - tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>, - ) -> $S::TokenStream; + tree: TokenTree<$TokenStream, $Span, $Symbol>, + ) -> $TokenStream; fn ts_concat_trees( - base: Option<$S::TokenStream>, - trees: Vec>, - ) -> $S::TokenStream; + base: Option<$TokenStream>, + trees: Vec>, + ) -> $TokenStream; fn ts_concat_streams( - base: Option<$S::TokenStream>, - streams: Vec<$S::TokenStream>, - ) -> $S::TokenStream; + base: Option<$TokenStream>, + streams: Vec<$TokenStream>, + ) -> $TokenStream; fn ts_into_trees( - stream: $S::TokenStream - ) -> Vec>; + stream: $TokenStream + ) -> Vec>; - fn span_debug(span: $S::Span) -> String; - fn span_parent(span: $S::Span) -> Option<$S::Span>; - fn span_source(span: $S::Span) -> $S::Span; - fn span_byte_range(span: $S::Span) -> Range; - fn span_start(span: $S::Span) -> $S::Span; - fn span_end(span: $S::Span) -> $S::Span; - fn span_line(span: $S::Span) -> usize; - fn span_column(span: $S::Span) -> usize; - fn span_file(span: $S::Span) -> String; - fn span_local_file(span: $S::Span) -> Option; - fn span_join(span: $S::Span, other: $S::Span) -> Option<$S::Span>; - fn span_subspan(span: $S::Span, start: Bound, end: Bound) -> Option<$S::Span>; - fn span_resolved_at(span: $S::Span, at: $S::Span) -> $S::Span; - fn span_source_text(span: $S::Span) -> Option; - fn span_save_span(span: $S::Span) -> usize; - fn span_recover_proc_macro_span(id: usize) -> $S::Span; + fn span_debug(span: $Span) -> String; + fn span_parent(span: $Span) -> Option<$Span>; + fn span_source(span: $Span) -> $Span; + fn span_byte_range(span: $Span) -> Range; + fn span_start(span: $Span) -> $Span; + fn span_end(span: $Span) -> $Span; + fn span_line(span: $Span) -> usize; + fn span_column(span: $Span) -> usize; + fn span_file(span: $Span) -> String; + fn span_local_file(span: $Span) -> Option; + fn span_join(span: $Span, other: $Span) -> Option<$Span>; + fn span_subspan(span: $Span, start: Bound, end: Bound) -> Option<$Span>; + fn span_resolved_at(span: $Span, at: $Span) -> $Span; + fn span_source_text(span: $Span) -> Option; + fn span_save_span(span: $Span) -> usize; + fn span_recover_proc_macro_span(id: usize) -> $Span; - fn symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>; + fn symbol_normalize_and_validate_ident(string: &str) -> Result<$Symbol, ()>; } }; } @@ -126,7 +122,7 @@ pub struct BridgeConfig<'a> { input: Buffer, /// Server-side function that the client uses to make requests. - dispatch: closure::Closure<'a, Buffer, Buffer>, + dispatch: closure::Closure<'a>, /// If 'true', always invoke the default panic hook force_show_panics: bool, @@ -146,7 +142,7 @@ macro_rules! declare_tags { rpc_encode_decode!(enum ApiTags { $($method),* }); } } -with_api!(self, declare_tags); +with_api!(declare_tags, __, __, __); /// Helper to wrap associated types to allow trait impl dispatch. /// That is, normally a pair of impls for `T::Foo` and `T::Bar` @@ -173,7 +169,7 @@ impl Mark for Marked { self.value } } -impl<'a, T, M> Mark for &'a Marked { +impl<'a, T> Mark for &'a Marked { type Unmarked = &'a T; fn mark(_: Self::Unmarked) -> Self { unreachable!() @@ -220,6 +216,8 @@ mark_noop! { Delimiter, LitKind, Level, + Bound, + Range, } rpc_encode_decode!( @@ -318,7 +316,7 @@ macro_rules! compound_traits { }; } -compound_traits!( +rpc_encode_decode!( enum Bound { Included(x), Excluded(x), @@ -390,7 +388,7 @@ pub struct Literal { pub span: Span, } -compound_traits!(struct Literal { kind, symbol, suffix, span }); +compound_traits!(struct Literal { kind, symbol, suffix, span }); #[derive(Clone)] pub enum TokenTree { @@ -434,6 +432,6 @@ compound_traits!( struct ExpnGlobals { def_site, call_site, mixed_site } ); -compound_traits!( +rpc_encode_decode!( struct Range { start, end } ); diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 63329c8c0260..7fee8654bc78 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -52,45 +52,37 @@ macro_rules! rpc_encode_decode { } }; (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => { - impl),+)?> Encode for $name $(<$($T),+>)? { - fn encode(self, w: &mut Buffer, s: &mut S) { - // HACK(eddyb): `Tag` enum duplicated between the - // two impls as there's no other place to stash it. - #[allow(non_camel_case_types)] - #[repr(u8)] - enum Tag { $($variant),* } + #[allow(non_upper_case_globals, non_camel_case_types)] + const _: () = { + #[repr(u8)] enum Tag { $($variant),* } - match self { - $($name::$variant $(($field))* => { - (Tag::$variant as u8).encode(w, s); - $($field.encode(w, s);)* - })* + $(const $variant: u8 = Tag::$variant as u8;)* + + impl),+)?> Encode for $name $(<$($T),+>)? { + fn encode(self, w: &mut Buffer, s: &mut S) { + match self { + $($name::$variant $(($field))* => { + $variant.encode(w, s); + $($field.encode(w, s);)* + })* + } } } - } - impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S> - for $name $(<$($T),+>)? - { - fn decode(r: &mut &'a [u8], s: &mut S) -> Self { - // HACK(eddyb): `Tag` enum duplicated between the - // two impls as there's no other place to stash it. - #[allow(non_upper_case_globals, non_camel_case_types)] - mod tag { - #[repr(u8)] enum Tag { $($variant),* } - - $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* - } - - match u8::decode(r, s) { - $(tag::$variant => { - $(let $field = Decode::decode(r, s);)* - $name::$variant $(($field))* - })* - _ => unreachable!(), + impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S> + for $name $(<$($T),+>)? + { + fn decode(r: &mut &'a [u8], s: &mut S) -> Self { + match u8::decode(r, s) { + $($variant => { + $(let $field = Decode::decode(r, s);)* + $name::$variant $(($field))* + })* + _ => unreachable!(), + } } } - } + }; } } diff --git a/library/proc_macro/src/bridge/selfless_reify.rs b/library/proc_macro/src/bridge/selfless_reify.rs index a53550e0b9e0..1a9951af8c9f 100644 --- a/library/proc_macro/src/bridge/selfless_reify.rs +++ b/library/proc_macro/src/bridge/selfless_reify.rs @@ -38,47 +38,27 @@ use std::mem; -// FIXME(eddyb) this could be `trait` impls except for the `const fn` requirement. -macro_rules! define_reify_functions { - ($( - fn $name:ident $(<$($param:ident),*>)? - for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty; - )+) => { - $(pub(super) const fn $name< - $($($param,)*)? - F: Fn($($arg_ty),*) -> $ret_ty + Copy - >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { - // FIXME(eddyb) describe the `F` type (e.g. via `type_name::`) once panic - // formatting becomes possible in `const fn`. - const { assert!(size_of::() == 0, "selfless_reify: closure must be zero-sized"); } - - $(extern $abi)? fn wrapper< - $($($param,)*)? - F: Fn($($arg_ty),*) -> $ret_ty + Copy - >($($arg: $arg_ty),*) -> $ret_ty { - let f = unsafe { - // SAFETY: `F` satisfies all criteria for "out of thin air" - // reconstructability (see module-level doc comment). - mem::MaybeUninit::::uninit().assume_init() - }; - f($($arg),*) - } - let _f_proof = f; - wrapper::< - $($($param,)*)? - F - > - })+ +pub(super) const fn reify_to_extern_c_fn_hrt_bridge< + R, + F: Fn(super::BridgeConfig<'_>) -> R + Copy, +>( + f: F, +) -> extern "C" fn(super::BridgeConfig<'_>) -> R { + // FIXME(eddyb) describe the `F` type (e.g. via `type_name::`) once panic + // formatting becomes possible in `const fn`. + const { + assert!(size_of::() == 0, "selfless_reify: closure must be zero-sized"); } -} - -define_reify_functions! { - fn _reify_to_extern_c_fn_unary for extern "C" fn(arg: A) -> R; - - // HACK(eddyb) this abstraction is used with `for<'a> fn(BridgeConfig<'a>) - // -> T` but that doesn't work with just `reify_to_extern_c_fn_unary` - // because of the `fn` pointer type being "higher-ranked" (i.e. the - // `for<'a>` binder). - // FIXME(eddyb) try to remove the lifetime from `BridgeConfig`, that'd help. - fn reify_to_extern_c_fn_hrt_bridge for extern "C" fn(bridge: super::BridgeConfig<'_>) -> R; + extern "C" fn wrapper) -> R + Copy>( + bridge: super::BridgeConfig<'_>, + ) -> R { + let f = unsafe { + // SAFETY: `F` satisfies all criteria for "out of thin air" + // reconstructability (see module-level doc comment). + mem::conjure_zst::() + }; + f(bridge) + } + let _f_proof = f; + wrapper:: } diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 073ddb554994..a3c6a232264e 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -58,12 +58,12 @@ struct Dispatcher { server: S, } -macro_rules! define_server_dispatcher_impl { +macro_rules! define_server { ( $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* ) => { pub trait Server { - type TokenStream: 'static + Clone; + type TokenStream: 'static + Clone + Default; type Span: 'static + Copy + Eq + Hash; type Symbol: 'static; @@ -77,22 +77,20 @@ macro_rules! define_server_dispatcher_impl { $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?;)* } + } +} +with_api!(define_server, Self::TokenStream, Self::Span, Self::Symbol); +macro_rules! define_dispatcher { + ( + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + ) => { // FIXME(eddyb) `pub` only for `ExecutionStrategy` below. pub trait DispatcherTrait { - // HACK(eddyb) these are here to allow `Self::$name` to work below. - type TokenStream; - type Span; - type Symbol; - fn dispatch(&mut self, buf: Buffer) -> Buffer; } impl DispatcherTrait for Dispatcher { - type TokenStream = MarkedTokenStream; - type Span = MarkedSpan; - type Symbol = MarkedSymbol; - fn dispatch(&mut self, mut buf: Buffer) -> Buffer { let Dispatcher { handle_store, server } = self; @@ -127,7 +125,7 @@ macro_rules! define_server_dispatcher_impl { } } } -with_api!(Self, define_server_dispatcher_impl); +with_api!(define_dispatcher, MarkedTokenStream, MarkedSpan, MarkedSymbol); pub trait ExecutionStrategy { fn run_bridge_and_client( @@ -312,7 +310,6 @@ impl client::Client { ) -> Result where S: Server, - S::TokenStream: Default, { let client::Client { handle_counters, run, _marker } = *self; run_server( @@ -338,7 +335,6 @@ impl client::Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream ) -> Result where S: Server, - S::TokenStream: Default, { let client::Client { handle_counters, run, _marker } = *self; run_server( diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 49b6f2ae41f8..e2f39c015bdd 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -27,6 +27,7 @@ #![feature(restricted_std)] #![feature(rustc_attrs)] #![feature(extend_one)] +#![feature(mem_conjure_zst)] #![recursion_limit = "256"] #![allow(internal_features)] #![deny(ffi_unwind_calls)] diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index a24baad61501..cf6f9594c002 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -277,6 +277,7 @@ pub struct OpenOptions(fs_imp::OpenOptions); /// Representation of the various timestamps on a file. #[derive(Copy, Clone, Debug, Default)] #[stable(feature = "file_set_times", since = "1.75.0")] +#[must_use = "must be applied to a file via `File::set_times` to have any effect"] pub struct FileTimes(fs_imp::FileTimes); /// Representation of the various permissions on a file. diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index dcde208fac77..39c2dd4c0cb7 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -394,7 +394,6 @@ // // Only for re-exporting: // tidy-alphabetical-start -#![feature(assert_matches)] #![feature(async_iterator)] #![feature(c_variadic)] #![feature(cfg_accessible)] @@ -726,7 +725,7 @@ pub use core::{ assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, r#try, unimplemented, unreachable, write, writeln, }; -#[unstable(feature = "assert_matches", issue = "82775")] +#[stable(feature = "assert_matches", since = "CURRENT_RUSTC_VERSION")] pub use core::{assert_matches, debug_assert_matches}; // Re-export unstable derive macro defined through core. diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs index 95cf4932e6e2..473d7ae3e2ae 100644 --- a/library/std/src/os/fd/mod.rs +++ b/library/std/src/os/fd/mod.rs @@ -16,6 +16,9 @@ mod owned; #[cfg(not(target_os = "trusty"))] mod net; +// Implementation of stdio file descriptor constants. +mod stdio; + #[cfg(test)] mod tests; @@ -24,3 +27,5 @@ mod tests; pub use owned::*; #[stable(feature = "os_fd", since = "1.66.0")] pub use raw::*; +#[unstable(feature = "stdio_fd_consts", issue = "150836")] +pub use stdio::*; diff --git a/library/std/src/os/fd/stdio.rs b/library/std/src/os/fd/stdio.rs new file mode 100644 index 000000000000..c50cbd39849b --- /dev/null +++ b/library/std/src/os/fd/stdio.rs @@ -0,0 +1,53 @@ +use super::BorrowedFd; + +/// The file descriptor for the standard input stream of the current process. +/// +/// See [`io::stdin()`][`crate::io::stdin`] for the higher level handle, which should be preferred +/// whenever possible. See [`STDERR`] for why the file descriptor might be required and caveats. +#[unstable(feature = "stdio_fd_consts", issue = "150836")] +pub const STDIN: BorrowedFd<'static> = unsafe { BorrowedFd::borrow_raw(0) }; + +/// The file descriptor for the standard output stream of the current process. +/// +/// See [`io::stdout()`][`crate::io::stdout`] for the higher level handle, which should be preferred +/// whenever possible. See [`STDERR`] for why the file descriptor might be required and caveats. In +/// addition to the issues discussed there, note that [`Stdout`][`crate::io::Stdout`] is buffered by +/// default, and writing to the file descriptor will bypass this buffer. +#[unstable(feature = "stdio_fd_consts", issue = "150836")] +pub const STDOUT: BorrowedFd<'static> = unsafe { BorrowedFd::borrow_raw(1) }; + +/// The file descriptor for the standard error stream of the current process. +/// +/// See [`io::stderr()`][`crate::io::stderr`] for the higher level handle, which should be preferred +/// whenever possible. However, there are situations where touching the `std::io` handles (or most +/// other parts of the standard library) risks deadlocks or other subtle bugs. For example: +/// +/// - Global allocators must be careful to [avoid reentrancy][global-alloc-reentrancy], and the +/// `std::io` handles may allocate memory on (some) accesses. +/// - Signal handlers must be *async-signal-safe*, which rules out panicking, taking locks (may +/// deadlock if the signal handler interrupted a thread holding that lock), allocating memory, or +/// anything else that is not explicitly declared async-signal-safe. +/// - `CommandExt::pre_exec` callbacks can safely panic (with some limitations), but otherwise must +/// abide by similar limitations as signal handlers. In particular, at the time these callbacks +/// run, the stdio file descriptors have already been replaced, but the locks protecting the +/// `std::io` handles may be permanently locked if another thread held the lock at `fork()` time. +/// +/// In these and similar cases, direct access to the file descriptor may be required. However, in +/// most cases, using the `std::io` handles and accessing the file descriptor via the `AsFd` +/// implementations is preferable, as it enables cooperation with the standard library's locking and +/// buffering. +/// +/// # I/O safety +/// +/// This is a `BorrowedFd<'static>` because the standard input/output/error streams are shared +/// resources that must remain available for the lifetime of the process. This is only true when +/// linking `std`, and may not always hold for [code running before `main()`][before-after-main] or +/// in `no_std` environments. It is [unsound][io-safety] to close these file descriptors. Safe +/// patterns for changing these file descriptors are available on Unix via the `StdioExt` extension +/// trait. +/// +/// [before-after-main]: ../../../std/index.html#use-before-and-after-main +/// [io-safety]: ../../../std/io/index.html#io-safety +/// [global-alloc-reentrancy]: ../../../std/alloc/trait.GlobalAlloc.html#re-entrance +#[unstable(feature = "stdio_fd_consts", issue = "150836")] +pub const STDERR: BorrowedFd<'static> = unsafe { BorrowedFd::borrow_raw(2) }; diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 25bd7005b994..14b41a427f1e 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -19,6 +19,20 @@ //! matter the platform or filesystem. An exception to this is made for Windows //! drive letters. //! +//! ## Path normalization +//! +//! Several methods in this module perform basic path normalization by disregarding +//! repeated separators, non-leading `.` components, and trailing separators. These include: +//! - Methods for iteration, such as [`Path::components`] and [`Path::iter`] +//! - Methods for inspection, such as [`Path::has_root`] +//! - Comparisons using [`PartialEq`], [`PartialOrd`], and [`Ord`] +//! +//! [`Path::join`] and [`PathBuf::push`] also disregard trailing slashes. +//! +// FIXME(normalize_lexically): mention normalize_lexically once stable +//! These methods **do not** resolve `..` components or symlinks. For full normalization +//! including `..` resolution, use [`Path::canonicalize`] (which does access the filesystem). +//! //! ## Simple usage //! //! Path manipulation includes both parsing components from slices and building diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index acfeb96900cc..c01ee17eecda 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -56,24 +56,36 @@ use crate::sys::sync as sys; /// # Examples /// /// ``` -/// use std::sync::RwLock; +/// use std::sync::{Arc, RwLock}; +/// use std::thread; +/// use std::time::Duration; /// -/// let lock = RwLock::new(5); +/// let data = Arc::new(RwLock::new(5)); /// -/// // many reader locks can be held at once -/// { -/// let r1 = lock.read().unwrap(); -/// let r2 = lock.read().unwrap(); -/// assert_eq!(*r1, 5); -/// assert_eq!(*r2, 5); -/// } // read locks are dropped at this point +/// // Multiple readers can access in parallel. +/// for i in 0..3 { +/// let lock_clone = Arc::clone(&data); /// -/// // only one write lock may be held, however -/// { -/// let mut w = lock.write().unwrap(); -/// *w += 1; -/// assert_eq!(*w, 6); -/// } // write lock is dropped here +/// thread::spawn(move || { +/// let value = lock_clone.read().unwrap(); +/// +/// println!("Reader {}: Read value {}, now holding lock...", i, *value); +/// +/// // Simulating a long read operation +/// thread::sleep(Duration::from_secs(1)); +/// +/// println!("Reader {}: Dropping lock.", i); +/// // Read lock unlocked when going out of scope. +/// }); +/// } +/// +/// thread::sleep(Duration::from_millis(100)); // Wait for readers to start +/// +/// // While all readers can proceed, a call to .write() has to wait for +// // current active reader locks. +/// let mut writable_data = data.write().unwrap(); +/// println!("Writer proceeds..."); +/// *writable_data += 1; /// ``` /// /// [`Mutex`]: super::Mutex @@ -370,7 +382,8 @@ impl RwLock { /// /// # Panics /// - /// This function might panic when called if the lock is already held by the current thread. + /// This function might panic when called if the lock is already held by the current thread + /// in read or write mode. /// /// # Examples /// @@ -467,7 +480,8 @@ impl RwLock { /// /// # Panics /// - /// This function might panic when called if the lock is already held by the current thread. + /// This function might panic when called if the lock is already held by the current thread + /// in read or write mode. /// /// # Examples /// diff --git a/library/std/src/sys/env/uefi.rs b/library/std/src/sys/env/uefi.rs index af16a02642a4..bc2aed423179 100644 --- a/library/std/src/sys/env/uefi.rs +++ b/library/std/src/sys/env/uefi.rs @@ -43,7 +43,20 @@ mod uefi_env { pub(crate) fn unset(key: &OsStr) -> io::Result<()> { let mut key_ptr = helpers::os_string_to_raw(key) .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; - unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) } + let r = unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) }; + + // The UEFI Shell spec only lists `EFI_SUCCESS` as a possible return value for + // `SetEnv`, but the edk2 implementation can return errors. Allow most of these + // errors to bubble up to the caller, but ignore `NotFound` errors; deleting a + // nonexistent variable is not listed as an error condition of + // `std::env::remove_var`. + if let Err(err) = &r + && err.kind() == io::ErrorKind::NotFound + { + Ok(()) + } else { + r + } } pub(crate) fn get_all() -> io::Result> { diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 3ca84db0f47f..7db474544f04 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -2132,9 +2132,6 @@ pub fn link(original: &CStr, link: &CStr) -> io::Result<()> { // Android has `linkat` on newer versions, but we happen to know // `link` always has the correct behavior, so it's here as well. target_os = "android", - // wasi-sdk-29-and-prior have a buggy `linkat` so use `link` instead - // until wasi-sdk is updated (see WebAssembly/wasi-libc#690) - target_os = "wasi", // Other misc platforms target_os = "horizon", target_os = "vita", diff --git a/library/std/src/sys/sync/condvar/xous.rs b/library/std/src/sys/sync/condvar/xous.rs index 21a1587214a1..5d1b14443c62 100644 --- a/library/std/src/sys/sync/condvar/xous.rs +++ b/library/std/src/sys/sync/condvar/xous.rs @@ -38,7 +38,7 @@ impl Condvar { // possible for `counter` to decrease due to a condvar timing out, in which // case the corresponding `timed_out` will increase accordingly. let Ok(waiter_count) = - self.counter.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |counter| { + self.counter.try_update(Ordering::Relaxed, Ordering::Relaxed, |counter| { if counter == 0 { return None; } else { diff --git a/library/std/src/sys/sync/rwlock/futex.rs b/library/std/src/sys/sync/rwlock/futex.rs index 961819cae8d6..0e8e954de075 100644 --- a/library/std/src/sys/sync/rwlock/futex.rs +++ b/library/std/src/sys/sync/rwlock/futex.rs @@ -86,7 +86,7 @@ impl RwLock { #[inline] pub fn try_read(&self) -> bool { self.state - .fetch_update(Acquire, Relaxed, |s| is_read_lockable(s).then(|| s + READ_LOCKED)) + .try_update(Acquire, Relaxed, |s| is_read_lockable(s).then(|| s + READ_LOCKED)) .is_ok() } @@ -164,7 +164,7 @@ impl RwLock { #[inline] pub fn try_write(&self) -> bool { self.state - .fetch_update(Acquire, Relaxed, |s| is_unlocked(s).then(|| s + WRITE_LOCKED)) + .try_update(Acquire, Relaxed, |s| is_unlocked(s).then(|| s + WRITE_LOCKED)) .is_ok() } diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs index 62f084acfd25..b41a65f7303b 100644 --- a/library/std/src/sys/sync/rwlock/queue.rs +++ b/library/std/src/sys/sync/rwlock/queue.rs @@ -329,7 +329,7 @@ impl RwLock { #[inline] pub fn try_read(&self) -> bool { - self.state.fetch_update(Acquire, Relaxed, read_lock).is_ok() + self.state.try_update(Acquire, Relaxed, read_lock).is_ok() } #[inline] @@ -343,7 +343,7 @@ impl RwLock { pub fn try_write(&self) -> bool { // Atomically set the `LOCKED` bit. This is lowered to a single atomic instruction on most // modern processors (e.g. "lock bts" on x86 and "ldseta" on modern AArch64), and therefore - // is more efficient than `fetch_update(lock(true))`, which can spuriously fail if a new + // is more efficient than `try_update(lock(true))`, which can spuriously fail if a new // node is appended to the queue. self.state.fetch_or(LOCKED, Acquire).addr() & LOCKED == 0 } @@ -453,7 +453,7 @@ impl RwLock { #[inline] pub unsafe fn read_unlock(&self) { - match self.state.fetch_update(Release, Acquire, |state| { + match self.state.try_update(Release, Acquire, |state| { if state.addr() & QUEUED == 0 { // If there are no threads queued, simply decrement the reader count. let count = state.addr() - (SINGLE | LOCKED); diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index b758737d00c6..22f9bfef5a38 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -44,15 +44,6 @@ impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn new(stack: usize, init: Box) -> io::Result { - // FIXME: remove this block once wasi-sdk is updated with the fix from - // https://github.com/WebAssembly/wasi-libc/pull/716 - // WASI does not support threading via pthreads. While wasi-libc provides - // pthread stubs, pthread_create returns EAGAIN, which causes confusing - // errors. We return UNSUPPORTED_PLATFORM directly instead. - if cfg!(all(target_os = "wasi", not(target_feature = "atomics"))) { - return Err(io::Error::UNSUPPORTED_PLATFORM); - } - let data = init; let mut attr: mem::MaybeUninit = mem::MaybeUninit::uninit(); assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0); diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 2e16f2cf27e7..366fa8e150f8 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1316,7 +1316,7 @@ def bootstrap(args): # Give a hard error if `--config` or `RUST_BOOTSTRAP_CONFIG` are set to a missing path, # but not if `bootstrap.toml` hasn't been created. if not using_default_path or os.path.exists(toml_path): - with open(toml_path) as config: + with open(toml_path, encoding="utf-8") as config: config_toml = config.read() else: config_toml = "" diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 8bbd03ac3afa..55426bfffc73 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -706,7 +706,7 @@ macro_rules! tool_check_step { const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.paths(&[ $path, $( $alt_path ),* ]) + run.path($path) $( .path( $alt_path ) )* } fn is_default_step(_builder: &Builder<'_>) -> bool { diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 405ab9f6eaa2..adba8f19894a 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1378,6 +1378,9 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS } } + // The host this new compiler will *run* on. + cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple); + if builder.config.rust_verify_llvm_ir { cargo.env("RUSTC_VERIFY_LLVM_IR", "1"); } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index a918ae929d2e..325f54d78a50 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -953,6 +953,13 @@ impl Step for Rustc { cargo.rustdocflag("--extern-html-root-url"); cargo.rustdocflag("ena=https://docs.rs/ena/latest/"); + // Point std library crate links to local docs for offline usage. + for krate in STD_PUBLIC_CRATES { + cargo.rustdocflag("--extern-html-root-url"); + cargo.rustdocflag(&format!("{krate}=../")); + } + cargo.rustdocflag("--extern-html-root-takes-precedence"); + let mut to_open = None; let out_dir = builder.stage_out(build_compiler, Mode::Rustc).join(target).join("doc"); diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index dbd4f1c81405..c04a8cd6b7ab 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1008,33 +1008,9 @@ impl Step for OmpOffload { t!(fs::create_dir_all(&out_dir)); builder.config.update_submodule("src/llvm-project"); - let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/runtimes/")); - // If we use an external clang as opposed to building our own llvm_clang, than that clang will - // come with it's own set of default include directories, which are based on a potentially older - // LLVM. This can cause issues, so we overwrite it to include headers based on our - // `src/llvm-project` submodule instead. - // FIXME(offload): With LLVM-22 we hopefully won't need an external clang anymore. - let mut cflags = CcFlags::default(); - if !builder.config.llvm_clang { - let base = builder.llvm_out(target).join("include"); - let inc_dir = base.display(); - cflags.push_all(format!(" -I {inc_dir}")); - } - - configure_cmake(builder, target, &mut cfg, true, LdFlags::default(), cflags, &[]); - - // Re-use the same flags as llvm to control the level of debug information - // generated for offload. - let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) { - (false, _) => "Debug", - (true, false) => "Release", - (true, true) => "RelWithDebInfo", - }; - trace!(?profile); - - // OpenMP/Offload builds currently (LLVM-21) still depend on Clang, although there are - // intentions to loosen this requirement for LLVM-22. If we were to + // OpenMP/Offload builds currently (LLVM-22) still depend on Clang, although there are + // intentions to loosen this requirement over time. FIXME(offload): re-evaluate on LLVM 23 let clang_dir = if !builder.config.llvm_clang { // We must have an external clang to use. assert!(&builder.build.config.llvm_clang_dir.is_some()); @@ -1044,23 +1020,66 @@ impl Step for OmpOffload { None }; - // FIXME(offload): Once we move from OMP to Offload (Ol) APIs, we should drop the openmp - // runtime to simplify our build. We should also re-evaluate the LLVM_Root and try to get - // rid of the Clang_DIR, once we upgrade to LLVM-22. - cfg.out_dir(&out_dir) - .profile(profile) - .env("LLVM_CONFIG_REAL", &host_llvm_config) - .define("LLVM_ENABLE_ASSERTIONS", "ON") - .define("LLVM_ENABLE_RUNTIMES", "openmp;offload") - .define("LLVM_INCLUDE_TESTS", "OFF") - .define("OFFLOAD_INCLUDE_TESTS", "OFF") - .define("OPENMP_STANDALONE_BUILD", "ON") - .define("LLVM_ROOT", builder.llvm_out(target).join("build")) - .define("LLVM_DIR", llvm_cmake_dir); - if let Some(p) = clang_dir { - cfg.define("Clang_DIR", p); + // In the context of OpenMP offload, some libraries must be compiled for the gpu target, + // some for the host, and others for both. We do not perform a full cross-compilation, since + // we don't want to run rustc on a GPU. + let omp_targets = vec![target.triple.as_ref(), "amdgcn-amd-amdhsa", "nvptx64-nvidia-cuda"]; + for omp_target in omp_targets { + let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/runtimes/")); + + // If we use an external clang as opposed to building our own llvm_clang, than that clang will + // come with it's own set of default include directories, which are based on a potentially older + // LLVM. This can cause issues, so we overwrite it to include headers based on our + // `src/llvm-project` submodule instead. + // FIXME(offload): With LLVM-22 we hopefully won't need an external clang anymore. + let mut cflags = CcFlags::default(); + if !builder.config.llvm_clang { + let base = builder.llvm_out(target).join("include"); + let inc_dir = base.display(); + cflags.push_all(format!(" -I {inc_dir}")); + } + + configure_cmake(builder, target, &mut cfg, true, LdFlags::default(), cflags, &[]); + + // Re-use the same flags as llvm to control the level of debug information + // generated for offload. + let profile = + match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) { + (false, _) => "Debug", + (true, false) => "Release", + (true, true) => "RelWithDebInfo", + }; + trace!(?profile); + + // FIXME(offload): Once we move from OMP to Offload (Ol) APIs, we should drop the openmp + // runtime to simplify our build. So far, these are still under development. + cfg.out_dir(&out_dir) + .profile(profile) + .env("LLVM_CONFIG_REAL", &host_llvm_config) + .define("LLVM_ENABLE_ASSERTIONS", "ON") + .define("LLVM_INCLUDE_TESTS", "OFF") + .define("OFFLOAD_INCLUDE_TESTS", "OFF") + .define("LLVM_ROOT", builder.llvm_out(target).join("build")) + .define("LLVM_DIR", llvm_cmake_dir.clone()) + .define("LLVM_DEFAULT_TARGET_TRIPLE", omp_target); + if let Some(p) = clang_dir.clone() { + cfg.define("Clang_DIR", p); + } + + // We don't perform a full cross-compilation of rustc, therefore our target.triple + // will still be a CPU target. + if *omp_target == *target.triple { + // The offload library provides functionality which only makes sense on the host. + cfg.define("LLVM_ENABLE_RUNTIMES", "openmp;offload"); + } else { + // OpenMP provides some device libraries, so we also compile it for all gpu targets. + cfg.define("LLVM_USE_LINKER", "lld"); + cfg.define("LLVM_ENABLE_RUNTIMES", "openmp"); + cfg.define("CMAKE_C_COMPILER_TARGET", omp_target); + cfg.define("CMAKE_CXX_COMPILER_TARGET", omp_target); + } + cfg.build(); } - cfg.build(); t!(stamp.write()); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 0add77f0b09a..fda9f3bbba3a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2184,6 +2184,9 @@ Please disable assertions with `rust.debug-assertions = false`. for flag in targetflags { cmd.arg("--target-rustcflags").arg(flag); } + if target.is_synthetic() { + cmd.arg("--target-rustcflags").arg("-Zunstable-options"); + } cmd.arg("--python").arg( builder.config.python.as_ref().expect("python is required for running rustdoc tests"), @@ -3156,7 +3159,7 @@ impl Step for CrateRustdoc { const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.paths(&["src/librustdoc", "src/tools/rustdoc"]) + run.path("src/librustdoc").path("src/tools/rustdoc") } fn is_default_step(_builder: &Builder<'_>) -> bool { @@ -3817,7 +3820,7 @@ impl Step for CodegenCranelift { const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.paths(&["compiler/rustc_codegen_cranelift"]) + run.path("compiler/rustc_codegen_cranelift") } fn is_default_step(_builder: &Builder<'_>) -> bool { @@ -3938,7 +3941,7 @@ impl Step for CodegenGCC { const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.paths(&["compiler/rustc_codegen_gcc"]) + run.path("compiler/rustc_codegen_gcc") } fn is_default_step(_builder: &Builder<'_>) -> bool { diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 7150b2b0d59f..e3fa826c45af 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -10,7 +10,7 @@ use crate::core::build_steps::tool::SourceType; use crate::core::config::SplitDebuginfo; use crate::core::config::flags::Color; use crate::utils::build_stamp; -use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_args, linker_flags}; +use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_flags}; use crate::{ BootstrapCommand, CLang, Compiler, Config, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, RemapScheme, TargetSelection, command, prepare_behaviour_dump_dir, t, @@ -310,7 +310,15 @@ impl Cargo { } } - for arg in linker_args(builder, compiler.host, LldThreads::Yes) { + // We need to set host linker flags for compiling build scripts and proc-macros. + // This is done the same way as the target linker flags below, so cargo won't see + // any fingerprint difference between host==target versus cross-compiled targets + // when it comes to those host build artifacts. + if let Some(host_linker) = builder.linker(compiler.host) { + let host = crate::envify(&compiler.host.triple); + self.command.env(format!("CARGO_TARGET_{host}_LINKER"), host_linker); + } + for arg in linker_flags(builder, compiler.host, LldThreads::Yes) { self.hostflags.arg(&arg); } @@ -319,11 +327,11 @@ impl Cargo { self.command.env(format!("CARGO_TARGET_{target}_LINKER"), target_linker); } // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not - // `linker_args` here. + // `linker_args` here. Cargo will pass that to both rustc and rustdoc invocations. for flag in linker_flags(builder, target, LldThreads::Yes) { self.rustflags.arg(&flag); } - for arg in linker_args(builder, target, LldThreads::Yes) { + for arg in linker_flags(builder, target, LldThreads::Yes) { self.rustdocflags.arg(&arg); } @@ -331,10 +339,6 @@ impl Cargo { self.rustflags.arg("-Clink-arg=-gz"); } - // Ignore linker warnings for now. These are complicated to fix and don't affect the build. - // FIXME: we should really investigate these... - self.rustflags.arg("-Alinker-messages"); - // Throughout the build Cargo can execute a number of build scripts // compiling C/C++ code and we need to pass compilers, archivers, flags, etc // obtained previously to those build scripts. @@ -1256,13 +1260,7 @@ impl Builder<'_> { // when compiling the standard library, since this might be linked into the final outputs // produced by rustc. Since this mitigation is only available on Windows, only enable it // for the standard library in case the compiler is run on a non-Windows platform. - // This is not needed for stage 0 artifacts because these will only be used for building - // the stage 1 compiler. - if cfg!(windows) - && mode == Mode::Std - && self.config.control_flow_guard - && compiler.stage >= 1 - { + if cfg!(windows) && mode == Mode::Std && self.config.control_flow_guard { rustflags.arg("-Ccontrol-flow-guard"); } @@ -1270,9 +1268,7 @@ impl Builder<'_> { // standard library, since this might be linked into the final outputs produced by rustc. // Since this mitigation is only available on Windows, only enable it for the standard // library in case the compiler is run on a non-Windows platform. - // This is not needed for stage 0 artifacts because these will only be used for building - // the stage 1 compiler. - if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 { + if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard { rustflags.arg("-Zehcont-guard"); } @@ -1289,52 +1285,13 @@ impl Builder<'_> { rustdocflags.arg("--crate-version").arg(&rust_version); // Environment variables *required* throughout the build - // - // FIXME: should update code to not require this env var - // The host this new compiler will *run* on. - cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple); // The host this new compiler is being *built* on. cargo.env("CFG_COMPILER_BUILD_TRIPLE", compiler.host.triple); // Set this for all builds to make sure doc builds also get it. cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel); - // This one's a bit tricky. As of the time of this writing the compiler - // links to the `winapi` crate on crates.io. This crate provides raw - // bindings to Windows system functions, sort of like libc does for - // Unix. This crate also, however, provides "import libraries" for the - // MinGW targets. There's an import library per dll in the windows - // distribution which is what's linked to. These custom import libraries - // are used because the winapi crate can reference Windows functions not - // present in the MinGW import libraries. - // - // For example MinGW may ship libdbghelp.a, but it may not have - // references to all the functions in the dbghelp dll. Instead the - // custom import library for dbghelp in the winapi crates has all this - // information. - // - // Unfortunately for us though the import libraries are linked by - // default via `-ldylib=winapi_foo`. That is, they're linked with the - // `dylib` type with a `winapi_` prefix (so the winapi ones don't - // conflict with the system MinGW ones). This consequently means that - // the binaries we ship of things like rustc_codegen_llvm (aka the rustc_codegen_llvm - // DLL) when linked against *again*, for example with procedural macros - // or plugins, will trigger the propagation logic of `-ldylib`, passing - // `-lwinapi_foo` to the linker again. This isn't actually available in - // our distribution, however, so the link fails. - // - // To solve this problem we tell winapi to not use its bundled import - // libraries. This means that it will link to the system MinGW import - // libraries by default, and the `-ldylib=foo` directives will still get - // passed to the final linker, but they'll look like `-lfoo` which can - // be resolved because MinGW has the import library. The downside is we - // don't get newer functions from Windows, but we don't use any of them - // anyway. - if !mode.is_tool() { - cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1"); - } - // verbose cargo output is very noisy, so only enable it with -vv for _ in 0..self.verbosity.saturating_sub(1) { cargo.arg("--verbose"); diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap index 8a7815487913..3adf952d66e0 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap @@ -44,7 +44,6 @@ expression: bench - Set({bench::compiler/rustc_errors}) - Set({bench::compiler/rustc_expand}) - Set({bench::compiler/rustc_feature}) - - Set({bench::compiler/rustc_fluent_macro}) - Set({bench::compiler/rustc_fs_util}) - Set({bench::compiler/rustc_graphviz}) - Set({bench::compiler/rustc_hashes}) @@ -98,4 +97,5 @@ expression: bench - Set({bench::compiler/rustc_windows_rc}) [Bench] test::CrateRustdoc targets: [x86_64-unknown-linux-gnu] - - Set({bench::src/librustdoc, bench::src/tools/rustdoc}) + - Set({bench::src/librustdoc}) + - Set({bench::src/tools/rustdoc}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap index 721ecaf4c487..1d6e63696b06 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap @@ -26,7 +26,6 @@ expression: build compiler - Set({build::compiler/rustc_errors}) - Set({build::compiler/rustc_expand}) - Set({build::compiler/rustc_feature}) - - Set({build::compiler/rustc_fluent_macro}) - Set({build::compiler/rustc_fs_util}) - Set({build::compiler/rustc_graphviz}) - Set({build::compiler/rustc_hashes}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap index 0fe26fac57fc..6fc2e190290e 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap @@ -28,7 +28,6 @@ expression: check - Set({check::compiler/rustc_errors}) - Set({check::compiler/rustc_expand}) - Set({check::compiler/rustc_feature}) - - Set({check::compiler/rustc_fluent_macro}) - Set({check::compiler/rustc_fs_util}) - Set({check::compiler/rustc_graphviz}) - Set({check::compiler/rustc_hashes}) @@ -82,7 +81,8 @@ expression: check - Set({check::compiler/rustc_windows_rc}) [Check] check::Rustdoc targets: [x86_64-unknown-linux-gnu] - - Set({check::src/librustdoc, check::src/tools/rustdoc}) + - Set({check::src/librustdoc}) + - Set({check::src/tools/rustdoc}) [Check] check::CraneliftCodegenBackend targets: [x86_64-unknown-linux-gnu] - Set({check::cg_clif}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap index 71d9c0fafb80..c0456f7f84d3 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap @@ -28,7 +28,6 @@ expression: check compiler - Set({check::compiler/rustc_errors}) - Set({check::compiler/rustc_expand}) - Set({check::compiler/rustc_feature}) - - Set({check::compiler/rustc_fluent_macro}) - Set({check::compiler/rustc_fs_util}) - Set({check::compiler/rustc_graphviz}) - Set({check::compiler/rustc_hashes}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap index dae515c67ec6..10f36ffa6748 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap @@ -28,7 +28,6 @@ expression: check compiletest --include-default-paths - Set({check::compiler/rustc_errors}) - Set({check::compiler/rustc_expand}) - Set({check::compiler/rustc_feature}) - - Set({check::compiler/rustc_fluent_macro}) - Set({check::compiler/rustc_fs_util}) - Set({check::compiler/rustc_graphviz}) - Set({check::compiler/rustc_hashes}) @@ -82,7 +81,8 @@ expression: check compiletest --include-default-paths - Set({check::compiler/rustc_windows_rc}) [Check] check::Rustdoc targets: [x86_64-unknown-linux-gnu] - - Set({check::src/librustdoc, check::src/tools/rustdoc}) + - Set({check::src/librustdoc}) + - Set({check::src/tools/rustdoc}) [Check] check::CraneliftCodegenBackend targets: [x86_64-unknown-linux-gnu] - Set({check::cg_clif}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap index fe8334641285..492a10d3862a 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap @@ -43,7 +43,6 @@ expression: clippy - Set({clippy::compiler/rustc_errors}) - Set({clippy::compiler/rustc_expand}) - Set({clippy::compiler/rustc_feature}) - - Set({clippy::compiler/rustc_fluent_macro}) - Set({clippy::compiler/rustc_fs_util}) - Set({clippy::compiler/rustc_graphviz}) - Set({clippy::compiler/rustc_hashes}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap index 222c0ffb4050..41889cd12480 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap @@ -28,7 +28,6 @@ expression: fix - Set({fix::compiler/rustc_errors}) - Set({fix::compiler/rustc_expand}) - Set({fix::compiler/rustc_feature}) - - Set({fix::compiler/rustc_fluent_macro}) - Set({fix::compiler/rustc_fs_util}) - Set({fix::compiler/rustc_graphviz}) - Set({fix::compiler/rustc_hashes}) @@ -82,7 +81,8 @@ expression: fix - Set({fix::compiler/rustc_windows_rc}) [Fix] check::Rustdoc targets: [x86_64-unknown-linux-gnu] - - Set({fix::src/librustdoc, fix::src/tools/rustdoc}) + - Set({fix::src/librustdoc}) + - Set({fix::src/tools/rustdoc}) [Fix] check::CraneliftCodegenBackend targets: [x86_64-unknown-linux-gnu] - Set({fix::cg_clif}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap index 4ab84c3cabc1..51e2c270e3ba 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap @@ -94,7 +94,6 @@ expression: test - Set({test::compiler/rustc_errors}) - Set({test::compiler/rustc_expand}) - Set({test::compiler/rustc_feature}) - - Set({test::compiler/rustc_fluent_macro}) - Set({test::compiler/rustc_fs_util}) - Set({test::compiler/rustc_graphviz}) - Set({test::compiler/rustc_hashes}) @@ -148,7 +147,8 @@ expression: test - Set({test::compiler/rustc_windows_rc}) [Test] test::CrateRustdoc targets: [x86_64-unknown-linux-gnu] - - Set({test::src/librustdoc, test::src/tools/rustdoc}) + - Set({test::src/librustdoc}) + - Set({test::src/tools/rustdoc}) [Test] test::CrateRustdocJsonTypes targets: [x86_64-unknown-linux-gnu] - Set({test::src/rustdoc-json-types}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_librustdoc_rustdoc.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_librustdoc_rustdoc.snap index c8eee72aec42..8dcdce0c17ab 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_librustdoc_rustdoc.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_librustdoc_rustdoc.snap @@ -4,7 +4,8 @@ expression: test librustdoc rustdoc --- [Test] test::CrateRustdoc targets: [x86_64-unknown-linux-gnu] - - Set({test::src/librustdoc, test::src/tools/rustdoc}) + - Set({test::src/librustdoc}) + - Set({test::src/tools/rustdoc}) [Test] test::RustdocBook targets: [x86_64-unknown-linux-gnu] - Set({test::src/doc/rustdoc}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap index 2a4805e4fd68..bc828c162bb0 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap @@ -93,7 +93,6 @@ expression: test --skip=coverage - Set({test::compiler/rustc_errors}) - Set({test::compiler/rustc_expand}) - Set({test::compiler/rustc_feature}) - - Set({test::compiler/rustc_fluent_macro}) - Set({test::compiler/rustc_fs_util}) - Set({test::compiler/rustc_graphviz}) - Set({test::compiler/rustc_hashes}) @@ -147,7 +146,8 @@ expression: test --skip=coverage - Set({test::compiler/rustc_windows_rc}) [Test] test::CrateRustdoc targets: [x86_64-unknown-linux-gnu] - - Set({test::src/librustdoc, test::src/tools/rustdoc}) + - Set({test::src/librustdoc}) + - Set({test::src/tools/rustdoc}) [Test] test::CrateRustdocJsonTypes targets: [x86_64-unknown-linux-gnu] - Set({test::src/rustdoc-json-types}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap index 1468964c7818..ceb910e4cb36 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap @@ -1,6 +1,5 @@ --- source: src/bootstrap/src/core/builder/cli_paths/tests.rs -assertion_line: 68 expression: test --skip=tests --- [Test] test::Tidy @@ -58,7 +57,6 @@ expression: test --skip=tests - Set({test::compiler/rustc_errors}) - Set({test::compiler/rustc_expand}) - Set({test::compiler/rustc_feature}) - - Set({test::compiler/rustc_fluent_macro}) - Set({test::compiler/rustc_fs_util}) - Set({test::compiler/rustc_graphviz}) - Set({test::compiler/rustc_hashes}) @@ -112,7 +110,8 @@ expression: test --skip=tests - Set({test::compiler/rustc_windows_rc}) [Test] test::CrateRustdoc targets: [x86_64-unknown-linux-gnu] - - Set({test::src/librustdoc, test::src/tools/rustdoc}) + - Set({test::src/librustdoc}) + - Set({test::src/tools/rustdoc}) [Test] test::CrateRustdocJsonTypes targets: [x86_64-unknown-linux-gnu] - Set({test::src/rustdoc-json-types}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap index 7ff6a201e77a..f0e8f1aee2c7 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap @@ -1,6 +1,5 @@ --- source: src/bootstrap/src/core/builder/cli_paths/tests.rs -assertion_line: 68 expression: test --skip=tests --skip=coverage-map --skip=coverage-run --skip=library --skip=tidyselftest --- [Test] test::Tidy @@ -38,7 +37,6 @@ expression: test --skip=tests --skip=coverage-map --skip=coverage-run --skip=lib - Set({test::compiler/rustc_errors}) - Set({test::compiler/rustc_expand}) - Set({test::compiler/rustc_feature}) - - Set({test::compiler/rustc_fluent_macro}) - Set({test::compiler/rustc_fs_util}) - Set({test::compiler/rustc_graphviz}) - Set({test::compiler/rustc_hashes}) @@ -92,7 +90,8 @@ expression: test --skip=tests --skip=coverage-map --skip=coverage-run --skip=lib - Set({test::compiler/rustc_windows_rc}) [Test] test::CrateRustdoc targets: [x86_64-unknown-linux-gnu] - - Set({test::src/librustdoc, test::src/tools/rustdoc}) + - Set({test::src/librustdoc}) + - Set({test::src/tools/rustdoc}) [Test] test::CrateRustdocJsonTypes targets: [x86_64-unknown-linux-gnu] - Set({test::src/rustdoc-json-types}) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index a995f5c8a219..ae91b2040629 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -558,38 +558,19 @@ impl<'a> ShouldRun<'a> { /// single, non-aliased path /// /// Must be an on-disk path; use `alias` for names that do not correspond to on-disk paths. - pub fn path(self, path: &str) -> Self { - self.paths(&[path]) - } - - /// Multiple aliases for the same job. - /// - /// This differs from [`path`] in that multiple calls to path will end up calling `make_run` - /// multiple times, whereas a single call to `paths` will only ever generate a single call to - /// `make_run`. - /// - /// This is analogous to `all_krates`, although `all_krates` is gone now. Prefer [`path`] where possible. - /// - /// [`path`]: ShouldRun::path - pub fn paths(mut self, paths: &[&str]) -> Self { + pub fn path(mut self, path: &str) -> Self { let submodules_paths = self.builder.submodule_paths(); - self.paths.insert(PathSet::Set( - paths - .iter() - .map(|p| { - // assert only if `p` isn't submodule - if !submodules_paths.iter().any(|sm_p| p.contains(sm_p)) { - assert!( - self.builder.src.join(p).exists(), - "`should_run.paths` should correspond to real on-disk paths - use `alias` if there is no relevant path: {p}" - ); - } + // assert only if `p` isn't submodule + if !submodules_paths.iter().any(|sm_p| path.contains(sm_p)) { + assert!( + self.builder.src.join(path).exists(), + "`should_run.path` should correspond to a real on-disk path - use `alias` if there is no relevant path: {path}" + ); + } - TaskPath { path: p.into(), kind: Some(self.kind) } - }) - .collect(), - )); + let task = TaskPath { path: path.into(), kind: Some(self.kind) }; + self.paths.insert(PathSet::Set(BTreeSet::from_iter([task]))); self } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index af26c2f87d02..61db494c8c18 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1815,7 +1815,7 @@ mod snapshot { insta::assert_snapshot!( ctx.config("check") .path("compiler") - .render_steps(), @"[check] rustc 0 -> rustc 1 (75 crates)"); + .render_steps(), @"[check] rustc 0 -> rustc 1 (74 crates)"); } #[test] @@ -1841,7 +1841,7 @@ mod snapshot { ctx.config("check") .path("compiler") .stage(1) - .render_steps(), @"[check] rustc 0 -> rustc 1 (75 crates)"); + .render_steps(), @"[check] rustc 0 -> rustc 1 (74 crates)"); } #[test] @@ -1855,7 +1855,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [check] rustc 1 -> rustc 2 (75 crates) + [check] rustc 1 -> rustc 2 (74 crates) "); } @@ -1871,7 +1871,7 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [check] rustc 1 -> std 1 - [check] rustc 1 -> rustc 2 (75 crates) + [check] rustc 1 -> rustc 2 (74 crates) [check] rustc 1 -> rustc 2 [check] rustc 1 -> Rustdoc 2 [check] rustc 1 -> rustc_codegen_cranelift 2 @@ -1967,7 +1967,7 @@ mod snapshot { ctx.config("check") .paths(&["library", "compiler"]) .args(&args) - .render_steps(), @"[check] rustc 0 -> rustc 1 (75 crates)"); + .render_steps(), @"[check] rustc 0 -> rustc 1 (74 crates)"); } #[test] diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index f1bf6e399fb1..fee9b12aca06 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -3,8 +3,6 @@ /// See pub static CRATES: &[&str] = &[ // tidy-alphabetical-start - "annotate-snippets", - "anstyle", "askama_derive", "askama_parser", "basic-toml", @@ -59,7 +57,6 @@ pub static CRATES: &[&str] = &[ "unic-langid-impl", "unic-langid-macros", "unicode-ident", - "unicode-width", "version_check", "wasm-bindgen-macro-support", "wasm-bindgen-shared", diff --git a/src/ci/citool/tests/jobs.rs b/src/ci/citool/tests/jobs.rs index b0309c52003f..247871de6025 100644 --- a/src/ci/citool/tests/jobs.rs +++ b/src/ci/citool/tests/jobs.rs @@ -6,7 +6,7 @@ const TEST_JOBS_YML_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/tes fn auto_jobs() { let stdout = get_matrix("push", "commit", "refs/heads/automation/bors/auto"); insta::assert_snapshot!(stdout, @r#" - jobs=[{"name":"aarch64-gnu","full_name":"auto - aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"x86_64-gnu-llvm-18-1","full_name":"auto - x86_64-gnu-llvm-18-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DOCKER_SCRIPT":"stage_2_test_set1.sh","IMAGE":"x86_64-gnu-llvm-18","READ_ONLY_SRC":"0","RUST_BACKTRACE":1,"TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"aarch64-apple","full_name":"auto - aarch64-apple","os":"macos-14","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DEVELOPER_DIR":"/Applications/Xcode_15.4.app/Contents/Developer","MACOSX_DEPLOYMENT_TARGET":11.0,"MACOSX_STD_DEPLOYMENT_TARGET":11.0,"NO_DEBUG_ASSERTIONS":1,"NO_LLVM_ASSERTIONS":1,"NO_OVERFLOW_CHECKS":1,"RUSTC_RETRY_LINKER_ON_SEGFAULT":1,"RUST_CONFIGURE_ARGS":"--enable-sanitizers --enable-profiler --set rust.jemalloc","SCRIPT":"./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin","TOOLSTATE_PUBLISH":1,"USE_XCODE_CLANG":1}},{"name":"dist-i686-msvc","full_name":"auto - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}},{"name":"pr-check-1","full_name":"auto - pr-check-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"pr-check-2","full_name":"auto - pr-check-2","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"tidy","full_name":"auto - tidy","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true,"doc_url":"https://foo.bar"}] + jobs=[{"name":"aarch64-gnu","full_name":"auto - aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"x86_64-gnu-llvm-18-1","full_name":"auto - x86_64-gnu-llvm-18-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DOCKER_SCRIPT":"stage_2_test_set1.sh","IMAGE":"x86_64-gnu-llvm-18","READ_ONLY_SRC":"0","RUST_BACKTRACE":1,"TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"aarch64-apple","full_name":"auto - aarch64-apple","os":"macos-15","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DEVELOPER_DIR":"/Applications/Xcode_26.2.app/Contents/Developer","MACOSX_DEPLOYMENT_TARGET":11.0,"MACOSX_STD_DEPLOYMENT_TARGET":11.0,"NO_DEBUG_ASSERTIONS":1,"NO_LLVM_ASSERTIONS":1,"NO_OVERFLOW_CHECKS":1,"RUSTC_RETRY_LINKER_ON_SEGFAULT":1,"RUST_CONFIGURE_ARGS":"--enable-sanitizers --enable-profiler --set rust.jemalloc","SCRIPT":"./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin","TOOLSTATE_PUBLISH":1}},{"name":"dist-i686-msvc","full_name":"auto - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}},{"name":"pr-check-1","full_name":"auto - pr-check-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"pr-check-2","full_name":"auto - pr-check-2","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"tidy","full_name":"auto - tidy","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true,"doc_url":"https://foo.bar"}] run_type=auto "#); } diff --git a/src/ci/citool/tests/test-jobs.yml b/src/ci/citool/tests/test-jobs.yml index 95cd328e7848..3bad1fe1b427 100644 --- a/src/ci/citool/tests/test-jobs.yml +++ b/src/ci/citool/tests/test-jobs.yml @@ -13,7 +13,7 @@ runners: <<: *base-job - &job-macos-m1 - os: macos-14 + os: macos-15 <<: *base-job - &job-windows @@ -33,7 +33,7 @@ envs: # Ensure that host tooling is tested on our minimum supported macOS version. MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 - DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -112,8 +112,7 @@ auto: --enable-profiler --set rust.jemalloc RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer - USE_XCODE_CLANG: 1 + DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else # supports the hardware, so only need to test it there. MACOSX_DEPLOYMENT_TARGET: 11.0 diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index 323cd409a978..dacf3c0db202 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -85,9 +85,9 @@ RUN /tmp/build-fuchsia-toolchain.sh COPY host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/ RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-29/wasi-sdk-29.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-30/wasi-sdk-30.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/tmp/wasi-sdk-29.0-x86_64-linux +ENV WASI_SDK_PATH=/tmp/wasi-sdk-30.0-x86_64-linux COPY scripts/freebsd-toolchain.sh /tmp/ RUN /tmp/freebsd-toolchain.sh i686 diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile index 68162d136c3f..885bf0b30b18 100644 --- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile @@ -21,9 +21,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ mingw-w64 \ && rm -rf /var/lib/apt/lists/* -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-29/wasi-sdk-29.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-30/wasi-sdk-30.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/wasi-sdk-29.0-x86_64-linux +ENV WASI_SDK_PATH=/wasi-sdk-30.0-x86_64-linux ENV RUST_CONFIGURE_ARGS="--set rust.validate-mir-opts=3" diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 9b1bf6c0df99..6512240730cc 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -41,9 +41,9 @@ WORKDIR / COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-29/wasi-sdk-29.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-30/wasi-sdk-30.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/wasi-sdk-29.0-x86_64-linux +ENV WASI_SDK_PATH=/wasi-sdk-30.0-x86_64-linux ENV RUST_CONFIGURE_ARGS \ --musl-root-x86_64=/usr/local/x86_64-linux-musl \ diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 6f333ecde79b..2300904f22c1 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -23,7 +23,7 @@ runners: <<: *base-job - &job-macos - os: macos-14 + os: macos-15 # macOS 15 Arm64 <<: *base-job - &job-windows @@ -459,23 +459,46 @@ auto: # Ensure that host tooling is built to support our minimum support macOS version. MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 - DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer - USE_XCODE_CLANG: 1 + DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift <<: *job-macos - name: dist-apple-various env: - SCRIPT: ./x.py dist bootstrap --include-default-paths --host='' --target=aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim,aarch64-apple-ios-macabi,x86_64-apple-ios-macabi + # Build and distribute the standard library for these targets. + TARGETS: "aarch64-apple-ios,\ + aarch64-apple-ios-sim,\ + x86_64-apple-ios,\ + aarch64-apple-ios-macabi,\ + x86_64-apple-ios-macabi,\ + aarch64-apple-tvos,\ + aarch64-apple-tvos-sim,\ + aarch64-apple-visionos,\ + aarch64-apple-visionos-sim,\ + aarch64-apple-watchos,\ + aarch64-apple-watchos-sim" + SCRIPT: ./x.py dist bootstrap --include-default-paths --host='' --target=$TARGETS # Mac Catalyst cannot currently compile the sanitizer: # https://github.com/rust-lang/rust/issues/129069 - RUST_CONFIGURE_ARGS: --enable-sanitizers --enable-profiler --set rust.jemalloc --set target.aarch64-apple-ios-macabi.sanitizers=false --set target.x86_64-apple-ios-macabi.sanitizers=false + # + # And tvOS and watchOS don't currently support the profiler runtime: + # https://github.com/rust-lang/rust/issues/152426 + RUST_CONFIGURE_ARGS: >- + --enable-sanitizers + --enable-profiler + --set rust.jemalloc + --set target.aarch64-apple-ios-macabi.sanitizers=false + --set target.x86_64-apple-ios-macabi.sanitizers=false + --set target.aarch64-apple-tvos.profiler=false + --set target.aarch64-apple-tvos-sim.profiler=false + --set target.aarch64-apple-watchos.profiler=false + --set target.aarch64-apple-watchos-sim.profiler=false # Ensure that host tooling is built to support our minimum support macOS version. # FIXME(madsmtm): This might be redundant, as we're not building host tooling here (?) MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 - DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer <<: *job-macos - name: dist-aarch64-apple @@ -496,8 +519,7 @@ auto: # supports the hardware. MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 - DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer - USE_XCODE_CLANG: 1 + DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift <<: *job-macos @@ -511,8 +533,7 @@ auto: --enable-sanitizers --enable-profiler --set rust.jemalloc - DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer - USE_XCODE_CLANG: 1 + DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else # supports the hardware, so only need to test it there. MACOSX_DEPLOYMENT_TARGET: 11.0 diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh index 657e1492a6d6..75e8fc240660 100755 --- a/src/ci/scripts/install-clang.sh +++ b/src/ci/scripts/install-clang.sh @@ -9,41 +9,11 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" -# Update both macOS's and Windows's tarballs when bumping the version here. +# Update Windows's tarballs when bumping the version here. # Try to keep this in sync with src/ci/docker/scripts/build-clang.sh LLVM_VERSION="20.1.3" -if isMacOS; then - # FIXME: This is the latest pre-built version of LLVM that's available for - # x86_64 MacOS. We may want to consider building our own LLVM binaries - # instead, or set `USE_XCODE_CLANG` like AArch64 does. - LLVM_VERSION="15.0.7" - - # If the job selects a specific Xcode version, use that instead of - # downloading our own version. - if [[ ${USE_XCODE_CLANG-0} -eq 1 ]]; then - bindir="$(xcode-select --print-path)/Toolchains/XcodeDefault.xctoolchain/usr/bin" - else - file="${MIRRORS_BASE}/clang%2Bllvm-${LLVM_VERSION}-x86_64-apple-darwin21.0.tar.xz" - retry curl -f "${file}" -o "clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin21.0.tar.xz" - tar xJf "clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin21.0.tar.xz" - bindir="$(pwd)/clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin21.0/bin" - fi - - ciCommandSetEnv CC "${bindir}/clang" - ciCommandSetEnv CXX "${bindir}/clang++" - - # macOS 10.15 onwards doesn't have libraries in /usr/include anymore: those - # are now located deep into the filesystem, under Xcode's own files. The - # native clang is configured to use the correct path, but our custom one - # doesn't. This sets the SDKROOT environment variable to the SDK so that - # our own clang can figure out the correct include path on its own. - ciCommandSetEnv SDKROOT "$(xcrun --sdk macosx --show-sdk-path)" - - # Configure `AR` specifically so bootstrap doesn't try to infer it as - # `clang-ar` by accident. - ciCommandSetEnv AR "ar" -elif isWindows && ! isKnownToBeMingwBuild; then +if isWindows && ! isKnownToBeMingwBuild; then # If we're compiling for MSVC then we, like most other distribution builders, # switch to clang as the compiler. This'll allow us eventually to enable LTO # amongst LLVM and rustc. Note that we only do this on MSVC as I don't think diff --git a/src/doc/book b/src/doc/book index 39aeceaa3aea..05d114287b7d 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 39aeceaa3aeab845bc4517e7a44e48727d3b9dbe +Subproject commit 05d114287b7d6f6c9253d5242540f00fbd6172ab diff --git a/src/doc/index.md b/src/doc/index.md index 892057a8f4db..418939353975 100644 --- a/src/doc/index.md +++ b/src/doc/index.md @@ -194,9 +194,8 @@ resources maintained by the [Embedded Working Group] useful. #### The Embedded Rust Book -[The Embedded Rust Book] is targeted at developers familiar with embedded -development and familiar with Rust, but have not used Rust for embedded -development. +[The Embedded Rust Book] is targeted at developers who are familiar with embedded +development and Rust, but who have not used Rust for embedded development. [The Embedded Rust Book]: embedded-book/index.html [Rust project]: https://www.rust-lang.org diff --git a/src/doc/nomicon b/src/doc/nomicon index 050c002a360f..b8f254a991b8 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 050c002a360fa45b701ea34feed7a860dc8a41bf +Subproject commit b8f254a991b8b7e8f704527f0d4f343a4697dfa9 diff --git a/src/doc/not_found.md b/src/doc/not_found.md index 9552759d2b8b..69e6ae3e2d03 100644 --- a/src/doc/not_found.md +++ b/src/doc/not_found.md @@ -41,7 +41,7 @@ Some things that might be helpful to you though: