From 0e6f7c6c7c8f64327150c973606c1a87d587a3cc Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 20 Dec 2023 17:41:51 +0000 Subject: [PATCH 01/18] Add AsyncFn family of traits --- library/core/src/lib.rs | 1 + library/core/src/ops/async_function.rs | 105 ++++++++++++++++++++++ library/core/src/ops/mod.rs | 4 + tests/ui/async-await/async-fn/simple.rs | 16 ++++ tests/ui/did_you_mean/bad-assoc-ty.stderr | 9 +- 5 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 library/core/src/ops/async_function.rs create mode 100644 tests/ui/async-await/async-fn/simple.rs diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 07720f235989b..1cd0240b87cc6 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -227,6 +227,7 @@ #![feature(fundamental)] #![feature(generic_arg_infer)] #![feature(if_let_guard)] +#![feature(impl_trait_in_assoc_type)] #![feature(inline_const)] #![feature(intra_doc_pointers)] #![feature(intrinsics)] diff --git a/library/core/src/ops/async_function.rs b/library/core/src/ops/async_function.rs new file mode 100644 index 0000000000000..0e05badd06df8 --- /dev/null +++ b/library/core/src/ops/async_function.rs @@ -0,0 +1,105 @@ +use crate::future::Future; +use crate::marker::Tuple; + +/// An async-aware version of the [`Fn`](crate::ops::Fn) trait. +/// +/// All `async fn` and functions returning futures implement this trait. +#[unstable(feature = "async_fn_traits", issue = "none")] +#[rustc_paren_sugar] +#[fundamental] +#[must_use = "async closures are lazy and do nothing unless called"] +pub trait AsyncFn: AsyncFnMut { + /// Future returned by [`AsyncFn::async_call`]. + #[unstable(feature = "async_fn_traits", issue = "none")] + type CallFuture<'a>: Future + where + Self: 'a; + + /// Call the [`AsyncFn`], returning a future which may borrow from the called closure. + #[unstable(feature = "async_fn_traits", issue = "none")] + extern "rust-call" fn async_call(&self, args: Args) -> Self::CallFuture<'_>; +} + +/// An async-aware version of the [`FnMut`](crate::ops::FnMut) trait. +/// +/// All `async fn` and functions returning futures implement this trait. +#[unstable(feature = "async_fn_traits", issue = "none")] +#[rustc_paren_sugar] +#[fundamental] +#[must_use = "async closures are lazy and do nothing unless called"] +pub trait AsyncFnMut: AsyncFnOnce { + /// Future returned by [`AsyncFnMut::async_call_mut`]. + #[unstable(feature = "async_fn_traits", issue = "none")] + type CallMutFuture<'a>: Future + where + Self: 'a; + + /// Call the [`AsyncFnMut`], returning a future which may borrow from the called closure. + #[unstable(feature = "async_fn_traits", issue = "none")] + extern "rust-call" fn async_call_mut(&mut self, args: Args) -> Self::CallMutFuture<'_>; +} + +/// An async-aware version of the [`FnOnce`](crate::ops::FnOnce) trait. +/// +/// All `async fn` and functions returning futures implement this trait. +#[unstable(feature = "async_fn_traits", issue = "none")] +#[rustc_paren_sugar] +#[fundamental] +#[must_use = "async closures are lazy and do nothing unless called"] +pub trait AsyncFnOnce { + /// Future returned by [`AsyncFnOnce::async_call_once`]. + #[unstable(feature = "async_fn_traits", issue = "none")] + type CallOnceFuture: Future; + + /// Output type of the called closure's future. + #[unstable(feature = "async_fn_traits", issue = "none")] + type Output; + + /// Call the [`AsyncFnOnce`], returning a future which may move out of the called closure. + #[unstable(feature = "async_fn_traits", issue = "none")] + extern "rust-call" fn async_call_once(self, args: Args) -> Self::CallOnceFuture; +} + +mod impls { + use super::{AsyncFn, AsyncFnMut, AsyncFnOnce}; + use crate::future::Future; + use crate::marker::Tuple; + + #[unstable(feature = "async_fn_traits", issue = "none")] + impl, A: Tuple> AsyncFn for F + where + >::Output: Future, + { + type CallFuture<'a> = impl Future where Self: 'a; + + extern "rust-call" fn async_call(&self, args: A) -> Self::CallFuture<'_> { + async { self.call(args).await } + } + } + + #[unstable(feature = "async_fn_traits", issue = "none")] + impl, A: Tuple> AsyncFnMut for F + where + >::Output: Future, + { + type CallMutFuture<'a> = impl Future where Self: 'a; + + extern "rust-call" fn async_call_mut(&mut self, args: A) -> Self::CallMutFuture<'_> { + async { self.call_mut(args).await } + } + } + + #[unstable(feature = "async_fn_traits", issue = "none")] + impl, A: Tuple> AsyncFnOnce for F + where + >::Output: Future, + { + type CallOnceFuture = impl Future; + + type Output = <>::Output as Future>::Output; + + extern "rust-call" fn async_call_once(self, args: A) -> Self::CallOnceFuture { + async { self.call_once(args).await } + } + } +} diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 35654d0b853b7..4289a86f89b06 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -139,6 +139,7 @@ #![stable(feature = "rust1", since = "1.0.0")] mod arith; +mod async_function; mod bit; mod control_flow; mod coroutine; @@ -173,6 +174,9 @@ pub use self::drop::Drop; #[stable(feature = "rust1", since = "1.0.0")] pub use self::function::{Fn, FnMut, FnOnce}; +#[unstable(feature = "async_fn_traits", issue = "none")] +pub use self::async_function::{AsyncFn, AsyncFnMut, AsyncFnOnce}; + #[stable(feature = "rust1", since = "1.0.0")] pub use self::index::{Index, IndexMut}; diff --git a/tests/ui/async-await/async-fn/simple.rs b/tests/ui/async-await/async-fn/simple.rs new file mode 100644 index 0000000000000..36d1a6d71036b --- /dev/null +++ b/tests/ui/async-await/async-fn/simple.rs @@ -0,0 +1,16 @@ +// edition: 2021 +// check-pass + +#![feature(async_fn_traits)] + +use std::ops::AsyncFn; + +async fn foo() {} + +async fn call_asyncly(f: impl AsyncFn(i32) -> i32) -> i32 { + f.async_call((1i32,)).await +} + +fn main() { + let fut = call_asyncly(|x| async move { x + 1 }); +} diff --git a/tests/ui/did_you_mean/bad-assoc-ty.stderr b/tests/ui/did_you_mean/bad-assoc-ty.stderr index 5c0c7a0b94ffe..356ddf74bb635 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.stderr +++ b/tests/ui/did_you_mean/bad-assoc-ty.stderr @@ -191,7 +191,14 @@ error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:33:10 | LL | type H = Fn(u8) -> (u8)::Output; - | ^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: use fully-qualified syntax + | +LL | type H = <(dyn Fn(u8) -> u8 + 'static) as AsyncFnOnce>::Output; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | type H = <(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:39:19 From fde86e586dfc9826d0db81d1230aba77eb043bc6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 20 Dec 2023 17:46:34 +0000 Subject: [PATCH 02/18] We do not need impl_trait_in_assoc_ty --- library/core/src/lib.rs | 1 - library/core/src/ops/async_function.rs | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 1cd0240b87cc6..07720f235989b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -227,7 +227,6 @@ #![feature(fundamental)] #![feature(generic_arg_infer)] #![feature(if_let_guard)] -#![feature(impl_trait_in_assoc_type)] #![feature(inline_const)] #![feature(intra_doc_pointers)] #![feature(intrinsics)] diff --git a/library/core/src/ops/async_function.rs b/library/core/src/ops/async_function.rs index 0e05badd06df8..5e5267ed97abf 100644 --- a/library/core/src/ops/async_function.rs +++ b/library/core/src/ops/async_function.rs @@ -70,10 +70,10 @@ mod impls { where >::Output: Future, { - type CallFuture<'a> = impl Future where Self: 'a; + type CallFuture<'a> = >::Output where Self: 'a; extern "rust-call" fn async_call(&self, args: A) -> Self::CallFuture<'_> { - async { self.call(args).await } + self.call(args) } } @@ -82,10 +82,10 @@ mod impls { where >::Output: Future, { - type CallMutFuture<'a> = impl Future where Self: 'a; + type CallMutFuture<'a> = >::Output where Self: 'a; extern "rust-call" fn async_call_mut(&mut self, args: A) -> Self::CallMutFuture<'_> { - async { self.call_mut(args).await } + self.call_mut(args) } } @@ -94,12 +94,12 @@ mod impls { where >::Output: Future, { - type CallOnceFuture = impl Future; + type CallOnceFuture = >::Output; type Output = <>::Output as Future>::Output; extern "rust-call" fn async_call_once(self, args: A) -> Self::CallOnceFuture { - async { self.call_once(args).await } + self.call_once(args) } } } From 17b433351d6c9ef4fb74dae985291b4eb073c807 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 20 Dec 2023 18:03:21 +0000 Subject: [PATCH 03/18] select AsyncFn traits during overloaded call op --- compiler/rustc_hir/src/lang_items.rs | 4 ++++ compiler/rustc_hir_typeck/src/callee.rs | 11 +++++++++++ compiler/rustc_span/src/symbol.rs | 6 ++++++ library/core/src/ops/async_function.rs | 3 +++ tests/ui/async-await/async-fn/simple.rs | 2 +- 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 3f3b57ba94f98..783a050742df9 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -208,6 +208,10 @@ language_item_table! { FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); + AsyncFn, sym::async_fn, async_fn_trait, Target::Trait, GenericRequirement::Exact(1); + AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); + AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1); + FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; Iterator, sym::iterator, iterator_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 2146effd84f06..e464dd45124d8 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -220,6 +220,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.lang_items().fn_trait(), Ident::with_dummy_span(sym::call), true), (self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true), (self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false), + (self.tcx.lang_items().async_fn_trait(), Ident::with_dummy_span(sym::async_call), true), + ( + self.tcx.lang_items().async_fn_mut_trait(), + Ident::with_dummy_span(sym::async_call_mut), + true, + ), + ( + self.tcx.lang_items().async_fn_once_trait(), + Ident::with_dummy_span(sym::async_call_once), + false, + ), ] { let Some(trait_def_id) = opt_trait_def_id else { continue }; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 95106cc64c129..a123a95352032 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -423,8 +423,14 @@ symbols! { assume, assume_init, async_await, + async_call, + async_call_mut, + async_call_once, async_closure, + async_fn, async_fn_in_trait, + async_fn_mut, + async_fn_once, async_fn_track_caller, async_for_loop, async_iterator, diff --git a/library/core/src/ops/async_function.rs b/library/core/src/ops/async_function.rs index 5e5267ed97abf..965873f163eff 100644 --- a/library/core/src/ops/async_function.rs +++ b/library/core/src/ops/async_function.rs @@ -8,6 +8,7 @@ use crate::marker::Tuple; #[rustc_paren_sugar] #[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] +#[cfg_attr(not(bootstrap), lang = "async_fn")] pub trait AsyncFn: AsyncFnMut { /// Future returned by [`AsyncFn::async_call`]. #[unstable(feature = "async_fn_traits", issue = "none")] @@ -27,6 +28,7 @@ pub trait AsyncFn: AsyncFnMut { #[rustc_paren_sugar] #[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] +#[cfg_attr(not(bootstrap), lang = "async_fn_mut")] pub trait AsyncFnMut: AsyncFnOnce { /// Future returned by [`AsyncFnMut::async_call_mut`]. #[unstable(feature = "async_fn_traits", issue = "none")] @@ -46,6 +48,7 @@ pub trait AsyncFnMut: AsyncFnOnce { #[rustc_paren_sugar] #[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] +#[cfg_attr(not(bootstrap), lang = "async_fn_once")] pub trait AsyncFnOnce { /// Future returned by [`AsyncFnOnce::async_call_once`]. #[unstable(feature = "async_fn_traits", issue = "none")] diff --git a/tests/ui/async-await/async-fn/simple.rs b/tests/ui/async-await/async-fn/simple.rs index 36d1a6d71036b..99a5d56a3093b 100644 --- a/tests/ui/async-await/async-fn/simple.rs +++ b/tests/ui/async-await/async-fn/simple.rs @@ -8,7 +8,7 @@ use std::ops::AsyncFn; async fn foo() {} async fn call_asyncly(f: impl AsyncFn(i32) -> i32) -> i32 { - f.async_call((1i32,)).await + f(1).await } fn main() { From 2c2f3ed2c4a99d6d2bc4c1a45890146c7b0c8561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 28 Dec 2023 19:09:04 +0000 Subject: [PATCH 04/18] Provide more context on recursive `impl` evaluation overflow When an associated type `Self::Assoc` is part of a `where` clause, we end up unable to evaluate the requirement and emit a E0275. We now point at the associated type if specified in the `impl`. If so, we also suggest using that type instead of `Self::Assoc`. Otherwise, we explain that these are not allowed. ``` error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` --> $DIR/impl-wf-cycle-1.rs:15:1 | LL | / impl Grault for (T,) LL | | LL | | where LL | | Self::A: Baz, LL | | Self::B: Fiz, | |_________________^ LL | { LL | type A = (); | ------ associated type `<(T,) as Grault>::A` is specified here | note: required for `(T,)` to implement `Grault` --> $DIR/impl-wf-cycle-1.rs:15:17 | LL | impl Grault for (T,) | ^^^^^^ ^^^^ ... LL | Self::A: Baz, | --- unsatisfied trait bound introduced here = note: 1 redundant requirement hidden = note: required for `(T,)` to implement `Grault` help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound | LL - Self::A: Baz, LL + , | ``` ``` error[E0275]: overflow evaluating the requirement `::Type == ::Type` --> $DIR/impl-wf-cycle-3.rs:7:1 | LL | / impl B for T LL | | where LL | | T: A, | |_____________________^ LL | { LL | type Type = bool; | --------- associated type `::Type` is specified here | note: required for `T` to implement `B` --> $DIR/impl-wf-cycle-3.rs:7:9 | LL | impl B for T | ^ ^ LL | where LL | T: A, | ------------- unsatisfied trait bound introduced here help: replace the associated type with the type specified in this `impl` | LL | T: A, | ~~~~ ``` ``` error[E0275]: overflow evaluating the requirement `::ToMatch == ::ToMatch` --> $DIR/impl-wf-cycle-4.rs:5:1 | LL | / impl Filter for T LL | | where LL | | T: Fn(Self::ToMatch), | |_________________________^ | note: required for `T` to implement `Filter` --> $DIR/impl-wf-cycle-4.rs:5:9 | LL | impl Filter for T | ^^^^^^ ^ LL | where LL | T: Fn(Self::ToMatch), | ----------------- unsatisfied trait bound introduced here note: associated types for the current `impl` cannot be restricted in `where` clauses --> $DIR/impl-wf-cycle-4.rs:7:11 | LL | T: Fn(Self::ToMatch), | ^^^^^^^^^^^^^ ``` Fix #116925 --- .../src/traits/error_reporting/suggestions.rs | 201 ++++++++++++++---- .../associated-types/impl-wf-cycle-1.stderr | 8 + .../associated-types/impl-wf-cycle-2.stderr | 8 + tests/ui/associated-types/impl-wf-cycle-3.rs | 13 ++ .../associated-types/impl-wf-cycle-3.stderr | 27 +++ tests/ui/associated-types/impl-wf-cycle-4.rs | 15 ++ .../associated-types/impl-wf-cycle-4.stderr | 25 +++ 7 files changed, 252 insertions(+), 45 deletions(-) create mode 100644 tests/ui/associated-types/impl-wf-cycle-3.rs create mode 100644 tests/ui/associated-types/impl-wf-cycle-3.stderr create mode 100644 tests/ui/associated-types/impl-wf-cycle-4.rs create mode 100644 tests/ui/associated-types/impl-wf-cycle-4.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 0e33e9cd790d0..f96392899222c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -7,7 +7,7 @@ use super::{ use crate::errors; use crate::infer::InferCtxt; -use crate::traits::{NormalizeExt, ObligationCtxt}; +use crate::traits::{ImplDerivedObligationCause, NormalizeExt, ObligationCtxt}; use hir::def::CtorOf; use rustc_data_structures::fx::FxHashSet; @@ -2973,7 +2973,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::ObjectTypeBound(..) => {} ObligationCauseCode::RustCall => { if let Some(pred) = predicate.to_opt_poly_trait_pred() - && Some(pred.def_id()) == self.tcx.lang_items().sized_trait() + && Some(pred.def_id()) == tcx.lang_items().sized_trait() { err.note("argument required to be sized due to `extern \"rust-call\"` ABI"); } @@ -3022,15 +3022,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let def_id = trait_pred.def_id(); let visible_item = if let Some(local) = def_id.as_local() { // Check for local traits being reachable. - let vis = &self.tcx.resolutions(()).effective_visibilities; + let vis = &tcx.resolutions(()).effective_visibilities; // Account for non-`pub` traits in the root of the local crate. - let is_locally_reachable = self.tcx.parent(def_id).is_crate_root(); + let is_locally_reachable = tcx.parent(def_id).is_crate_root(); vis.is_reachable(local) || is_locally_reachable } else { // Check for foreign traits being reachable. - self.tcx.visible_parent_map(()).get(&def_id).is_some() + tcx.visible_parent_map(()).get(&def_id).is_some() }; - if Some(def_id) == self.tcx.lang_items().sized_trait() + if Some(def_id) == tcx.lang_items().sized_trait() && let Some(hir::Node::TraitItem(hir::TraitItem { ident, kind: hir::TraitItemKind::Type(bounds, None), @@ -3039,7 +3039,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Do not suggest relaxing if there is an explicit `Sized` obligation. && !bounds.iter() .filter_map(|bound| bound.trait_ref()) - .any(|tr| tr.trait_def_id() == self.tcx.lang_items().sized_trait()) + .any(|tr| tr.trait_def_id() == tcx.lang_items().sized_trait()) { let (span, separator) = if let [.., last] = bounds { (last.span().shrink_to_hi(), " +") @@ -3102,10 +3102,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::Coercion { source, target } => { let mut file = None; - let source = - self.tcx.short_ty_string(self.resolve_vars_if_possible(source), &mut file); - let target = - self.tcx.short_ty_string(self.resolve_vars_if_possible(target), &mut file); + let source = tcx.short_ty_string(self.resolve_vars_if_possible(source), &mut file); + let target = tcx.short_ty_string(self.resolve_vars_if_possible(target), &mut file); err.note(with_forced_trimmed_paths!(format!( "required for the cast from `{source}` to `{target}`", ))); @@ -3154,7 +3152,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } - if self.tcx.sess.is_nightly_build() + if tcx.sess.is_nightly_build() && matches!(is_constable, IsConstable::Fn | IsConstable::Ctor) { err.help( @@ -3164,8 +3162,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } ObligationCauseCode::VariableType(hir_id) => { - let parent_node = self.tcx.hir().parent_id(hir_id); - match self.tcx.opt_hir_node(parent_node) { + let parent_node = tcx.hir().parent_id(hir_id); + match tcx.opt_hir_node(parent_node) { Some(Node::Local(hir::Local { ty: Some(ty), .. })) => { err.span_suggestion_verbose( ty.span.shrink_to_lo(), @@ -3203,7 +3201,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.note("all local variables must have a statically known size"); } } - if !self.tcx.features().unsized_locals { + if !tcx.features().unsized_locals { err.help("unsized locals are gated as an unstable feature"); } } @@ -3285,7 +3283,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.note("all function arguments must have a statically known size"); } if tcx.sess.opts.unstable_features.is_nightly_build() - && !self.tcx.features().unsized_fn_params + && !tcx.features().unsized_fn_params { err.help("unsized fn params are gated as an unstable feature"); } @@ -3354,7 +3352,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "all values captured by value by a closure must have a statically known size", ); let hir::ExprKind::Closure(closure) = - self.tcx.hir_node_by_def_id(closure_def_id).expect_expr().kind + tcx.hir_node_by_def_id(closure_def_id).expect_expr().kind else { bug!("expected closure in SizedClosureCapture obligation"); }; @@ -3365,7 +3363,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => { - let what = match self.tcx.coroutine_kind(coroutine_def_id) { + let what = match tcx.coroutine_kind(coroutine_def_id) { None | Some(hir::CoroutineKind::Coroutine(_)) | Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => { @@ -3416,10 +3414,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { 'print: { if !is_upvar_tys_infer_tuple { let mut file = None; - let ty_str = self.tcx.short_ty_string(ty, &mut file); + let ty_str = tcx.short_ty_string(ty, &mut file); let msg = format!("required because it appears within the type `{ty_str}`"); match ty.kind() { - ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) { + ty::Adt(def, _) => match tcx.opt_item_ident(def.did()) { Some(ident) => err.span_note(ident.span, msg), None => err.note(msg), }, @@ -3442,7 +3440,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { break 'print; } - err.span_note(self.tcx.def_span(def_id), msg) + err.span_note(tcx.def_span(def_id), msg) } ty::CoroutineWitness(def_id, args) => { use std::fmt::Write; @@ -3459,7 +3457,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.note(msg.trim_end_matches(", ").to_string()) } ty::Coroutine(def_id, _) => { - let sp = self.tcx.def_span(def_id); + let sp = tcx.def_span(def_id); // Special-case this to say "async block" instead of `[static coroutine]`. let kind = tcx.coroutine_kind(def_id).unwrap(); @@ -3471,7 +3469,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) } ty::Closure(def_id, _) => err.span_note( - self.tcx.def_span(def_id), + tcx.def_span(def_id), "required because it's used within this closure", ), ty::Str => err.note("`str` is considered to contain a `[u8]` slice for auto trait purposes"), @@ -3515,14 +3513,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.resolve_vars_if_possible(data.derived.parent_trait_pred); let parent_def_id = parent_trait_pred.def_id(); let mut file = None; - let self_ty = - self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); - let msg = format!( - "required for `{self_ty}` to implement `{}`", - parent_trait_pred.print_modifiers_and_trait_path() - ); + let self_ty_str = + tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); + let trait_name = parent_trait_pred.print_modifiers_and_trait_path().to_string(); + let msg = format!("required for `{self_ty_str}` to implement `{trait_name}`"); let mut is_auto_trait = false; - match self.tcx.hir().get_if_local(data.impl_or_alias_def_id) { + match tcx.hir().get_if_local(data.impl_or_alias_def_id) { Some(Node::Item(hir::Item { kind: hir::ItemKind::Trait(is_auto, ..), ident, @@ -3534,7 +3530,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.span_note(ident.span, msg); } Some(Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), + kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, generics, .. }), .. })) => { let mut spans = Vec::with_capacity(2); @@ -3561,6 +3557,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } err.span_note(spans, msg); + point_at_assoc_type_restriction( + tcx, + err, + &self_ty_str, + &trait_name, + predicate, + &generics, + &data, + ); } _ => { err.note(msg); @@ -3614,9 +3619,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { pluralize!(count) )); let mut file = None; - let self_ty = self - .tcx - .short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); + let self_ty = + tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); err.note(format!( "required for `{self_ty}` to implement `{}`", parent_trait_pred.print_modifiers_and_trait_path() @@ -3674,10 +3678,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { multispan.push_span_label(span, "required by this bound"); err.span_note( multispan, - format!( - "required by a bound on the type alias `{}`", - self.infcx.tcx.item_name(def_id) - ), + format!("required by a bound on the type alias `{}`", tcx.item_name(def_id)), ); } ObligationCauseCode::FunctionArgumentObligation { @@ -3708,25 +3709,23 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }); } ObligationCauseCode::CompareImplItemObligation { trait_item_def_id, kind, .. } => { - let item_name = self.tcx.item_name(trait_item_def_id); + let item_name = tcx.item_name(trait_item_def_id); let msg = format!( "the requirement `{predicate}` appears on the `impl`'s {kind} \ `{item_name}` but not on the corresponding trait's {kind}", ); - let sp = self - .tcx + let sp = tcx .opt_item_ident(trait_item_def_id) .map(|i| i.span) - .unwrap_or_else(|| self.tcx.def_span(trait_item_def_id)); + .unwrap_or_else(|| tcx.def_span(trait_item_def_id)); let mut assoc_span: MultiSpan = sp.into(); assoc_span.push_span_label( sp, format!("this trait's {kind} doesn't have the requirement `{predicate}`"), ); - if let Some(ident) = self - .tcx + if let Some(ident) = tcx .opt_associated_item(trait_item_def_id) - .and_then(|i| self.tcx.opt_item_ident(i.container_id(self.tcx))) + .and_then(|i| tcx.opt_item_ident(i.container_id(tcx))) { assoc_span.push_span_label(ident.span, "in this trait"); } @@ -4816,6 +4815,29 @@ fn hint_missing_borrow<'tcx>( } } +/// Collect all the paths that reference `Self`. +/// Used to suggest replacing associated types with an explicit type in `where` clauses. +#[derive(Debug)] +pub struct SelfVisitor<'v> { + pub paths: Vec<&'v hir::Ty<'v>>, + pub name: Option, +} + +impl<'v> Visitor<'v> for SelfVisitor<'v> { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + if let hir::TyKind::Path(path) = ty.kind + && let hir::QPath::TypeRelative(inner_ty, segment) = path + && (Some(segment.ident.name) == self.name || self.name.is_none()) + && let hir::TyKind::Path(inner_path) = inner_ty.kind + && let hir::QPath::Resolved(None, inner_path) = inner_path + && let Res::SelfTyAlias { .. } = inner_path.res + { + self.paths.push(ty); + } + hir::intravisit::walk_ty(self, ty); + } +} + /// Collect all the returned expressions within the input expression. /// Used to point at the return spans when we want to suggest some change to them. #[derive(Default)] @@ -5060,6 +5082,95 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( Some(sugg) } +/// On `impl` evaluation cycles, look for `Self::AssocTy` restrictions in `where` clauses, explain +/// they are not allowed and if possible suggest alternatives. +fn point_at_assoc_type_restriction( + tcx: TyCtxt<'_>, + err: &mut Diagnostic, + self_ty_str: &str, + trait_name: &str, + predicate: ty::Predicate<'_>, + generics: &hir::Generics<'_>, + data: &ImplDerivedObligationCause<'_>, +) { + let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() else { + return; + }; + let ty::ClauseKind::Projection(proj) = clause else { + return; + }; + let name = tcx.item_name(proj.projection_ty.def_id); + for pred in generics.predicates { + let hir::WherePredicate::BoundPredicate(pred) = pred else { + continue; + }; + for bound in pred.bounds { + let Some(trait_ref) = bound.trait_ref() else { + continue; + }; + if bound.span() != data.span { + continue; + } + if let hir::TyKind::Path(path) = pred.bounded_ty.kind + && let hir::QPath::TypeRelative(ty, segment) = path + && segment.ident.name == name + && let hir::TyKind::Path(inner_path) = ty.kind + && let hir::QPath::Resolved(None, inner_path) = inner_path + && let Res::SelfTyAlias { .. } = inner_path.res + { + err.span_suggestion_verbose( + pred.span, // FIXME: include the trailing comma. + "associated type for the current `impl` cannot be restricted in `where` \ + clauses, remove this bound", + "", + Applicability::MaybeIncorrect, + ); + } + if let Some(new) = + tcx.associated_items(data.impl_or_alias_def_id).find_by_name_and_kind( + tcx, + Ident::with_dummy_span(name), + ty::AssocKind::Type, + data.impl_or_alias_def_id, + ) + { + // The associated type is specified in the `impl` we're + // looking at. Point at it. + let span = tcx.def_span(new.def_id); + err.span_label( + span, + format!( + "associated type `<{self_ty_str} as {trait_name}>::{name}` is specified \ + here", + ), + ); + // Search for the associated type `Self::{name}`, get + // its type and suggest replacing the bound with it. + let mut visitor = SelfVisitor { paths: vec![], name: Some(name) }; + visitor.visit_trait_ref(trait_ref); + for path in visitor.paths { + err.span_suggestion_verbose( + path.span, + "replace the associated type with the type specified in this `impl`", + format!("{}", tcx.type_of(new.def_id).skip_binder(),), + Applicability::MachineApplicable, + ); + } + } else { + let mut visitor = SelfVisitor { paths: vec![], name: None }; + visitor.visit_trait_ref(trait_ref); + let span: MultiSpan = + visitor.paths.iter().map(|p| p.span).collect::>().into(); + err.span_note( + span, + "associated types for the current `impl` cannot be restricted in `where` \ + clauses", + ); + } + } + } +} + fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec) { let mut refs = vec![]; diff --git a/tests/ui/associated-types/impl-wf-cycle-1.stderr b/tests/ui/associated-types/impl-wf-cycle-1.stderr index 5fa36733f6677..b4fc73fc8cfd9 100644 --- a/tests/ui/associated-types/impl-wf-cycle-1.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-1.stderr @@ -7,6 +7,9 @@ LL | | where LL | | Self::A: Baz, LL | | Self::B: Fiz, | |_________________^ +LL | { +LL | type A = (); + | ------ associated type `<(T,) as Grault>::A` is specified here | note: required for `(T,)` to implement `Grault` --> $DIR/impl-wf-cycle-1.rs:15:17 @@ -18,6 +21,11 @@ LL | Self::A: Baz, | --- unsatisfied trait bound introduced here = note: 1 redundant requirement hidden = note: required for `(T,)` to implement `Grault` +help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound + | +LL - Self::A: Baz, +LL + , + | error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/impl-wf-cycle-2.stderr b/tests/ui/associated-types/impl-wf-cycle-2.stderr index 17a96bbd93430..b5ece55c1c9ad 100644 --- a/tests/ui/associated-types/impl-wf-cycle-2.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-2.stderr @@ -6,6 +6,9 @@ LL | | LL | | where LL | | Self::A: Copy, | |__________________^ +LL | { +LL | type A = (); + | ------ associated type `<(T,) as Grault>::A` is specified here | note: required for `(T,)` to implement `Grault` --> $DIR/impl-wf-cycle-2.rs:7:17 @@ -15,6 +18,11 @@ LL | impl Grault for (T,) ... LL | Self::A: Copy, | ---- unsatisfied trait bound introduced here +help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound + | +LL - Self::A: Copy, +LL + , + | error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/impl-wf-cycle-3.rs b/tests/ui/associated-types/impl-wf-cycle-3.rs new file mode 100644 index 0000000000000..044033c5e8ab1 --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-3.rs @@ -0,0 +1,13 @@ +trait A {} + +trait B { + type Type; +} + +impl B for T //~ ERROR overflow evaluating the requirement +where + T: A, +{ + type Type = bool; +} +fn main() {} diff --git a/tests/ui/associated-types/impl-wf-cycle-3.stderr b/tests/ui/associated-types/impl-wf-cycle-3.stderr new file mode 100644 index 0000000000000..d3ca06f890b34 --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-3.stderr @@ -0,0 +1,27 @@ +error[E0275]: overflow evaluating the requirement `::Type == ::Type` + --> $DIR/impl-wf-cycle-3.rs:7:1 + | +LL | / impl B for T +LL | | where +LL | | T: A, + | |_____________________^ +LL | { +LL | type Type = bool; + | --------- associated type `::Type` is specified here + | +note: required for `T` to implement `B` + --> $DIR/impl-wf-cycle-3.rs:7:9 + | +LL | impl B for T + | ^ ^ +LL | where +LL | T: A, + | ------------- unsatisfied trait bound introduced here +help: replace the associated type with the type specified in this `impl` + | +LL | T: A, + | ~~~~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/associated-types/impl-wf-cycle-4.rs b/tests/ui/associated-types/impl-wf-cycle-4.rs new file mode 100644 index 0000000000000..bfa8adc71a11d --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-4.rs @@ -0,0 +1,15 @@ +trait Filter { + type ToMatch; +} + +impl Filter for T //~ ERROR overflow evaluating the requirement +where + T: Fn(Self::ToMatch), +{ +} + +struct JustFilter { + filter: F, +} + +fn main() {} diff --git a/tests/ui/associated-types/impl-wf-cycle-4.stderr b/tests/ui/associated-types/impl-wf-cycle-4.stderr new file mode 100644 index 0000000000000..cdbac267d34dc --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-4.stderr @@ -0,0 +1,25 @@ +error[E0275]: overflow evaluating the requirement `::ToMatch == ::ToMatch` + --> $DIR/impl-wf-cycle-4.rs:5:1 + | +LL | / impl Filter for T +LL | | where +LL | | T: Fn(Self::ToMatch), + | |_________________________^ + | +note: required for `T` to implement `Filter` + --> $DIR/impl-wf-cycle-4.rs:5:9 + | +LL | impl Filter for T + | ^^^^^^ ^ +LL | where +LL | T: Fn(Self::ToMatch), + | ----------------- unsatisfied trait bound introduced here +note: associated types for the current `impl` cannot be restricted in `where` clauses + --> $DIR/impl-wf-cycle-4.rs:7:11 + | +LL | T: Fn(Self::ToMatch), + | ^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. From c85bb274f6376c0e8b1289d6cb8f187b011b8fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 28 Dec 2023 22:26:37 +0000 Subject: [PATCH 05/18] Account for trailing comma in removal suggestion --- .../src/traits/error_reporting/suggestions.rs | 29 +++++++++++++++-- .../associated-types/impl-wf-cycle-1.stderr | 1 - .../associated-types/impl-wf-cycle-2.stderr | 2 +- .../ui/associated-types/impl-wf-cycle-5.fixed | 31 ++++++++++++++++++ tests/ui/associated-types/impl-wf-cycle-5.rs | 32 +++++++++++++++++++ .../associated-types/impl-wf-cycle-5.stderr | 31 ++++++++++++++++++ .../ui/associated-types/impl-wf-cycle-6.fixed | 31 ++++++++++++++++++ tests/ui/associated-types/impl-wf-cycle-6.rs | 31 ++++++++++++++++++ .../associated-types/impl-wf-cycle-6.stderr | 29 +++++++++++++++++ 9 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 tests/ui/associated-types/impl-wf-cycle-5.fixed create mode 100644 tests/ui/associated-types/impl-wf-cycle-5.rs create mode 100644 tests/ui/associated-types/impl-wf-cycle-5.stderr create mode 100644 tests/ui/associated-types/impl-wf-cycle-6.fixed create mode 100644 tests/ui/associated-types/impl-wf-cycle-6.rs create mode 100644 tests/ui/associated-types/impl-wf-cycle-6.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index f96392899222c..acfe886db9c4a 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -5100,11 +5100,14 @@ fn point_at_assoc_type_restriction( return; }; let name = tcx.item_name(proj.projection_ty.def_id); - for pred in generics.predicates { + let mut predicates = generics.predicates.iter().peekable(); + let mut prev: Option<&hir::WhereBoundPredicate<'_>> = None; + while let Some(pred) = predicates.next() { let hir::WherePredicate::BoundPredicate(pred) = pred else { continue; }; - for bound in pred.bounds { + let mut bounds = pred.bounds.iter().peekable(); + while let Some(bound) = bounds.next() { let Some(trait_ref) = bound.trait_ref() else { continue; }; @@ -5118,8 +5121,27 @@ fn point_at_assoc_type_restriction( && let hir::QPath::Resolved(None, inner_path) = inner_path && let Res::SelfTyAlias { .. } = inner_path.res { + // The following block is to determine the right span to delete for this bound + // that will leave valid code after the suggestion is applied. + let span = if let Some(hir::WherePredicate::BoundPredicate(next)) = + predicates.peek() + && pred.origin == next.origin + { + // There's another bound, include the comma for the current one. + pred.span.until(next.span) + } else if let Some(prev) = prev + && pred.origin == prev.origin + { + // Last bound, try to remove the previous comma. + prev.span.shrink_to_hi().to(pred.span) + } else if pred.origin == hir::PredicateOrigin::WhereClause { + pred.span.with_hi(generics.where_clause_span.hi()) + } else { + pred.span + }; + err.span_suggestion_verbose( - pred.span, // FIXME: include the trailing comma. + span, "associated type for the current `impl` cannot be restricted in `where` \ clauses, remove this bound", "", @@ -5168,6 +5190,7 @@ fn point_at_assoc_type_restriction( ); } } + prev = Some(pred); } } diff --git a/tests/ui/associated-types/impl-wf-cycle-1.stderr b/tests/ui/associated-types/impl-wf-cycle-1.stderr index b4fc73fc8cfd9..d642f9f0e36a7 100644 --- a/tests/ui/associated-types/impl-wf-cycle-1.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-1.stderr @@ -24,7 +24,6 @@ LL | Self::A: Baz, help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound | LL - Self::A: Baz, -LL + , | error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/impl-wf-cycle-2.stderr b/tests/ui/associated-types/impl-wf-cycle-2.stderr index b5ece55c1c9ad..15d3f7579ea5a 100644 --- a/tests/ui/associated-types/impl-wf-cycle-2.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-2.stderr @@ -21,7 +21,7 @@ LL | Self::A: Copy, help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound | LL - Self::A: Copy, -LL + , +LL + | error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/impl-wf-cycle-5.fixed b/tests/ui/associated-types/impl-wf-cycle-5.fixed new file mode 100644 index 0000000000000..bff6ca90975d2 --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-5.fixed @@ -0,0 +1,31 @@ +// run-rustfix + +trait Baz {} +impl Baz for () {} +impl Baz for (T,) {} + +trait Fiz {} +impl Fiz for bool {} + +trait Grault { + type A; + type B; +} + +impl Grault for () { + type A = (); + type B = bool; +} + +impl Grault for (T,) +//~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` +where + T: Grault, +{ + type A = (); + type B = bool; +} + +fn main() { + let _: <((),) as Grault>::A = (); +} diff --git a/tests/ui/associated-types/impl-wf-cycle-5.rs b/tests/ui/associated-types/impl-wf-cycle-5.rs new file mode 100644 index 0000000000000..a822e1fb0081b --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-5.rs @@ -0,0 +1,32 @@ +// run-rustfix + +trait Baz {} +impl Baz for () {} +impl Baz for (T,) {} + +trait Fiz {} +impl Fiz for bool {} + +trait Grault { + type A; + type B; +} + +impl Grault for () { + type A = (); + type B = bool; +} + +impl Grault for (T,) +//~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` +where + T: Grault, + Self::A: Baz, +{ + type A = (); + type B = bool; +} + +fn main() { + let _: <((),) as Grault>::A = (); +} diff --git a/tests/ui/associated-types/impl-wf-cycle-5.stderr b/tests/ui/associated-types/impl-wf-cycle-5.stderr new file mode 100644 index 0000000000000..284a50bb9a3be --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-5.stderr @@ -0,0 +1,31 @@ +error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` + --> $DIR/impl-wf-cycle-5.rs:20:1 + | +LL | / impl Grault for (T,) +LL | | +LL | | where +LL | | T: Grault, +LL | | Self::A: Baz, + | |_________________^ +LL | { +LL | type A = (); + | ------ associated type `<(T,) as Grault>::A` is specified here + | +note: required for `(T,)` to implement `Grault` + --> $DIR/impl-wf-cycle-5.rs:20:9 + | +LL | impl Grault for (T,) + | ^^^^^^ ^^^^ +... +LL | Self::A: Baz, + | --- unsatisfied trait bound introduced here +help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound + | +LL - T: Grault, +LL - Self::A: Baz, +LL + T: Grault, + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/associated-types/impl-wf-cycle-6.fixed b/tests/ui/associated-types/impl-wf-cycle-6.fixed new file mode 100644 index 0000000000000..f98ae8523efcc --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-6.fixed @@ -0,0 +1,31 @@ +// run-rustfix + +trait Baz {} +impl Baz for () {} +impl Baz for (T,) {} + +trait Fiz {} +impl Fiz for bool {} + +trait Grault { + type A; + type B; +} + +impl Grault for () { + type A = (); + type B = bool; +} + +impl Grault for (T,) +//~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` +where + +{ + type A = (); + type B = bool; +} + +fn main() { + let _: <((),) as Grault>::A = (); +} diff --git a/tests/ui/associated-types/impl-wf-cycle-6.rs b/tests/ui/associated-types/impl-wf-cycle-6.rs new file mode 100644 index 0000000000000..20d635cac5d6f --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-6.rs @@ -0,0 +1,31 @@ +// run-rustfix + +trait Baz {} +impl Baz for () {} +impl Baz for (T,) {} + +trait Fiz {} +impl Fiz for bool {} + +trait Grault { + type A; + type B; +} + +impl Grault for () { + type A = (); + type B = bool; +} + +impl Grault for (T,) +//~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` +where + Self::A: Baz, +{ + type A = (); + type B = bool; +} + +fn main() { + let _: <((),) as Grault>::A = (); +} diff --git a/tests/ui/associated-types/impl-wf-cycle-6.stderr b/tests/ui/associated-types/impl-wf-cycle-6.stderr new file mode 100644 index 0000000000000..074671585ffec --- /dev/null +++ b/tests/ui/associated-types/impl-wf-cycle-6.stderr @@ -0,0 +1,29 @@ +error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` + --> $DIR/impl-wf-cycle-6.rs:20:1 + | +LL | / impl Grault for (T,) +LL | | +LL | | where +LL | | Self::A: Baz, + | |_________________^ +LL | { +LL | type A = (); + | ------ associated type `<(T,) as Grault>::A` is specified here + | +note: required for `(T,)` to implement `Grault` + --> $DIR/impl-wf-cycle-6.rs:20:17 + | +LL | impl Grault for (T,) + | ^^^^^^ ^^^^ +... +LL | Self::A: Baz, + | --- unsatisfied trait bound introduced here +help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound + | +LL - Self::A: Baz, +LL + + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. From 29bdf9ea512e8bb74455410416473644b708f45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 4 Jan 2024 00:39:56 +0000 Subject: [PATCH 06/18] Account for single `where` bound being removed --- .../src/traits/error_reporting/suggestions.rs | 22 ++++++++++++++++--- .../associated-types/impl-wf-cycle-2.stderr | 2 +- .../ui/associated-types/impl-wf-cycle-6.fixed | 3 +-- .../associated-types/impl-wf-cycle-6.stderr | 2 +- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index acfe886db9c4a..959aa1359ca03 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -5123,8 +5123,24 @@ fn point_at_assoc_type_restriction( { // The following block is to determine the right span to delete for this bound // that will leave valid code after the suggestion is applied. - let span = if let Some(hir::WherePredicate::BoundPredicate(next)) = - predicates.peek() + let span = if pred.origin == hir::PredicateOrigin::WhereClause + && generics + .predicates + .iter() + .filter(|p| { + matches!( + p, + hir::WherePredicate::BoundPredicate(p) + if hir::PredicateOrigin::WhereClause == p.origin + ) + }) + .count() + == 1 + { + // There's only one `where` bound, that needs to be removed. Remove the whole + // `where` clause. + generics.where_clause_span + } else if let Some(hir::WherePredicate::BoundPredicate(next)) = predicates.peek() && pred.origin == next.origin { // There's another bound, include the comma for the current one. @@ -5174,7 +5190,7 @@ fn point_at_assoc_type_restriction( err.span_suggestion_verbose( path.span, "replace the associated type with the type specified in this `impl`", - format!("{}", tcx.type_of(new.def_id).skip_binder(),), + tcx.type_of(new.def_id).skip_binder().to_string(), Applicability::MachineApplicable, ); } diff --git a/tests/ui/associated-types/impl-wf-cycle-2.stderr b/tests/ui/associated-types/impl-wf-cycle-2.stderr index 15d3f7579ea5a..9454d1d669792 100644 --- a/tests/ui/associated-types/impl-wf-cycle-2.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-2.stderr @@ -20,8 +20,8 @@ LL | Self::A: Copy, | ---- unsatisfied trait bound introduced here help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound | +LL - where LL - Self::A: Copy, -LL + | error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/impl-wf-cycle-6.fixed b/tests/ui/associated-types/impl-wf-cycle-6.fixed index f98ae8523efcc..73ed64f7ce381 100644 --- a/tests/ui/associated-types/impl-wf-cycle-6.fixed +++ b/tests/ui/associated-types/impl-wf-cycle-6.fixed @@ -19,8 +19,7 @@ impl Grault for () { impl Grault for (T,) //~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` -where - + { type A = (); type B = bool; diff --git a/tests/ui/associated-types/impl-wf-cycle-6.stderr b/tests/ui/associated-types/impl-wf-cycle-6.stderr index 074671585ffec..c9b5d8060be68 100644 --- a/tests/ui/associated-types/impl-wf-cycle-6.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-6.stderr @@ -20,8 +20,8 @@ LL | Self::A: Baz, | --- unsatisfied trait bound introduced here help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound | +LL - where LL - Self::A: Baz, -LL + | error: aborting due to 1 previous error From db7cd5709133ef4a0e45bc13b1b3db7cbf1b3c8f Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 23 Jan 2024 15:23:22 +0000 Subject: [PATCH 07/18] Remove track_errors entirely --- .../src/coherence/builtin.rs | 150 ++++++---- .../rustc_hir_analysis/src/coherence/mod.rs | 55 ++-- .../src/coherence/unsafety.rs | 16 +- compiler/rustc_hir_analysis/src/lib.rs | 16 +- .../src/rmeta/decoder/cstore_impl.rs | 9 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_middle/src/query/erase.rs | 5 + compiler/rustc_middle/src/query/mod.rs | 8 +- compiler/rustc_middle/src/ty/util.rs | 2 +- compiler/rustc_monomorphize/src/collector.rs | 8 +- compiler/rustc_monomorphize/src/lib.rs | 5 +- compiler/rustc_session/src/session.rs | 14 - tests/ui/associated-consts/issue-105330.rs | 1 - .../ui/associated-consts/issue-105330.stderr | 12 +- tests/ui/associated-types/issue-38821.rs | 15 + tests/ui/associated-types/issue-38821.stderr | 268 +++++++++++++++++- tests/ui/coercion/issue-53475.rs | 2 +- .../bad-generic-in-copy-impl.rs | 1 + .../bad-generic-in-copy-impl.stderr | 10 +- tests/ui/enum-discriminant/issue-72554.rs | 1 + tests/ui/enum-discriminant/issue-72554.stderr | 21 +- .../specializing-constness-2.stderr | 14 +- .../bad-const-wf-doesnt-specialize.rs | 1 - .../bad-const-wf-doesnt-specialize.stderr | 22 +- .../ui/typeck/typeck_type_placeholder_item.rs | 1 - .../typeck_type_placeholder_item.stderr | 37 +-- 26 files changed, 496 insertions(+), 200 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 8d362f74b0a26..5a3878445937b 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -25,14 +25,21 @@ use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCause}; use std::collections::BTreeMap; -pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) { +pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> { let lang_items = tcx.lang_items(); - Checker { tcx, trait_def_id } - .check(lang_items.drop_trait(), visit_implementation_of_drop) - .check(lang_items.copy_trait(), visit_implementation_of_copy) - .check(lang_items.const_param_ty_trait(), visit_implementation_of_const_param_ty) - .check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized) - .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn); + let checker = Checker { tcx, trait_def_id }; + let mut res = checker.check(lang_items.drop_trait(), visit_implementation_of_drop); + res = res.and(checker.check(lang_items.copy_trait(), visit_implementation_of_copy)); + res = res.and( + checker.check(lang_items.const_param_ty_trait(), visit_implementation_of_const_param_ty), + ); + res = res.and( + checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized), + ); + res.and( + checker + .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn), + ) } struct Checker<'tcx> { @@ -41,33 +48,40 @@ struct Checker<'tcx> { } impl<'tcx> Checker<'tcx> { - fn check(&self, trait_def_id: Option, mut f: F) -> &Self + fn check(&self, trait_def_id: Option, mut f: F) -> Result<(), ErrorGuaranteed> where - F: FnMut(TyCtxt<'tcx>, LocalDefId), + F: FnMut(TyCtxt<'tcx>, LocalDefId) -> Result<(), ErrorGuaranteed>, { + let mut res = Ok(()); if Some(self.trait_def_id) == trait_def_id { for &impl_def_id in self.tcx.hir().trait_impls(self.trait_def_id) { - f(self.tcx, impl_def_id); + res = res.and(f(self.tcx, impl_def_id)); } } - self + res } } -fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { +fn visit_implementation_of_drop( + tcx: TyCtxt<'_>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { // Destructors only work on local ADT types. match tcx.type_of(impl_did).instantiate_identity().kind() { - ty::Adt(def, _) if def.did().is_local() => return, - ty::Error(_) => return, + ty::Adt(def, _) if def.did().is_local() => return Ok(()), + ty::Error(_) => return Ok(()), _ => {} } let impl_ = tcx.hir().expect_item(impl_did).expect_impl(); - tcx.dcx().emit_err(errors::DropImplOnWrongItem { span: impl_.self_ty.span }); + Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem { span: impl_.self_ty.span })) } -fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { +fn visit_implementation_of_copy( + tcx: TyCtxt<'_>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { debug!("visit_implementation_of_copy: impl_did={:?}", impl_did); let self_type = tcx.type_of(impl_did).instantiate_identity(); @@ -79,59 +93,68 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type); let span = match tcx.hir().expect_item(impl_did).expect_impl() { - hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return, + hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return Ok(()), hir::Impl { self_ty, .. } => self_ty.span, }; let cause = traits::ObligationCause::misc(span, impl_did); match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) { - Ok(()) => {} + Ok(()) => Ok(()), Err(CopyImplementationError::InfringingFields(fields)) => { - infringing_fields_error(tcx, fields, LangItem::Copy, impl_did, span); + Err(infringing_fields_error(tcx, fields, LangItem::Copy, impl_did, span)) } Err(CopyImplementationError::NotAnAdt) => { - tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span }); + Err(tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span })) } Err(CopyImplementationError::HasDestructor) => { - tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span }); + Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span })) } } } -fn visit_implementation_of_const_param_ty(tcx: TyCtxt<'_>, impl_did: LocalDefId) { +fn visit_implementation_of_const_param_ty( + tcx: TyCtxt<'_>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { let self_type = tcx.type_of(impl_did).instantiate_identity(); assert!(!self_type.has_escaping_bound_vars()); let param_env = tcx.param_env(impl_did); let span = match tcx.hir().expect_item(impl_did).expect_impl() { - hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return, + hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return Ok(()), impl_ => impl_.self_ty.span, }; let cause = traits::ObligationCause::misc(span, impl_did); match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { - Ok(()) => {} + Ok(()) => Ok(()), Err(ConstParamTyImplementationError::InfrigingFields(fields)) => { - infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span); + Err(infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span)) } Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => { - tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span }); + Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span })) } } } -fn visit_implementation_of_coerce_unsized(tcx: TyCtxt<'_>, impl_did: LocalDefId) { +fn visit_implementation_of_coerce_unsized( + tcx: TyCtxt<'_>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did); // Just compute this for the side-effects, in particular reporting // errors; other parts of the code may demand it for the info of // course. let span = tcx.def_span(impl_did); - tcx.at(span).coerce_unsized_info(impl_did); + tcx.at(span).ensure().coerce_unsized_info(impl_did) } -fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDefId) { +fn visit_implementation_of_dispatch_from_dyn( + tcx: TyCtxt<'_>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did); let span = tcx.def_span(impl_did); @@ -166,26 +189,28 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef match (source.kind(), target.kind()) { (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) if infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, r_a, *r_b).is_ok() - && mutbl_a == *mutbl_b => {} - (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (), + && mutbl_a == *mutbl_b => + { + Ok(()) + } + (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => Ok(()), (&Adt(def_a, args_a), &Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => { if def_a != def_b { let source_path = tcx.def_path_str(def_a.did()); let target_path = tcx.def_path_str(def_b.did()); - tcx.dcx().emit_err(errors::DispatchFromDynCoercion { + return Err(tcx.dcx().emit_err(errors::DispatchFromDynCoercion { span, trait_name: "DispatchFromDyn", note: true, source_path, target_path, - }); - - return; + })); } + let mut res = Ok(()); if def_a.repr().c() || def_a.repr().packed() { - tcx.dcx().emit_err(errors::DispatchFromDynRepr { span }); + res = Err(tcx.dcx().emit_err(errors::DispatchFromDynRepr { span })); } let fields = &def_a.non_enum_variant().fields; @@ -207,11 +232,11 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, ty_a, ty_b) { if ok.obligations.is_empty() { - tcx.dcx().emit_err(errors::DispatchFromDynZST { + res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST { span, name: field.name, ty: ty_a, - }); + })); return false; } @@ -222,13 +247,13 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef .collect::>(); if coerced_fields.is_empty() { - tcx.dcx().emit_err(errors::DispatchFromDynSingle { + res = Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle { span, trait_name: "DispatchFromDyn", note: true, - }); + })); } else if coerced_fields.len() > 1 { - tcx.dcx().emit_err(errors::DispatchFromDynMulti { + res = Err(tcx.dcx().emit_err(errors::DispatchFromDynMulti { span, coercions_note: true, number: coerced_fields.len(), @@ -244,7 +269,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef }) .collect::>() .join(", "), - }); + })); } else { let ocx = ObligationCtxt::new(&infcx); for field in coerced_fields { @@ -261,21 +286,25 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef } let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(errors); + res = Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } // Finally, resolve all regions. let outlives_env = OutlivesEnvironment::new(param_env); - let _ = ocx.resolve_regions_and_report_errors(impl_did, &outlives_env); + res = res.and(ocx.resolve_regions_and_report_errors(impl_did, &outlives_env)); } + res } - _ => { - tcx.dcx().emit_err(errors::CoerceUnsizedMay { span, trait_name: "DispatchFromDyn" }); - } + _ => Err(tcx + .dcx() + .emit_err(errors::CoerceUnsizedMay { span, trait_name: "DispatchFromDyn" })), } } -pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> CoerceUnsizedInfo { +pub fn coerce_unsized_info<'tcx>( + tcx: TyCtxt<'tcx>, + impl_did: LocalDefId, +) -> Result { debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did); let span = tcx.def_span(impl_did); @@ -292,8 +321,6 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe let param_env = tcx.param_env(impl_did); assert!(!source.has_escaping_bound_vars()); - let err_info = CoerceUnsizedInfo { custom_kind: None }; - debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target); let infcx = tcx.infer_ctxt().build(); @@ -337,14 +364,13 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe if def_a != def_b { let source_path = tcx.def_path_str(def_a.did()); let target_path = tcx.def_path_str(def_b.did()); - tcx.dcx().emit_err(errors::DispatchFromDynSame { + return Err(tcx.dcx().emit_err(errors::DispatchFromDynSame { span, trait_name: "CoerceUnsized", note: true, source_path, target_path, - }); - return err_info; + })); } // Here we are considering a case of converting @@ -419,12 +445,11 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe .collect::>(); if diff_fields.is_empty() { - tcx.dcx().emit_err(errors::CoerceUnsizedOneField { + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedOneField { span, trait_name: "CoerceUnsized", note: true, - }); - return err_info; + })); } else if diff_fields.len() > 1 { let item = tcx.hir().expect_item(impl_did); let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind { @@ -433,7 +458,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe tcx.def_span(impl_did) }; - tcx.dcx().emit_err(errors::CoerceUnsizedMulti { + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedMulti { span, coercions_note: true, number: diff_fields.len(), @@ -442,9 +467,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe .map(|&(i, a, b)| format!("`{}` (`{}` to `{}`)", fields[i].name, a, b)) .collect::>() .join(", "), - }); - - return err_info; + })); } let (i, a, b) = diff_fields[0]; @@ -453,8 +476,9 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe } _ => { - tcx.dcx().emit_err(errors::DispatchFromDynStruct { span, trait_name: "CoerceUnsized" }); - return err_info; + return Err(tcx + .dcx() + .emit_err(errors::DispatchFromDynStruct { span, trait_name: "CoerceUnsized" })); } }; @@ -477,7 +501,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe let outlives_env = OutlivesEnvironment::new(param_env); let _ = ocx.resolve_regions_and_report_errors(impl_did, &outlives_env); - CoerceUnsizedInfo { custom_kind: kind } + Ok(CoerceUnsizedInfo { custom_kind: kind }) } fn infringing_fields_error( diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 561a254e89ef7..dafa899ef248f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -10,6 +10,7 @@ use rustc_errors::{error_code, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; +use rustc_span::ErrorGuaranteed; use rustc_trait_selection::traits; mod builtin; @@ -18,7 +19,11 @@ mod inherent_impls_overlap; mod orphan; mod unsafety; -fn check_impl(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef<'_>) { +fn check_impl( + tcx: TyCtxt<'_>, + impl_def_id: LocalDefId, + trait_ref: ty::TraitRef<'_>, +) -> Result<(), ErrorGuaranteed> { debug!( "(checking implementation) adding impl for trait '{:?}', item '{}'", trait_ref, @@ -28,18 +33,18 @@ fn check_impl(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef< // Skip impls where one of the self type is an error type. // This occurs with e.g., resolve failures (#30589). if trait_ref.references_error() { - return; + return Ok(()); } - enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id); - enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id); + enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id) + .and(enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id)) } fn enforce_trait_manually_implementable( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_def_id: DefId, -) { +) -> Result<(), ErrorGuaranteed> { let impl_header_span = tcx.def_span(impl_def_id); // Disallow *all* explicit impls of traits marked `#[rustc_deny_explicit_impl]` @@ -59,18 +64,17 @@ fn enforce_trait_manually_implementable( err.code(error_code!(E0328)); } - err.emit(); - return; + return Err(err.emit()); } if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = tcx.trait_def(trait_def_id).specialization_kind { if !tcx.features().specialization && !tcx.features().min_specialization { - tcx.dcx().emit_err(errors::SpecializationTrait { span: impl_header_span }); - return; + return Err(tcx.dcx().emit_err(errors::SpecializationTrait { span: impl_header_span })); } } + Ok(()) } /// We allow impls of marker traits to overlap, so they can't override impls @@ -79,22 +83,22 @@ fn enforce_empty_impls_for_marker_traits( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_def_id: DefId, -) { +) -> Result<(), ErrorGuaranteed> { if !tcx.trait_def(trait_def_id).is_marker { - return; + return Ok(()); } if tcx.associated_item_def_ids(trait_def_id).is_empty() { - return; + return Ok(()); } - struct_span_code_err!( + Err(struct_span_code_err!( tcx.dcx(), tcx.def_span(impl_def_id), E0715, "impls for marker traits cannot contain items" ) - .emit(); + .emit()) } pub fn provide(providers: &mut Providers) { @@ -115,23 +119,23 @@ pub fn provide(providers: &mut Providers) { }; } -fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) { +fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed> { // Trigger building the specialization graph for the trait. This will detect and report any // overlap errors. - tcx.ensure().specialization_graph_of(def_id); + let mut res = tcx.ensure().specialization_graph_of(def_id); let impls = tcx.hir().trait_impls(def_id); for &impl_def_id in impls { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); - check_impl(tcx, impl_def_id, trait_ref); - check_object_overlap(tcx, impl_def_id, trait_ref); + res = res.and(check_impl(tcx, impl_def_id, trait_ref)); + res = res.and(check_object_overlap(tcx, impl_def_id, trait_ref)); - unsafety::check_item(tcx, impl_def_id); - tcx.ensure().orphan_check_impl(impl_def_id); + res = res.and(unsafety::check_item(tcx, impl_def_id)); + res = res.and(tcx.ensure().orphan_check_impl(impl_def_id)); } - builtin::check_trait(tcx, def_id); + res.and(builtin::check_trait(tcx, def_id)) } /// Checks whether an impl overlaps with the automatic `impl Trait for dyn Trait`. @@ -139,12 +143,12 @@ fn check_object_overlap<'tcx>( tcx: TyCtxt<'tcx>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef<'tcx>, -) { +) -> Result<(), ErrorGuaranteed> { let trait_def_id = trait_ref.def_id; if trait_ref.references_error() { debug!("coherence: skipping impl {:?} with error {:?}", impl_def_id, trait_ref); - return; + return Ok(()); } // check for overlap with the automatic `impl Trait for dyn Trait` @@ -173,7 +177,7 @@ fn check_object_overlap<'tcx>( let mut supertrait_def_ids = traits::supertrait_def_ids(tcx, component_def_id); if supertrait_def_ids.any(|d| d == trait_def_id) { let span = tcx.def_span(impl_def_id); - struct_span_code_err!( + return Err(struct_span_code_err!( tcx.dcx(), span, E0371, @@ -189,9 +193,10 @@ fn check_object_overlap<'tcx>( tcx.def_path_str(trait_def_id) ), ) - .emit(); + .emit()); } } } } + Ok(()) } diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs index 7b146573a1b3b..e4c407af53ff0 100644 --- a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs +++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs @@ -6,8 +6,9 @@ use rustc_hir as hir; use rustc_hir::Unsafety; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; +use rustc_span::ErrorGuaranteed; -pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { +pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> { let item = tcx.hir().expect_item(def_id); let impl_ = item.expect_impl(); @@ -18,7 +19,7 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) { (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { - struct_span_code_err!( + return Err(struct_span_code_err!( tcx.dcx(), tcx.def_span(def_id), E0199, @@ -31,11 +32,11 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "", rustc_errors::Applicability::MachineApplicable, ) - .emit(); + .emit()); } (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => { - struct_span_code_err!( + return Err(struct_span_code_err!( tcx.dcx(), tcx.def_span(def_id), E0200, @@ -54,11 +55,11 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "unsafe ", rustc_errors::Applicability::MaybeIncorrect, ) - .emit(); + .emit()); } (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => { - struct_span_code_err!( + return Err(struct_span_code_err!( tcx.dcx(), tcx.def_span(def_id), E0569, @@ -77,7 +78,7 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "unsafe ", rustc_errors::Applicability::MaybeIncorrect, ) - .emit(); + .emit()); } (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { @@ -92,4 +93,5 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { } } } + Ok(()) } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 0a3a71ba1a28d..ad5b666467748 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -172,19 +172,15 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { tcx.sess.time("coherence_checking", || { // Check impls constrain their parameters - let res = + let mut res = tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_impl_wf(module)); - // FIXME(matthewjasper) We shouldn't need to use `track_errors` anywhere in this function - // or the compiler in general. - res.and(tcx.sess.track_errors(|| { - for &trait_def_id in tcx.all_local_trait_impls(()).keys() { - tcx.ensure().coherent_trait(trait_def_id); - } - })) + for &trait_def_id in tcx.all_local_trait_impls(()).keys() { + res = res.and(tcx.ensure().coherent_trait(trait_def_id)); + } // these queries are executed for side-effects (error reporting): - .and(tcx.ensure().crate_inherent_impls(())) - .and(tcx.ensure().crate_inherent_impls_overlap_check(())) + res.and(tcx.ensure().crate_inherent_impls(())) + .and(tcx.ensure().crate_inherent_impls_overlap_check(())) })?; if tcx.features().rustc_attrs { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index c3d6c21c4027d..0b352a02b64c7 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -236,7 +236,14 @@ provide! { tcx, def_id, other, cdata, impl_polarity => { table_direct } defaultness => { table_direct } constness => { table_direct } - coerce_unsized_info => { table } + coerce_unsized_info => { + Ok(cdata + .root + .tables + .coerce_unsized_info + .get(cdata, def_id.index) + .map(|lazy| lazy.decode((cdata, tcx))) + .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) } mir_const_qualif => { table } rendered_const => { table } asyncness => { table_direct } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 2d4e49e27d9b9..542caf8622385 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1994,7 +1994,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // if this is an impl of `CoerceUnsized`, create its // "unsized info", else just store None if Some(trait_ref.def_id) == tcx.lang_items().coerce_unsized_trait() { - let coerce_unsized_info = tcx.coerce_unsized_info(def_id); + let coerce_unsized_info = tcx.coerce_unsized_info(def_id).unwrap(); record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info); } } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 778b4308d2ee8..1b01df6a18714 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -1,6 +1,7 @@ use crate::mir; use crate::query::CyclePlaceholder; use crate::traits; +use crate::ty::adjustment::CoerceUnsizedInfo; use crate::ty::{self, Ty}; use std::intrinsics::transmute_unchecked; use std::mem::{size_of, MaybeUninit}; @@ -105,6 +106,10 @@ impl EraseType for Result>, rustc_errors::ErrorGuarantee [u8; size_of::>, rustc_errors::ErrorGuaranteed>>()]; } +impl EraseType for Result { + type Result = [u8; size_of::>()]; +} + impl EraseType for Result>>, rustc_errors::ErrorGuaranteed> { type Result = [u8; size_of::< Result>>, rustc_errors::ErrorGuaranteed>, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a9dc7f5d11a20..335c12e99b374 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -968,10 +968,11 @@ rustc_queries! { } /// Caches `CoerceUnsized` kinds for impls on custom types. - query coerce_unsized_info(key: DefId) -> ty::adjustment::CoerceUnsizedInfo { + query coerce_unsized_info(key: DefId) -> Result { desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern + ensure_forwards_result_if_red } query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { @@ -991,8 +992,9 @@ rustc_queries! { desc { |tcx| "checking whether `{}` has a body", tcx.def_path_str(def_id) } } - query coherent_trait(def_id: DefId) -> () { + query coherent_trait(def_id: DefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } + ensure_forwards_result_if_red } /// Borrow-checks the function body. If this is a closure, returns @@ -1023,6 +1025,7 @@ rustc_queries! { "checking whether impl `{}` follows the orphan rules", tcx.def_path_str(key), } + ensure_forwards_result_if_red } /// Check whether the function has any recursion that could cause the inliner to trigger @@ -1291,6 +1294,7 @@ rustc_queries! { query specialization_graph_of(trait_id: DefId) -> Result<&'tcx specialization_graph::Graph, ErrorGuaranteed> { desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(trait_id) } cache_on_disk_if { true } + ensure_forwards_result_if_red } query object_safety_violations(trait_id: DefId) -> &'tcx [ObjectSafetyViolation] { desc { |tcx| "determining object safety of trait `{}`", tcx.def_path_str(trait_id) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index ef621f29bfd71..1f81678d6a54c 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -350,7 +350,7 @@ impl<'tcx> TyCtxt<'tcx> { validate: impl Fn(Self, DefId) -> Result<(), ErrorGuaranteed>, ) -> Option { let drop_trait = self.lang_items().drop_trait()?; - self.ensure().coherent_trait(drop_trait); + self.ensure().coherent_trait(drop_trait).ok()?; let ty = self.type_of(adt_did).instantiate_identity(); let mut dtor_candidate = None; diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 3e09066141452..674e7e3553e98 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1114,7 +1114,13 @@ fn find_vtable_types_for_unsizing<'tcx>( assert_eq!(source_adt_def, target_adt_def); let CustomCoerceUnsized::Struct(coerce_index) = - crate::custom_coerce_unsize_info(tcx, source_ty, target_ty); + match crate::custom_coerce_unsize_info(tcx, source_ty, target_ty) { + Ok(ccu) => ccu, + Err(e) => { + let e = Ty::new_error(tcx.tcx, e); + return (e, e); + } + }; let source_fields = &source_adt_def.non_enum_variant().fields; let target_fields = &target_adt_def.non_enum_variant().fields; diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 2f5f2d15cd456..3fa40db3c806e 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -15,6 +15,7 @@ use rustc_middle::query::{Providers, TyCtxtAt}; use rustc_middle::traits; use rustc_middle::ty::adjustment::CustomCoerceUnsized; use rustc_middle::ty::{self, Ty}; +use rustc_span::ErrorGuaranteed; mod collector; mod errors; @@ -28,7 +29,7 @@ fn custom_coerce_unsize_info<'tcx>( tcx: TyCtxtAt<'tcx>, source_ty: Ty<'tcx>, target_ty: Ty<'tcx>, -) -> CustomCoerceUnsized { +) -> Result { let trait_ref = ty::TraitRef::from_lang_item( tcx.tcx, LangItem::CoerceUnsized, @@ -40,7 +41,7 @@ fn custom_coerce_unsize_info<'tcx>( Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData { impl_def_id, .. - })) => tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap(), + })) => Ok(tcx.coerce_unsized_info(impl_def_id)?.custom_kind.unwrap()), impl_source => { bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source); } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 24aa336c68fe9..c81261cc5a886 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -332,20 +332,6 @@ impl Session { } } - // FIXME(matthewjasper) Remove this method, it should never be needed. - pub fn track_errors(&self, f: F) -> Result - where - F: FnOnce() -> T, - { - let old_count = self.dcx().err_count(); - let result = f(); - if self.dcx().err_count() == old_count { - Ok(result) - } else { - Err(self.dcx().delayed_bug("`self.err_count()` changed but an error was not emitted")) - } - } - /// Used for code paths of expensive computations that should only take place when /// warnings or errors are emitted. If no messages are emitted ("good path"), then /// it's likely a bug. diff --git a/tests/ui/associated-consts/issue-105330.rs b/tests/ui/associated-consts/issue-105330.rs index 6c6dae864f340..285e89cce4985 100644 --- a/tests/ui/associated-consts/issue-105330.rs +++ b/tests/ui/associated-consts/issue-105330.rs @@ -14,6 +14,5 @@ fn foo>() { //~ ERROR E0658 fn main>() { //~^ ERROR E0658 - //~| ERROR E0131 foo::(); } diff --git a/tests/ui/associated-consts/issue-105330.stderr b/tests/ui/associated-consts/issue-105330.stderr index b4c021d0f4fdc..452367bed1201 100644 --- a/tests/ui/associated-consts/issue-105330.stderr +++ b/tests/ui/associated-consts/issue-105330.stderr @@ -43,13 +43,7 @@ LL | impl TraitWAssocConst for impl Demo { | = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0131]: `main` function is not allowed to have generic parameters - --> $DIR/issue-105330.rs:15:8 - | -LL | fn main>() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` cannot have generic parameters - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors -Some errors have detailed explanations: E0131, E0404, E0562, E0658. -For more information about an error, try `rustc --explain E0131`. +Some errors have detailed explanations: E0404, E0562, E0658. +For more information about an error, try `rustc --explain E0404`. diff --git a/tests/ui/associated-types/issue-38821.rs b/tests/ui/associated-types/issue-38821.rs index 6753860e9ff80..34c35d7acf1db 100644 --- a/tests/ui/associated-types/issue-38821.rs +++ b/tests/ui/associated-types/issue-38821.rs @@ -22,7 +22,22 @@ pub trait Column: Expression {} #[derive(Debug, Copy, Clone)] //~^ ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied pub enum ColumnInsertValue where +//~^ ERROR the trait bound `::SqlType: NotNull` is not satisfied +//~| ERROR the trait bound `::SqlType: NotNull` is not satisfied Col: Column, Expr: Expression::Nullable>, { diff --git a/tests/ui/associated-types/issue-38821.stderr b/tests/ui/associated-types/issue-38821.stderr index 8b628f5ae6d19..0e34f541ea303 100644 --- a/tests/ui/associated-types/issue-38821.stderr +++ b/tests/ui/associated-types/issue-38821.stderr @@ -17,6 +17,272 @@ help: consider further restricting the associated type LL | Expr: Expression::Nullable>, ::SqlType: NotNull, | +++++++++++++++++++++++++++++++++++++++ -error: aborting due to 1 previous error +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:38:1 + | +LL | pub enum ColumnInsertValue where + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here +help: consider extending the `where` clause, but there might be an alternative better way to express this requirement + | +LL | Expr: Expression::Nullable>, ::SqlType: NotNull + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:38:1 + | +LL | / pub enum ColumnInsertValue where +LL | | +LL | | +LL | | Col: Column, +... | +LL | | Default(Col), +LL | | } + | |_^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here +help: consider extending the `where` clause, but there might be an alternative better way to express this requirement + | +LL | Expr: Expression::Nullable>, ::SqlType: NotNull + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting the associated type + | +LL | Expr: Expression::Nullable>, ::SqlType: NotNull, + | +++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting the associated type + | +LL | Expr: Expression::Nullable>, ::SqlType: NotNull, + | +++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:17 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting the associated type + | +LL | Expr: Expression::Nullable>, ::SqlType: NotNull, + | +++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting the associated type + | +LL | Expr: Expression::Nullable>, ::SqlType: NotNull, + | +++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider further restricting the associated type + | +LL | Expr: Expression::Nullable>, ::SqlType: NotNull, + | +++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:10 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied + --> $DIR/issue-38821.rs:23:23 + | +LL | #[derive(Debug, Copy, Clone)] + | ^^^^^ the trait `NotNull` is not implemented for `::SqlType` + | +note: required for `::SqlType` to implement `IntoNullable` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl IntoNullable for T { + | ------- ^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 16 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coercion/issue-53475.rs b/tests/ui/coercion/issue-53475.rs index 3770c024fb92e..f389169f95405 100644 --- a/tests/ui/coercion/issue-53475.rs +++ b/tests/ui/coercion/issue-53475.rs @@ -3,7 +3,7 @@ use std::any::Any; use std::ops::CoerceUnsized; -struct Foo { +struct Foo { data: Box, } diff --git a/tests/ui/const-generics/bad-generic-in-copy-impl.rs b/tests/ui/const-generics/bad-generic-in-copy-impl.rs index b5663464cf422..092a7af236bad 100644 --- a/tests/ui/const-generics/bad-generic-in-copy-impl.rs +++ b/tests/ui/const-generics/bad-generic-in-copy-impl.rs @@ -2,6 +2,7 @@ pub struct Foo { x: [u8; SIZE], //~^ ERROR mismatched types + //~| ERROR mismatched types } const SIZE: u32 = 1; diff --git a/tests/ui/const-generics/bad-generic-in-copy-impl.stderr b/tests/ui/const-generics/bad-generic-in-copy-impl.stderr index d60d8ec0e90d3..fbd546d42fb52 100644 --- a/tests/ui/const-generics/bad-generic-in-copy-impl.stderr +++ b/tests/ui/const-generics/bad-generic-in-copy-impl.stderr @@ -4,6 +4,14 @@ error[E0308]: mismatched types LL | x: [u8; SIZE], | ^^^^ expected `usize`, found `u32` -error: aborting due to 1 previous error +error[E0308]: mismatched types + --> $DIR/bad-generic-in-copy-impl.rs:3:13 + | +LL | x: [u8; SIZE], + | ^^^^ expected `usize`, found `u32` + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/enum-discriminant/issue-72554.rs b/tests/ui/enum-discriminant/issue-72554.rs index 54f7e9ac592eb..1fe9a5f4faa7d 100644 --- a/tests/ui/enum-discriminant/issue-72554.rs +++ b/tests/ui/enum-discriminant/issue-72554.rs @@ -3,6 +3,7 @@ use std::collections::BTreeSet; #[derive(Hash)] pub enum ElemDerived { //~^ ERROR recursive type `ElemDerived` has infinite size + //~| ERROR cycle detected A(ElemDerived) } diff --git a/tests/ui/enum-discriminant/issue-72554.stderr b/tests/ui/enum-discriminant/issue-72554.stderr index 381f24d351e9c..648680c6031da 100644 --- a/tests/ui/enum-discriminant/issue-72554.stderr +++ b/tests/ui/enum-discriminant/issue-72554.stderr @@ -3,7 +3,7 @@ error[E0072]: recursive type `ElemDerived` has infinite size | LL | pub enum ElemDerived { | ^^^^^^^^^^^^^^^^^^^^ -LL | +... LL | A(ElemDerived) | ----------- recursive without indirection | @@ -12,6 +12,21 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle LL | A(Box) | ++++ + -error: aborting due to 1 previous error +error[E0391]: cycle detected when computing drop-check constraints for `ElemDerived` + --> $DIR/issue-72554.rs:4:1 + | +LL | pub enum ElemDerived { + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: ...which immediately requires computing drop-check constraints for `ElemDerived` again +note: cycle used when computing drop-check constraints for `Elem` + --> $DIR/issue-72554.rs:11:1 + | +LL | pub enum Elem { + | ^^^^^^^^^^^^^ + = 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 + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0072`. +Some errors have detailed explanations: E0072, E0391. +For more information about an error, try `rustc --explain E0072`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr index e5b9493b3ce11..5210a6942010d 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr @@ -7,16 +7,6 @@ LL | impl A for T { LL | impl const A for T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation -error[E0308]: mismatched types - --> $DIR/specializing-constness-2.rs:27:5 - | -LL | ::a(); - | ^^^^^^^^^^^^^ expected `host`, found `true` - | - = note: expected constant `host` - found constant `true` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0119, E0308. -For more information about an error, try `rustc --explain E0119`. +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs index 8ceecd801d3a7..a0ee771441772 100644 --- a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs +++ b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs @@ -6,7 +6,6 @@ struct S; impl Copy for S {} -//~^ ERROR the constant `N` is not of type `usize` impl Copy for S {} //~^ ERROR: conflicting implementations of trait `Copy` for type `S<_>` diff --git a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr index 3268a8767339f..2953bc95917c9 100644 --- a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr +++ b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr @@ -1,29 +1,11 @@ error[E0119]: conflicting implementations of trait `Copy` for type `S<_>` - --> $DIR/bad-const-wf-doesnt-specialize.rs:10:1 + --> $DIR/bad-const-wf-doesnt-specialize.rs:9:1 | LL | impl Copy for S {} | -------------------------------- first implementation here -LL | LL | impl Copy for S {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `S<_>` -error: the constant `N` is not of type `usize` - --> $DIR/bad-const-wf-doesnt-specialize.rs:8:29 - | -LL | impl Copy for S {} - | ^^^^ expected `usize`, found `i32` - | -note: required for `S` to implement `Clone` - --> $DIR/bad-const-wf-doesnt-specialize.rs:5:10 - | -LL | #[derive(Clone)] - | ^^^^^ -LL | struct S; - | ----- unsatisfied trait bound introduced in this `derive` macro -note: required by a bound in `Copy` - --> $SRC_DIR/core/src/marker.rs:LL:COL - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/typeck/typeck_type_placeholder_item.rs b/tests/ui/typeck/typeck_type_placeholder_item.rs index f6d1fb6a23d4c..4eba14f5a93fb 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.rs +++ b/tests/ui/typeck/typeck_type_placeholder_item.rs @@ -198,7 +198,6 @@ trait Qux { //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated types } impl Qux for Struct { - //~^ ERROR: not all trait items implemented, missing: `F` type A = _; //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated types type B = _; diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index bfcc76c1dae7d..a4325b01f02c4 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr @@ -29,7 +29,7 @@ LL | struct BadStruct2<_, T>(_, T); | ^ expected identifier, found reserved identifier error: associated constant in `impl` without body - --> $DIR/typeck_type_placeholder_item.rs:206:5 + --> $DIR/typeck_type_placeholder_item.rs:205:5 | LL | const C: _; | ^^^^^^^^^^- @@ -411,7 +411,7 @@ LL | type Y = impl Trait<_>; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/typeck_type_placeholder_item.rs:217:31 + --> $DIR/typeck_type_placeholder_item.rs:216:31 | LL | fn value() -> Option<&'static _> { | ----------------^- @@ -420,7 +420,7 @@ LL | fn value() -> Option<&'static _> { | help: replace with the correct return type: `Option<&'static u8>` error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/typeck_type_placeholder_item.rs:222:10 + --> $DIR/typeck_type_placeholder_item.rs:221:10 | LL | const _: Option<_> = map(value); | ^^^^^^^^^ @@ -429,7 +429,7 @@ LL | const _: Option<_> = map(value); | help: replace with the correct type: `Option` error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/typeck_type_placeholder_item.rs:225:31 + --> $DIR/typeck_type_placeholder_item.rs:224:31 | LL | fn evens_squared(n: usize) -> _ { | ^ @@ -438,13 +438,13 @@ LL | fn evens_squared(n: usize) -> _ { | help: replace with an appropriate return type: `impl Iterator` error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/typeck_type_placeholder_item.rs:230:10 + --> $DIR/typeck_type_placeholder_item.rs:229:10 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^ not allowed in type signatures | -note: however, the inferred type `Map, {closure@typeck_type_placeholder_item.rs:230:29}>, {closure@typeck_type_placeholder_item.rs:230:49}>` cannot be named - --> $DIR/typeck_type_placeholder_item.rs:230:14 +note: however, the inferred type `Map, {closure@typeck_type_placeholder_item.rs:229:29}>, {closure@typeck_type_placeholder_item.rs:229:49}>` cannot be named + --> $DIR/typeck_type_placeholder_item.rs:229:14 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -631,25 +631,25 @@ LL | fn clone_from(&mut self, other: &FnTest9) { *self = FnTest9; } | ~~~~~~~~ error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types - --> $DIR/typeck_type_placeholder_item.rs:202:14 + --> $DIR/typeck_type_placeholder_item.rs:201:14 | LL | type A = _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types - --> $DIR/typeck_type_placeholder_item.rs:204:14 + --> $DIR/typeck_type_placeholder_item.rs:203:14 | LL | type B = _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants - --> $DIR/typeck_type_placeholder_item.rs:206:14 + --> $DIR/typeck_type_placeholder_item.rs:205:14 | LL | const C: _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants - --> $DIR/typeck_type_placeholder_item.rs:209:14 + --> $DIR/typeck_type_placeholder_item.rs:208:14 | LL | const D: _ = 42; | ^ @@ -657,16 +657,7 @@ LL | const D: _ = 42; | not allowed in type signatures | help: replace with the correct type: `i32` -error[E0046]: not all trait items implemented, missing: `F` - --> $DIR/typeck_type_placeholder_item.rs:200:1 - | -LL | type F: std::ops::Fn(_); - | ----------------------- `F` from trait -... -LL | impl Qux for Struct { - | ^^^^^^^^^^^^^^^^^^^ missing `F` in implementation - -error: aborting due to 72 previous errors +error: aborting due to 71 previous errors -Some errors have detailed explanations: E0046, E0121, E0282, E0403. -For more information about an error, try `rustc --explain E0046`. +Some errors have detailed explanations: E0121, E0282, E0403. +For more information about an error, try `rustc --explain E0121`. From 849d884141530c1e5b04a7cd66a0332fe9dd1d7a Mon Sep 17 00:00:00 2001 From: David Koloski Date: Tue, 23 Jan 2024 19:10:17 +0000 Subject: [PATCH 08/18] Remove --fatal-warnings on wasm targets These were added with good intentions, but a recent change in LLVM 18 emits a warning while examining .rmeta sections in .rlib files. Since this flag is a nice-to-have and users can update their LLVM linker independently of rustc's LLVM version, we can just omit the flag. --- compiler/rustc_target/src/spec/base/wasm.rs | 3 --- .../src/spec/targets/wasm32_unknown_emscripten.rs | 5 +---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/compiler/rustc_target/src/spec/base/wasm.rs b/compiler/rustc_target/src/spec/base/wasm.rs index 87ade9e58cf45..4b4d2aca26e4a 100644 --- a/compiler/rustc_target/src/spec/base/wasm.rs +++ b/compiler/rustc_target/src/spec/base/wasm.rs @@ -38,9 +38,6 @@ pub fn options() -> TargetOptions { // supposed to be imported and have all other symbols generate errors if // they remain undefined. concat!($prefix, "--allow-undefined"), - // Rust code should never have warnings, and warnings are often - // indicative of bugs, let's prevent them. - concat!($prefix, "--fatal-warnings"), // LLD only implements C++-like demangling, which doesn't match our own // mangling scheme. Tell LLD to not demangle anything and leave it up to // us to demangle these symbols later. Currently rustc does not perform 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 394f02ecac495..e9e874297d39b 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -5,10 +5,7 @@ use crate::spec::{ pub fn target() -> Target { // Reset flags for non-Em flavors back to empty to satisfy sanity checking tests. let pre_link_args = LinkArgs::new(); - let post_link_args = TargetOptions::link_args( - LinkerFlavor::EmCc, - &["-sABORTING_MALLOC=0", "-Wl,--fatal-warnings"], - ); + let post_link_args = TargetOptions::link_args(LinkerFlavor::EmCc, &["-sABORTING_MALLOC=0"]); let opts = TargetOptions { os: "emscripten".into(), From 83ef18cd6cdcf80b7075424625123f878bda8722 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 24 Jan 2024 12:57:11 +1100 Subject: [PATCH 09/18] coverage: Dismantle `Instrumentor` into ordinary functions --- .../rustc_mir_transform/src/coverage/mod.rs | 265 +++++++++--------- 1 file changed, 126 insertions(+), 139 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index a11d224e8f1b5..d40b625be88a2 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -59,167 +59,154 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { _ => {} } - trace!("InstrumentCoverage starting for {def_id:?}"); - Instrumentor::new(tcx, mir_body).inject_counters(); - trace!("InstrumentCoverage done for {def_id:?}"); + instrument_function_for_coverage(tcx, mir_body); } } -struct Instrumentor<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - mir_body: &'a mut mir::Body<'tcx>, - hir_info: ExtractedHirInfo, - basic_coverage_blocks: CoverageGraph, -} - -impl<'a, 'tcx> Instrumentor<'a, 'tcx> { - fn new(tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self { - let hir_info = extract_hir_info(tcx, mir_body.source.def_id().expect_local()); +fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { + let def_id = mir_body.source.def_id(); + let _span = debug_span!("instrument_function_for_coverage", ?def_id).entered(); - debug!(?hir_info, "instrumenting {:?}", mir_body.source.def_id()); + let hir_info = extract_hir_info(tcx, def_id.expect_local()); + let basic_coverage_blocks = CoverageGraph::from_mir(mir_body); - let basic_coverage_blocks = CoverageGraph::from_mir(mir_body); + //////////////////////////////////////////////////// + // Compute coverage spans from the `CoverageGraph`. + let Some(coverage_spans) = + CoverageSpans::generate_coverage_spans(mir_body, &hir_info, &basic_coverage_blocks) + else { + // No relevant spans were found in MIR, so skip instrumenting this function. + return; + }; - Self { tcx, mir_body, hir_info, basic_coverage_blocks } + //////////////////////////////////////////////////// + // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure + // every coverage span has a `Counter` or `Expression` assigned to its `BasicCoverageBlock` + // and all `Expression` dependencies (operands) are also generated, for any other + // `BasicCoverageBlock`s not already associated with a coverage span. + let bcb_has_coverage_spans = |bcb| coverage_spans.bcb_has_coverage_spans(bcb); + let coverage_counters = + CoverageCounters::make_bcb_counters(&basic_coverage_blocks, bcb_has_coverage_spans); + + let mappings = create_mappings(tcx, &hir_info, &coverage_spans, &coverage_counters); + if mappings.is_empty() { + // No spans could be converted into valid mappings, so skip this function. + debug!("no spans could be converted into valid mappings; skipping"); + return; } - fn inject_counters(&'a mut self) { - //////////////////////////////////////////////////// - // Compute coverage spans from the `CoverageGraph`. - let Some(coverage_spans) = CoverageSpans::generate_coverage_spans( - self.mir_body, - &self.hir_info, - &self.basic_coverage_blocks, - ) else { - // No relevant spans were found in MIR, so skip instrumenting this function. - return; - }; - - //////////////////////////////////////////////////// - // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure - // every coverage span has a `Counter` or `Expression` assigned to its `BasicCoverageBlock` - // and all `Expression` dependencies (operands) are also generated, for any other - // `BasicCoverageBlock`s not already associated with a coverage span. - let bcb_has_coverage_spans = |bcb| coverage_spans.bcb_has_coverage_spans(bcb); - let coverage_counters = CoverageCounters::make_bcb_counters( - &self.basic_coverage_blocks, - bcb_has_coverage_spans, - ); + inject_coverage_statements( + mir_body, + &basic_coverage_blocks, + bcb_has_coverage_spans, + &coverage_counters, + ); - let mappings = self.create_mappings(&coverage_spans, &coverage_counters); - if mappings.is_empty() { - // No spans could be converted into valid mappings, so skip this function. - debug!("no spans could be converted into valid mappings; skipping"); - return; - } + mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { + function_source_hash: hir_info.function_source_hash, + num_counters: coverage_counters.num_counters(), + expressions: coverage_counters.into_expressions(), + mappings, + })); +} - self.inject_coverage_statements(bcb_has_coverage_spans, &coverage_counters); +/// For each coverage span extracted from MIR, create a corresponding +/// mapping. +/// +/// Precondition: All BCBs corresponding to those spans have been given +/// coverage counters. +fn create_mappings<'tcx>( + tcx: TyCtxt<'tcx>, + hir_info: &ExtractedHirInfo, + coverage_spans: &CoverageSpans, + coverage_counters: &CoverageCounters, +) -> Vec { + let source_map = tcx.sess.source_map(); + let body_span = hir_info.body_span; + + let source_file = source_map.lookup_source_file(body_span.lo()); + use rustc_session::RemapFileNameExt; + let file_name = Symbol::intern(&source_file.name.for_codegen(tcx.sess).to_string_lossy()); + + let term_for_bcb = |bcb| { + coverage_counters + .bcb_counter(bcb) + .expect("all BCBs with spans were given counters") + .as_term() + }; - self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { - function_source_hash: self.hir_info.function_source_hash, - num_counters: coverage_counters.num_counters(), - expressions: coverage_counters.into_expressions(), - mappings, - })); - } + coverage_spans + .all_bcb_mappings() + .filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| { + let kind = match bcb_mapping_kind { + BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)), + }; + let code_region = make_code_region(source_map, file_name, span, body_span)?; + Some(Mapping { kind, code_region }) + }) + .collect::>() +} - /// For each coverage span extracted from MIR, create a corresponding - /// mapping. - /// - /// Precondition: All BCBs corresponding to those spans have been given - /// coverage counters. - fn create_mappings( - &self, - coverage_spans: &CoverageSpans, - coverage_counters: &CoverageCounters, - ) -> Vec { - let source_map = self.tcx.sess.source_map(); - let body_span = self.hir_info.body_span; - - let source_file = source_map.lookup_source_file(body_span.lo()); - use rustc_session::RemapFileNameExt; - let file_name = - Symbol::intern(&source_file.name.for_codegen(self.tcx.sess).to_string_lossy()); - - let term_for_bcb = |bcb| { - coverage_counters - .bcb_counter(bcb) - .expect("all BCBs with spans were given counters") - .as_term() +/// For each BCB node or BCB edge that has an associated coverage counter, +/// inject any necessary coverage statements into MIR. +fn inject_coverage_statements<'tcx>( + mir_body: &mut mir::Body<'tcx>, + basic_coverage_blocks: &CoverageGraph, + bcb_has_coverage_spans: impl Fn(BasicCoverageBlock) -> bool, + coverage_counters: &CoverageCounters, +) { + // Process the counters associated with BCB nodes. + for (bcb, counter_kind) in coverage_counters.bcb_node_counters() { + let do_inject = match counter_kind { + // Counter-increment statements always need to be injected. + BcbCounter::Counter { .. } => true, + // The only purpose of expression-used statements is to detect + // when a mapping is unreachable, so we only inject them for + // expressions with one or more mappings. + BcbCounter::Expression { .. } => bcb_has_coverage_spans(bcb), }; - - coverage_spans - .all_bcb_mappings() - .filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| { - let kind = match bcb_mapping_kind { - BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)), - }; - let code_region = make_code_region(source_map, file_name, span, body_span)?; - Some(Mapping { kind, code_region }) - }) - .collect::>() + if do_inject { + inject_statement( + mir_body, + make_mir_coverage_kind(counter_kind), + basic_coverage_blocks[bcb].leader_bb(), + ); + } } - /// For each BCB node or BCB edge that has an associated coverage counter, - /// inject any necessary coverage statements into MIR. - fn inject_coverage_statements( - &mut self, - bcb_has_coverage_spans: impl Fn(BasicCoverageBlock) -> bool, - coverage_counters: &CoverageCounters, - ) { - // Process the counters associated with BCB nodes. - for (bcb, counter_kind) in coverage_counters.bcb_node_counters() { - let do_inject = match counter_kind { - // Counter-increment statements always need to be injected. - BcbCounter::Counter { .. } => true, - // The only purpose of expression-used statements is to detect - // when a mapping is unreachable, so we only inject them for - // expressions with one or more mappings. - BcbCounter::Expression { .. } => bcb_has_coverage_spans(bcb), - }; - if do_inject { - inject_statement( - self.mir_body, - self.make_mir_coverage_kind(counter_kind), - self.basic_coverage_blocks[bcb].leader_bb(), - ); - } + // Process the counters associated with BCB edges. + for (from_bcb, to_bcb, counter_kind) in coverage_counters.bcb_edge_counters() { + let do_inject = match counter_kind { + // Counter-increment statements always need to be injected. + BcbCounter::Counter { .. } => true, + // BCB-edge expressions never have mappings, so they never need + // a corresponding statement. + BcbCounter::Expression { .. } => false, + }; + if !do_inject { + continue; } - // Process the counters associated with BCB edges. - for (from_bcb, to_bcb, counter_kind) in coverage_counters.bcb_edge_counters() { - let do_inject = match counter_kind { - // Counter-increment statements always need to be injected. - BcbCounter::Counter { .. } => true, - // BCB-edge expressions never have mappings, so they never need - // a corresponding statement. - BcbCounter::Expression { .. } => false, - }; - if !do_inject { - continue; - } + // We need to inject a coverage statement into a new BB between the + // last BB of `from_bcb` and the first BB of `to_bcb`. + let from_bb = basic_coverage_blocks[from_bcb].last_bb(); + let to_bb = basic_coverage_blocks[to_bcb].leader_bb(); - // We need to inject a coverage statement into a new BB between the - // last BB of `from_bcb` and the first BB of `to_bcb`. - let from_bb = self.basic_coverage_blocks[from_bcb].last_bb(); - let to_bb = self.basic_coverage_blocks[to_bcb].leader_bb(); - - let new_bb = inject_edge_counter_basic_block(self.mir_body, from_bb, to_bb); - debug!( - "Edge {from_bcb:?} (last {from_bb:?}) -> {to_bcb:?} (leader {to_bb:?}) \ + let new_bb = inject_edge_counter_basic_block(mir_body, from_bb, to_bb); + debug!( + "Edge {from_bcb:?} (last {from_bb:?}) -> {to_bcb:?} (leader {to_bb:?}) \ requires a new MIR BasicBlock {new_bb:?} for edge counter {counter_kind:?}", - ); + ); - // Inject a counter into the newly-created BB. - inject_statement(self.mir_body, self.make_mir_coverage_kind(counter_kind), new_bb); - } + // Inject a counter into the newly-created BB. + inject_statement(mir_body, make_mir_coverage_kind(counter_kind), new_bb); } +} - fn make_mir_coverage_kind(&self, counter_kind: &BcbCounter) -> CoverageKind { - match *counter_kind { - BcbCounter::Counter { id } => CoverageKind::CounterIncrement { id }, - BcbCounter::Expression { id } => CoverageKind::ExpressionUsed { id }, - } +fn make_mir_coverage_kind(counter_kind: &BcbCounter) -> CoverageKind { + match *counter_kind { + BcbCounter::Counter { id } => CoverageKind::CounterIncrement { id }, + BcbCounter::Expression { id } => CoverageKind::ExpressionUsed { id }, } } From 572d7e9e69ca85c7a11e8730d9e4921d59691800 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 24 Jan 2024 12:40:31 +1100 Subject: [PATCH 10/18] coverage: Flatten the functions for extracting/refining coverage spans Consolidating this code into flatter functions reduces the amount of pointer-chasing required to read and modify it. --- .../rustc_mir_transform/src/coverage/mod.rs | 2 +- .../rustc_mir_transform/src/coverage/spans.rs | 119 +++++++----------- .../src/coverage/spans/from_mir.rs | 6 + 3 files changed, 53 insertions(+), 74 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index d40b625be88a2..59a83e8d3568d 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -73,7 +73,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: //////////////////////////////////////////////////// // Compute coverage spans from the `CoverageGraph`. let Some(coverage_spans) = - CoverageSpans::generate_coverage_spans(mir_body, &hir_info, &basic_coverage_blocks) + spans::generate_coverage_spans(mir_body, &hir_info, &basic_coverage_blocks) else { // No relevant spans were found in MIR, so skip instrumenting this function. return; diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 81f6c8312061b..dee6a3b7143f5 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -26,45 +26,6 @@ pub(super) struct CoverageSpans { } impl CoverageSpans { - /// Extracts coverage-relevant spans from MIR, and associates them with - /// their corresponding BCBs. - /// - /// Returns `None` if no coverage-relevant spans could be extracted. - pub(super) fn generate_coverage_spans( - mir_body: &mir::Body<'_>, - hir_info: &ExtractedHirInfo, - basic_coverage_blocks: &CoverageGraph, - ) -> Option { - let mut mappings = vec![]; - - let coverage_spans = CoverageSpansGenerator::generate_coverage_spans( - mir_body, - hir_info, - basic_coverage_blocks, - ); - mappings.extend(coverage_spans.into_iter().map(|CoverageSpan { bcb, span, .. }| { - // Each span produced by the generator represents an ordinary code region. - BcbMapping { kind: BcbMappingKind::Code(bcb), span } - })); - - if mappings.is_empty() { - return None; - } - - // Identify which BCBs have one or more mappings. - let mut bcb_has_mappings = BitSet::new_empty(basic_coverage_blocks.num_nodes()); - let mut insert = |bcb| { - bcb_has_mappings.insert(bcb); - }; - for &BcbMapping { kind, span: _ } in &mappings { - match kind { - BcbMappingKind::Code(bcb) => insert(bcb), - } - } - - Some(Self { bcb_has_mappings, mappings }) - } - pub(super) fn bcb_has_coverage_spans(&self, bcb: BasicCoverageBlock) -> bool { self.bcb_has_mappings.contains(bcb) } @@ -74,6 +35,43 @@ impl CoverageSpans { } } +/// Extracts coverage-relevant spans from MIR, and associates them with +/// their corresponding BCBs. +/// +/// Returns `None` if no coverage-relevant spans could be extracted. +pub(super) fn generate_coverage_spans( + mir_body: &mir::Body<'_>, + hir_info: &ExtractedHirInfo, + basic_coverage_blocks: &CoverageGraph, +) -> Option { + let mut mappings = vec![]; + + let sorted_spans = + from_mir::mir_to_initial_sorted_coverage_spans(mir_body, hir_info, basic_coverage_blocks); + let coverage_spans = SpansRefiner::refine_sorted_spans(basic_coverage_blocks, sorted_spans); + mappings.extend(coverage_spans.into_iter().map(|CoverageSpan { bcb, span, .. }| { + // Each span produced by the generator represents an ordinary code region. + BcbMapping { kind: BcbMappingKind::Code(bcb), span } + })); + + if mappings.is_empty() { + return None; + } + + // Identify which BCBs have one or more mappings. + let mut bcb_has_mappings = BitSet::new_empty(basic_coverage_blocks.num_nodes()); + let mut insert = |bcb| { + bcb_has_mappings.insert(bcb); + }; + for &BcbMapping { kind, span: _ } in &mappings { + match kind { + BcbMappingKind::Code(bcb) => insert(bcb), + } + } + + Some(CoverageSpans { bcb_has_mappings, mappings }) +} + /// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that /// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s. /// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent @@ -130,7 +128,7 @@ impl CoverageSpan { /// * Merge spans that represent continuous (both in source code and control flow), non-branching /// execution /// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures) -struct CoverageSpansGenerator<'a> { +struct SpansRefiner<'a> { /// The BasicCoverageBlock Control Flow Graph (BCB CFG). basic_coverage_blocks: &'a CoverageGraph, @@ -173,40 +171,15 @@ struct CoverageSpansGenerator<'a> { refined_spans: Vec, } -impl<'a> CoverageSpansGenerator<'a> { - /// Generate a minimal set of `CoverageSpan`s, each representing a contiguous code region to be - /// counted. - /// - /// The basic steps are: - /// - /// 1. Extract an initial set of spans from the `Statement`s and `Terminator`s of each - /// `BasicCoverageBlockData`. - /// 2. Sort the spans by span.lo() (starting position). Spans that start at the same position - /// are sorted with longer spans before shorter spans; and equal spans are sorted - /// (deterministically) based on "dominator" relationship (if any). - /// 3. Traverse the spans in sorted order to identify spans that can be dropped (for instance, - /// if another span or spans are already counting the same code region), or should be merged - /// into a broader combined span (because it represents a contiguous, non-branching, and - /// uninterrupted region of source code). - /// - /// Closures are exposed in their enclosing functions as `Assign` `Rvalue`s, and since - /// closures have their own MIR, their `Span` in their enclosing function should be left - /// "uncovered". - /// - /// Note the resulting vector of `CoverageSpan`s may not be fully sorted (and does not need - /// to be). - pub(super) fn generate_coverage_spans( - mir_body: &mir::Body<'_>, - hir_info: &ExtractedHirInfo, +impl<'a> SpansRefiner<'a> { + /// Takes the initial list of (sorted) spans extracted from MIR, and "refines" + /// them by merging compatible adjacent spans, removing redundant spans, + /// and carving holes in spans when they overlap in unwanted ways. + fn refine_sorted_spans( basic_coverage_blocks: &'a CoverageGraph, + sorted_spans: Vec, ) -> Vec { - let sorted_spans = from_mir::mir_to_initial_sorted_coverage_spans( - mir_body, - hir_info, - basic_coverage_blocks, - ); - - let coverage_spans = Self { + let this = Self { basic_coverage_blocks, sorted_spans_iter: sorted_spans.into_iter(), some_curr: None, @@ -217,7 +190,7 @@ impl<'a> CoverageSpansGenerator<'a> { refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2), }; - coverage_spans.to_refined_spans() + this.to_refined_spans() } /// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 1b6dfccd57495..8d8e8e6132743 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -12,6 +12,12 @@ use crate::coverage::graph::{ use crate::coverage::spans::CoverageSpan; use crate::coverage::ExtractedHirInfo; +/// Traverses the MIR body to produce an initial collection of coverage-relevant +/// spans, each associated with a node in the coverage graph (BCB) and possibly +/// other metadata. +/// +/// The returned spans are sorted in a specific order that is expected by the +/// subsequent span-refinement step. pub(super) fn mir_to_initial_sorted_coverage_spans( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, From 64f590a50d0fed4a7267076d73bf9f3dcd71aff2 Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 22 Jan 2024 12:08:48 +0100 Subject: [PATCH 11/18] Assert that a single scope is passed to `for_scope` --- compiler/rustc_session/src/session.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index cba6ce0d235ea..c26a8467b6adb 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1523,16 +1523,25 @@ pub trait RemapFileNameExt { where Self: 'a; - fn for_scope(&self, sess: &Session, scopes: RemapPathScopeComponents) -> Self::Output<'_>; + /// Returns a possibly remapped filename based on the passed scope and remap cli options. + /// + /// One and only one scope should be passed to this method. For anything related to + /// "codegen" see the [`RemapFileNameExt::for_codegen`] method. + fn for_scope(&self, sess: &Session, scope: RemapPathScopeComponents) -> Self::Output<'_>; + /// Return a possibly remapped filename, to be used in "codegen" related parts. fn for_codegen(&self, sess: &Session) -> Self::Output<'_>; } impl RemapFileNameExt for rustc_span::FileName { type Output<'a> = rustc_span::FileNameDisplay<'a>; - fn for_scope(&self, sess: &Session, scopes: RemapPathScopeComponents) -> Self::Output<'_> { - if sess.opts.unstable_opts.remap_path_scope.contains(scopes) { + fn for_scope(&self, sess: &Session, scope: RemapPathScopeComponents) -> Self::Output<'_> { + assert!( + scope.bits().count_ones() == 1, + "one and only one scope should be passed to for_scope" + ); + if sess.opts.unstable_opts.remap_path_scope.contains(scope) { self.prefer_remapped_unconditionaly() } else { self.prefer_local() @@ -1551,8 +1560,12 @@ impl RemapFileNameExt for rustc_span::FileName { impl RemapFileNameExt for rustc_span::RealFileName { type Output<'a> = &'a Path; - fn for_scope(&self, sess: &Session, scopes: RemapPathScopeComponents) -> Self::Output<'_> { - if sess.opts.unstable_opts.remap_path_scope.contains(scopes) { + fn for_scope(&self, sess: &Session, scope: RemapPathScopeComponents) -> Self::Output<'_> { + assert!( + scope.bits().count_ones() == 1, + "one and only one scope should be passed to for_scope" + ); + if sess.opts.unstable_opts.remap_path_scope.contains(scope) { self.remapped_path_if_available() } else { self.local_path_if_available() From cc34dc2bc7a76f7a3e038f567d11f0ac88a0e677 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 24 Jan 2024 11:04:13 +0000 Subject: [PATCH 12/18] Correctly explain `ensure_forwards_result_if_red` --- compiler/rustc_macros/src/query.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index ad1980136f3b9..25675e06e38d6 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -120,7 +120,8 @@ struct QueryModifiers { /// Forward the result on ensure if the query gets recomputed, and /// return `Ok(())` otherwise. Only applicable to queries returning - /// `Result<(), ErrorGuaranteed>` + /// `Result`. The `T` is not returned from `ensure` + /// invocations. ensure_forwards_result_if_red: Option, } From e088016f9d0dd8d69941a2ad2088ff2631706c1c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 24 Jan 2024 16:55:26 +0100 Subject: [PATCH 13/18] Let `ctor_sub_tys` return any Iterator they want Since we always clone and allocate the types somewhere else ourselves, no need to ask for `Cx` to do the allocation. --- compiler/rustc_pattern_analysis/src/lib.rs | 6 ++++- compiler/rustc_pattern_analysis/src/pat.rs | 3 +-- compiler/rustc_pattern_analysis/src/rustc.rs | 26 +++++++++---------- .../rustc_pattern_analysis/src/usefulness.rs | 8 +++--- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 4fd01b5e638ce..1cf74a89b65aa 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -101,7 +101,11 @@ pub trait TypeCx: Sized + fmt::Debug { /// The types of the fields for this constructor. The result must have a length of /// `ctor_arity()`. - fn ctor_sub_tys(&self, ctor: &Constructor, ty: &Self::Ty) -> &[Self::Ty]; + fn ctor_sub_tys<'a>( + &'a self, + ctor: &'a Constructor, + ty: &'a Self::Ty, + ) -> impl Iterator + ExactSizeIterator + Captures<'a>; /// The set of all the constructors for `ty`. /// diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 8cd0ecb073c2f..1c98ec5bc1261 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -241,8 +241,7 @@ impl WitnessPat { /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. pub(crate) fn wild_from_ctor(pcx: &PlaceCtxt<'_, Cx>, ctor: Constructor) -> Self { - let field_tys = pcx.ctor_sub_tys(&ctor); - let fields = field_tys.iter().cloned().map(|ty| Self::wildcard(ty)).collect(); + let fields = pcx.ctor_sub_tys(&ctor).map(|ty| Self::wildcard(ty)).collect(); Self::new(ctor, fields, pcx.ty.clone()) } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index d8c3c010a2a93..c36b312ccb659 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -3,7 +3,6 @@ use std::fmt; use std::iter::once; use rustc_arena::{DroplessArena, TypedArena}; -use rustc_data_structures::captures::Captures; use rustc_hir::def_id::DefId; use rustc_hir::HirId; use rustc_index::{Idx, IndexVec}; @@ -20,7 +19,7 @@ use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; use crate::constructor::{ IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility, }; -use crate::{errors, TypeCx}; +use crate::{errors, Captures, TypeCx}; use crate::constructor::Constructor::*; @@ -210,11 +209,11 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { /// Returns the types of the fields for a given constructor. The result must have a length of /// `ctor.arity()`. #[instrument(level = "trace", skip(self))] - pub(crate) fn ctor_sub_tys( - &self, - ctor: &Constructor<'p, 'tcx>, + pub(crate) fn ctor_sub_tys<'a>( + &'a self, + ctor: &'a Constructor<'p, 'tcx>, ty: RevealedTy<'tcx>, - ) -> &[RevealedTy<'tcx>] { + ) -> impl Iterator> + ExactSizeIterator + Captures<'a> { fn reveal_and_alloc<'a, 'tcx>( cx: &'a RustcMatchCheckCtxt<'_, 'tcx>, iter: impl Iterator>, @@ -222,7 +221,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { cx.dropless_arena.alloc_from_iter(iter.map(|ty| cx.reveal_opaque_ty(ty))) } let cx = self; - match ctor { + let slice = match ctor { Struct | Variant(_) | UnionField => match ty.kind() { ty::Tuple(fs) => reveal_and_alloc(cx, fs.iter()), ty::Adt(adt, args) => { @@ -263,7 +262,8 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { Or => { bug!("called `Fields::wildcards` on an `Or` ctor") } - } + }; + slice.iter().copied() } /// The number of fields for this constructor. @@ -964,11 +964,11 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { fn ctor_arity(&self, ctor: &crate::constructor::Constructor, ty: &Self::Ty) -> usize { self.ctor_arity(ctor, *ty) } - fn ctor_sub_tys( - &self, - ctor: &crate::constructor::Constructor, - ty: &Self::Ty, - ) -> &[Self::Ty] { + fn ctor_sub_tys<'a>( + &'a self, + ctor: &'a crate::constructor::Constructor, + ty: &'a Self::Ty, + ) -> impl Iterator + ExactSizeIterator + Captures<'a> { self.ctor_sub_tys(ctor, *ty) } fn ctors_for_ty( diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index d7852a2b2cbfc..0de48c9f3c0d0 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -750,7 +750,10 @@ impl<'a, Cx: TypeCx> PlaceCtxt<'a, Cx> { pub(crate) fn ctor_arity(&self, ctor: &Constructor) -> usize { self.mcx.tycx.ctor_arity(ctor, self.ty) } - pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor) -> &[Cx::Ty] { + pub(crate) fn ctor_sub_tys( + &'a self, + ctor: &'a Constructor, + ) -> impl Iterator + ExactSizeIterator + Captures<'a> { self.mcx.tycx.ctor_sub_tys(ctor, self.ty) } pub(crate) fn ctors_for_ty(&self) -> Result, Cx::Error> { @@ -1058,8 +1061,7 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> { ) -> Matrix<'p, Cx> { let ctor_sub_tys = pcx.ctor_sub_tys(ctor); let arity = ctor_sub_tys.len(); - let specialized_place_ty = - ctor_sub_tys.iter().chain(self.place_ty[1..].iter()).cloned().collect(); + let specialized_place_ty = ctor_sub_tys.chain(self.place_ty[1..].iter().cloned()).collect(); let ctor_sub_validity = self.place_validity[0].specialize(ctor); let specialized_place_validity = std::iter::repeat(ctor_sub_validity) .take(arity) From d992d9cd56c21c9fad629f95826895b03bdc7077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 24 Jan 2024 16:30:37 +0000 Subject: [PATCH 14/18] On E0308 involving `dyn Trait`, mention trait objects When encountering a type mismatch error involving `dyn Trait`, mention the existence of boxed trait objects if the other type involved implements `Trait`. Partially addresses #102629. --- .../infer/error_reporting/note_and_explain.rs | 49 +++++++++++++++++++ tests/ui/dst/dst-bad-assign-3.stderr | 1 + tests/ui/dst/dst-bad-assign.stderr | 1 + tests/ui/dyn-star/no-implicit-dyn-star.stderr | 1 + .../issue-79422.extended.stderr | 1 + ...n-trait-return-should-be-impl-trait.stderr | 2 + tests/ui/methods/issues/issue-61525.stderr | 1 + 7 files changed, 56 insertions(+) diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 01cd3c57925b3..662903e30fc3c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -294,6 +294,55 @@ impl Trait for X { ); } } + (ty::Dynamic(t, _, ty::DynKind::Dyn), _) + if let Some(def_id) = t.principal_def_id() => + { + let mut impl_def_ids = vec![]; + tcx.for_each_relevant_impl(def_id, values.found, |did| { + impl_def_ids.push(did) + }); + if let [_] = &impl_def_ids[..] { + let trait_name = tcx.item_name(def_id); + diag.help(format!( + "`{}` implements `{trait_name}` so you could box the found value \ + and coerce it to the trait object `Box`, you \ + will have to change the expected type as well", + values.found, + )); + } + } + (_, ty::Dynamic(t, _, ty::DynKind::Dyn)) + if let Some(def_id) = t.principal_def_id() => + { + let mut impl_def_ids = vec![]; + tcx.for_each_relevant_impl(def_id, values.expected, |did| { + impl_def_ids.push(did) + }); + if let [_] = &impl_def_ids[..] { + let trait_name = tcx.item_name(def_id); + diag.help(format!( + "`{}` implements `{trait_name}` so you could change the expected \ + type to `Box`", + values.expected, + )); + } + } + (ty::Dynamic(t, _, ty::DynKind::DynStar), _) + if let Some(def_id) = t.principal_def_id() => + { + let mut impl_def_ids = vec![]; + tcx.for_each_relevant_impl(def_id, values.found, |did| { + impl_def_ids.push(did) + }); + if let [_] = &impl_def_ids[..] { + let trait_name = tcx.item_name(def_id); + diag.help(format!( + "`{}` implements `{trait_name}`, `#[feature(dyn_star)]` is likely \ + not enabled; that feature it is currently incomplete", + values.found, + )); + } + } (_, ty::Alias(ty::Opaque, opaque_ty)) | (ty::Alias(ty::Opaque, opaque_ty), _) => { if opaque_ty.def_id.is_local() diff --git a/tests/ui/dst/dst-bad-assign-3.stderr b/tests/ui/dst/dst-bad-assign-3.stderr index 6dd3434fd21be..007f6b898bee5 100644 --- a/tests/ui/dst/dst-bad-assign-3.stderr +++ b/tests/ui/dst/dst-bad-assign-3.stderr @@ -8,6 +8,7 @@ LL | f5.2 = Bar1 {f: 36}; | = note: expected trait object `dyn ToBar` found struct `Bar1` + = help: `Bar1` implements `ToBar` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error[E0277]: the size for values of type `dyn ToBar` cannot be known at compilation time --> $DIR/dst-bad-assign-3.rs:33:5 diff --git a/tests/ui/dst/dst-bad-assign.stderr b/tests/ui/dst/dst-bad-assign.stderr index d8d1057876f4f..f935d27e96e2f 100644 --- a/tests/ui/dst/dst-bad-assign.stderr +++ b/tests/ui/dst/dst-bad-assign.stderr @@ -8,6 +8,7 @@ LL | f5.ptr = Bar1 {f: 36}; | = note: expected trait object `dyn ToBar` found struct `Bar1` + = help: `Bar1` implements `ToBar` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error[E0277]: the size for values of type `dyn ToBar` cannot be known at compilation time --> $DIR/dst-bad-assign.rs:35:5 diff --git a/tests/ui/dyn-star/no-implicit-dyn-star.stderr b/tests/ui/dyn-star/no-implicit-dyn-star.stderr index 06c7bcc574825..bea334a8a69a8 100644 --- a/tests/ui/dyn-star/no-implicit-dyn-star.stderr +++ b/tests/ui/dyn-star/no-implicit-dyn-star.stderr @@ -8,6 +8,7 @@ LL | dyn_star_foreign::require_dyn_star_display(1usize); | = note: expected trait object `(dyn* std::fmt::Display + 'static)` found type `usize` + = help: `usize` implements `Display`, `#[feature(dyn_star)]` is likely not enabled; that feature it is currently incomplete note: function defined here --> $DIR/auxiliary/dyn-star-foreign.rs:6:8 | diff --git a/tests/ui/generic-associated-types/issue-79422.extended.stderr b/tests/ui/generic-associated-types/issue-79422.extended.stderr index 14492266cdaf3..ae1526296a795 100644 --- a/tests/ui/generic-associated-types/issue-79422.extended.stderr +++ b/tests/ui/generic-associated-types/issue-79422.extended.stderr @@ -27,6 +27,7 @@ LL | type VRefCont<'a> = &'a V where Self: 'a; | ^^^^^ = note: expected trait object `(dyn RefCont<'_, u8> + 'static)` found reference `&u8` + = help: `&u8` implements `RefCont` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well = note: required for the cast from `Box>` to `Box + 'static)>>` error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index 4a52f82540d83..9cc199d1e0ec6 100644 --- a/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -6,6 +6,7 @@ LL | fn fuz() -> (usize, Trait) { (42, Struct) } | = note: expected trait object `(dyn Trait + 'static)` found struct `Struct` + = help: `Struct` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time --> $DIR/dyn-trait-return-should-be-impl-trait.rs:7:13 @@ -27,6 +28,7 @@ LL | fn bar() -> (usize, dyn Trait) { (42, Struct) } | = note: expected trait object `(dyn Trait + 'static)` found struct `Struct` + = help: `Struct` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time --> $DIR/dyn-trait-return-should-be-impl-trait.rs:10:13 diff --git a/tests/ui/methods/issues/issue-61525.stderr b/tests/ui/methods/issues/issue-61525.stderr index a329b52e37375..2670a3e4755b1 100644 --- a/tests/ui/methods/issues/issue-61525.stderr +++ b/tests/ui/methods/issues/issue-61525.stderr @@ -27,6 +27,7 @@ LL | 1.query::("") | = note: expected trait object `dyn ToString` found reference `&'static str` + = help: `&'static str` implements `ToString` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well note: method defined here --> $DIR/issue-61525.rs:2:8 | From 796814d91627203444769a680c53ff59a7ff4696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 24 Jan 2024 16:57:15 +0000 Subject: [PATCH 15/18] Account for expected `dyn Trait` found `impl Trait` --- .../infer/error_reporting/note_and_explain.rs | 20 +++++++++ tests/ui/impl-trait/dyn-impl-type-mismatch.rs | 18 ++++++++ .../impl-trait/dyn-impl-type-mismatch.stderr | 43 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 tests/ui/impl-trait/dyn-impl-type-mismatch.rs create mode 100644 tests/ui/impl-trait/dyn-impl-type-mismatch.stderr diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 662903e30fc3c..21ea48c6c83da 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -294,6 +294,26 @@ impl Trait for X { ); } } + (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) + if let Some(def_id) = t.principal_def_id() + && tcx.explicit_item_bounds(alias.def_id).skip_binder().iter().any( + |(pred, _span)| match pred.kind().skip_binder() { + ty::ClauseKind::Trait(trait_predicate) + if trait_predicate.polarity + == ty::ImplPolarity::Positive => + { + trait_predicate.def_id() == def_id + } + _ => false, + }, + ) => + { + diag.help(format!( + "you can box the `{}` to coerce it to `Box<{}>`, but you'll have to \ + change the expected type as well", + values.found, values.expected, + )); + } (ty::Dynamic(t, _, ty::DynKind::Dyn), _) if let Some(def_id) = t.principal_def_id() => { diff --git a/tests/ui/impl-trait/dyn-impl-type-mismatch.rs b/tests/ui/impl-trait/dyn-impl-type-mismatch.rs new file mode 100644 index 0000000000000..c6170abb58285 --- /dev/null +++ b/tests/ui/impl-trait/dyn-impl-type-mismatch.rs @@ -0,0 +1,18 @@ +trait Trait {} +struct Struct; +impl Trait for Struct {} +fn foo() -> impl Trait { + Struct +} +fn main() { + let a: Box = if true { + Box::new(Struct) + } else { + foo() //~ ERROR E0308 + }; + let a: dyn Trait = if true { + Struct //~ ERROR E0308 + } else { + foo() //~ ERROR E0308 + }; +} diff --git a/tests/ui/impl-trait/dyn-impl-type-mismatch.stderr b/tests/ui/impl-trait/dyn-impl-type-mismatch.stderr new file mode 100644 index 0000000000000..ddbdaf3eb4b03 --- /dev/null +++ b/tests/ui/impl-trait/dyn-impl-type-mismatch.stderr @@ -0,0 +1,43 @@ +error[E0308]: mismatched types + --> $DIR/dyn-impl-type-mismatch.rs:11:9 + | +LL | fn foo() -> impl Trait { + | ---------- the found opaque type +... +LL | foo() + | ^^^^^ expected `Box`, found opaque type + | + = note: expected struct `Box` + found opaque type `impl Trait` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +help: store this in the heap by calling `Box::new` + | +LL | Box::new(foo()) + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/dyn-impl-type-mismatch.rs:14:9 + | +LL | Struct + | ^^^^^^ expected `dyn Trait`, found `Struct` + | + = note: expected trait object `dyn Trait` + found struct `Struct` + = help: `Struct` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well + +error[E0308]: mismatched types + --> $DIR/dyn-impl-type-mismatch.rs:16:9 + | +LL | fn foo() -> impl Trait { + | ---------- the found opaque type +... +LL | foo() + | ^^^^^ expected `dyn Trait`, found opaque type + | + = note: expected trait object `dyn Trait` + found opaque type `impl Trait` + = help: you can box the `impl Trait` to coerce it to `Box`, but you'll have to change the expected type as well + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. From bdab21399372bee9127b5e6641c8a54034005138 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 24 Jan 2024 20:04:33 +0100 Subject: [PATCH 16/18] Most of the `DeconstructedPat` `Debug` impl is reusable --- compiler/rustc_pattern_analysis/src/lib.rs | 8 +- compiler/rustc_pattern_analysis/src/pat.rs | 70 +++++++++++- compiler/rustc_pattern_analysis/src/rustc.rs | 110 ++----------------- 3 files changed, 86 insertions(+), 102 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 4fd01b5e638ce..5178490317804 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -108,8 +108,12 @@ pub trait TypeCx: Sized + fmt::Debug { /// This must follow the invariants of `ConstructorSet` fn ctors_for_ty(&self, ty: &Self::Ty) -> Result, Self::Error>; - /// Best-effort `Debug` implementation. - fn debug_pat(f: &mut fmt::Formatter<'_>, pat: &DeconstructedPat<'_, Self>) -> fmt::Result; + /// Write the name of the variant represented by `pat`. Used for the best-effort `Debug` impl of + /// `DeconstructedPat`. Only invoqued when `pat.ctor()` is `Struct | Variant(_) | UnionField`. + fn write_variant_name( + f: &mut fmt::Formatter<'_>, + pat: &crate::pat::DeconstructedPat<'_, Self>, + ) -> fmt::Result; /// Raise a bug. fn bug(&self, fmt: fmt::Arguments<'_>) -> !; diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 8cd0ecb073c2f..e9f0a80699f8b 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -142,7 +142,75 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> { /// This is best effort and not good enough for a `Display` impl. impl<'p, Cx: TypeCx> fmt::Debug for DeconstructedPat<'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Cx::debug_pat(f, self) + let pat = self; + let mut first = true; + let mut start_or_continue = |s| { + if first { + first = false; + "" + } else { + s + } + }; + let mut start_or_comma = || start_or_continue(", "); + + match pat.ctor() { + Struct | Variant(_) | UnionField => { + Cx::write_variant_name(f, pat)?; + // Without `cx`, we can't know which field corresponds to which, so we can't + // get the names of the fields. Instead we just display everything as a tuple + // struct, which should be good enough. + write!(f, "(")?; + for p in pat.iter_fields() { + write!(f, "{}", start_or_comma())?; + write!(f, "{p:?}")?; + } + write!(f, ")") + } + // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should + // be careful to detect strings here. However a string literal pattern will never + // be reported as a non-exhaustiveness witness, so we can ignore this issue. + Ref => { + let subpattern = pat.iter_fields().next().unwrap(); + write!(f, "&{:?}", subpattern) + } + Slice(slice) => { + let mut subpatterns = pat.iter_fields(); + write!(f, "[")?; + match slice.kind { + SliceKind::FixedLen(_) => { + for p in subpatterns { + write!(f, "{}{:?}", start_or_comma(), p)?; + } + } + SliceKind::VarLen(prefix_len, _) => { + for p in subpatterns.by_ref().take(prefix_len) { + write!(f, "{}{:?}", start_or_comma(), p)?; + } + write!(f, "{}", start_or_comma())?; + write!(f, "..")?; + for p in subpatterns { + write!(f, "{}{:?}", start_or_comma(), p)?; + } + } + } + write!(f, "]") + } + Bool(b) => write!(f, "{b}"), + // Best-effort, will render signed ranges incorrectly + IntRange(range) => write!(f, "{range:?}"), + F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"), + F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"), + Str(value) => write!(f, "{value:?}"), + Opaque(..) => write!(f, ""), + Or => { + for pat in pat.iter_fields() { + write!(f, "{}{:?}", start_or_continue(" | "), pat)?; + } + Ok(()) + } + Wildcard | Missing { .. } | NonExhaustive | Hidden => write!(f, "_ : {:?}", pat.ty()), + } } } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index d8c3c010a2a93..f60f1b9f96d4a 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -850,103 +850,6 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { Pat { ty: pat.ty().inner(), span: DUMMY_SP, kind } } - - /// Best-effort `Debug` implementation. - pub(crate) fn debug_pat( - f: &mut fmt::Formatter<'_>, - pat: &crate::pat::DeconstructedPat<'_, Self>, - ) -> fmt::Result { - let mut first = true; - let mut start_or_continue = |s| { - if first { - first = false; - "" - } else { - s - } - }; - let mut start_or_comma = || start_or_continue(", "); - - match pat.ctor() { - Struct | Variant(_) | UnionField => match pat.ty().kind() { - ty::Adt(def, _) if def.is_box() => { - // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside - // of `std`). So this branch is only reachable when the feature is enabled and - // the pattern is a box pattern. - let subpattern = pat.iter_fields().next().unwrap(); - write!(f, "box {subpattern:?}") - } - ty::Adt(..) | ty::Tuple(..) => { - let variant = - match pat.ty().kind() { - ty::Adt(adt, _) => Some(adt.variant( - RustcMatchCheckCtxt::variant_index_for_adt(pat.ctor(), *adt), - )), - ty::Tuple(_) => None, - _ => unreachable!(), - }; - - if let Some(variant) = variant { - write!(f, "{}", variant.name)?; - } - - // Without `cx`, we can't know which field corresponds to which, so we can't - // get the names of the fields. Instead we just display everything as a tuple - // struct, which should be good enough. - write!(f, "(")?; - for p in pat.iter_fields() { - write!(f, "{}", start_or_comma())?; - write!(f, "{p:?}")?; - } - write!(f, ")") - } - _ => write!(f, "_"), - }, - // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should - // be careful to detect strings here. However a string literal pattern will never - // be reported as a non-exhaustiveness witness, so we can ignore this issue. - Ref => { - let subpattern = pat.iter_fields().next().unwrap(); - write!(f, "&{:?}", subpattern) - } - Slice(slice) => { - let mut subpatterns = pat.iter_fields(); - write!(f, "[")?; - match slice.kind { - SliceKind::FixedLen(_) => { - for p in subpatterns { - write!(f, "{}{:?}", start_or_comma(), p)?; - } - } - SliceKind::VarLen(prefix_len, _) => { - for p in subpatterns.by_ref().take(prefix_len) { - write!(f, "{}{:?}", start_or_comma(), p)?; - } - write!(f, "{}", start_or_comma())?; - write!(f, "..")?; - for p in subpatterns { - write!(f, "{}{:?}", start_or_comma(), p)?; - } - } - } - write!(f, "]") - } - Bool(b) => write!(f, "{b}"), - // Best-effort, will render signed ranges incorrectly - IntRange(range) => write!(f, "{range:?}"), - F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"), - F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"), - Str(value) => write!(f, "{value}"), - Opaque(..) => write!(f, ""), - Or => { - for pat in pat.iter_fields() { - write!(f, "{}{:?}", start_or_continue(" | "), pat)?; - } - Ok(()) - } - Wildcard | Missing { .. } | NonExhaustive | Hidden => write!(f, "_ : {:?}", pat.ty()), - } - } } impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { @@ -978,12 +881,21 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { self.ctors_for_ty(*ty) } - fn debug_pat( + fn write_variant_name( f: &mut fmt::Formatter<'_>, pat: &crate::pat::DeconstructedPat<'_, Self>, ) -> fmt::Result { - Self::debug_pat(f, pat) + if let ty::Adt(adt, _) = pat.ty().kind() { + if adt.is_box() { + write!(f, "Box")? + } else { + let variant = adt.variant(Self::variant_index_for_adt(pat.ctor(), *adt)); + write!(f, "{}", variant.name)?; + } + } + Ok(()) } + fn bug(&self, fmt: fmt::Arguments<'_>) -> ! { span_bug!(self.scrut_span, "{}", fmt) } From 354b45f52854d4534aaf4bf860d539a9657a596f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 24 Jan 2024 20:09:30 +0100 Subject: [PATCH 17/18] Improve `Range: Debug` impl --- .../rustc_pattern_analysis/src/constructor.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index eba71a23435a6..e94a0373c79ff 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -391,12 +391,18 @@ impl IntRange { /// first. impl fmt::Debug for IntRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Finite(lo) = self.lo { + if self.is_singleton() { + // Only finite ranges can be singletons. + let Finite(lo) = self.lo else { unreachable!() }; write!(f, "{lo}")?; - } - write!(f, "{}", RangeEnd::Excluded)?; - if let Finite(hi) = self.hi { - write!(f, "{hi}")?; + } else { + if let Finite(lo) = self.lo { + write!(f, "{lo}")?; + } + write!(f, "{}", RangeEnd::Excluded)?; + if let Finite(hi) = self.hi { + write!(f, "{hi}")?; + } } Ok(()) } From 8f3af4c6e21feb3cada901ea6f41f198b0f0e74a Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 24 Jan 2024 15:36:57 -0800 Subject: [PATCH 18/18] rustc_data_structures: use either instead of itertools --- Cargo.lock | 2 +- compiler/rustc_data_structures/Cargo.toml | 2 +- compiler/rustc_data_structures/src/sharded.rs | 2 +- compiler/rustc_data_structures/src/sso/map.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 537571ee6b542..09eb0d98efc90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3654,10 +3654,10 @@ version = "0.0.0" dependencies = [ "arrayvec", "bitflags 2.4.1", + "either", "elsa", "ena", "indexmap", - "itertools", "jobserver", "libc", "measureme", diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 9d598c32e6fc4..08aa68ca54a7a 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -7,10 +7,10 @@ edition = "2021" # tidy-alphabetical-start arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" +either = "1.0" elsa = "=1.7.1" ena = "0.14.2" indexmap = { version = "2.0.0" } -itertools = "0.11" jobserver_crate = { version = "0.1.27", package = "jobserver" } libc = "0.2" measureme = "11" diff --git a/compiler/rustc_data_structures/src/sharded.rs b/compiler/rustc_data_structures/src/sharded.rs index 162dbd234d676..4b02b18346069 100644 --- a/compiler/rustc_data_structures/src/sharded.rs +++ b/compiler/rustc_data_structures/src/sharded.rs @@ -3,7 +3,7 @@ use crate::fx::{FxHashMap, FxHasher}; use crate::sync::{is_dyn_thread_safe, CacheAligned}; use crate::sync::{Lock, LockGuard, Mode}; #[cfg(parallel_compiler)] -use itertools::Either; +use either::Either; use std::borrow::Borrow; use std::collections::hash_map::RawEntryMut; use std::hash::{Hash, Hasher}; diff --git a/compiler/rustc_data_structures/src/sso/map.rs b/compiler/rustc_data_structures/src/sso/map.rs index 04e359a54708c..2ef4a2ccd8476 100644 --- a/compiler/rustc_data_structures/src/sso/map.rs +++ b/compiler/rustc_data_structures/src/sso/map.rs @@ -1,6 +1,6 @@ use crate::fx::FxHashMap; use arrayvec::ArrayVec; -use itertools::Either; +use either::Either; use std::fmt; use std::hash::Hash; use std::ops::Index;