From e52b231f510fb93c3b7f90a4b3583b77b6b2f3d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 15 Jul 2025 22:25:49 +0000 Subject: [PATCH] Detect unmet bound error caused by lack of perfect derives When encountering an unmet bound E0599 error originating in a derived trait impl, provide additional context: ``` error[E0599]: the method `clone` exists for struct `S`, but its trait bounds were not satisfied --> $DIR/lack-of-perfect-derives.rs:27:16 | LL | struct S(Arc, Rc, Arc, ()); | ----------------- method `clone` not found for this struct because it doesn't satisfy `S: Clone` ... LL | struct X; | -------- doesn't satisfy `X: Clone` ... LL | let s2 = s.clone(); | ^^^^^ method cannot be called on `S` due to unsatisfied trait bounds | note: trait bound `X: Clone` was not satisfied --> $DIR/lack-of-perfect-derives.rs:4:10 | LL | #[derive(Clone, PartialEq)] | ^^^^^ unsatisfied trait bound introduced in this `derive` macro LL | struct S(Arc, Rc, Arc, ()); | - - implicit unsatisfied bound on this type parameter | | | implicit unsatisfied bound on this type parameter = note: `#[derive(Clone)]` introduces `where`-bounds requiring every type parameter to implement `Clone`, even when not strictly necessary = help: you can manually implement `Clone` with more targeted bounds: `impl Clone for S where Arc: Clone, Rc: Clone, Arc: Clone { /* .. */ }` help: consider annotating `X` with `#[derive(Clone)]` | LL + #[derive(Clone)] LL | struct X; | ``` We now add a `note` mentioning that `derive` introduces `where`-bounds on the type parameters. If the derived bounds would be different under "perfect derives", meaning a relaxation of the impl bounds could make the impl apply, then we mention (an approximate of) the impl that can be manually written. --- .../rustc_hir_typeck/src/method/suggest.rs | 294 ++++++++++++++++-- .../derives/derive-assoc-type-not-impl.stderr | 4 + .../deriving-with-repr-packed-2.stderr | 4 + tests/ui/derives/lack-of-perfect-derives.rs | 39 +++ .../ui/derives/lack-of-perfect-derives.stderr | 240 ++++++++++++++ tests/ui/union/union-derive-clone.stderr | 3 + 6 files changed, 565 insertions(+), 19 deletions(-) create mode 100644 tests/ui/derives/lack-of-perfect-derives.rs create mode 100644 tests/ui/derives/lack-of-perfect-derives.stderr diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 2815621ffdecf..da3b74e2e5c76 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -10,7 +10,7 @@ use std::path::PathBuf; use hir::Expr; use rustc_ast::ast::Mutability; use rustc_attr_data_structures::{AttributeKind, find_attr}; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; @@ -19,7 +19,7 @@ use rustc_errors::{ }; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; use rustc_hir::lang_items::LangItem; use rustc_hir::{self as hir, ExprKind, HirId, Node, PathSegment, QPath}; use rustc_infer::infer::{BoundRegionConversionTime, RegionVariableOrigin}; @@ -1082,19 +1082,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Find all the requirements that come from a local `impl` block. let mut skip_list: UnordSet<_> = Default::default(); let mut spanned_predicates = FxIndexMap::default(); + let mut bounds = FxIndexMap::>::default(); + let mut impl_def_id_and_mac = None; + for (p, parent_p, cause) in unsatisfied_predicates { // Extract the predicate span and parent def id of the cause, // if we have one. - let (item_def_id, cause_span) = match cause.as_ref().map(|cause| cause.code()) { - Some(ObligationCauseCode::ImplDerived(data)) => { - (data.impl_or_alias_def_id, data.span) - } - Some( - ObligationCauseCode::WhereClauseInExpr(def_id, span, _, _) - | ObligationCauseCode::WhereClause(def_id, span), - ) if !span.is_dummy() => (*def_id, *span), - _ => continue, - }; + let (item_def_id, cause_span, pred_index) = + match cause.as_ref().map(|cause| cause.code()) { + Some(ObligationCauseCode::ImplDerived(data)) => { + (data.impl_or_alias_def_id, data.span, data.impl_def_predicate_index) + } + Some( + ObligationCauseCode::WhereClauseInExpr(def_id, span, _, _) + | ObligationCauseCode::WhereClause(def_id, span), + ) if !span.is_dummy() => (*def_id, *span, None), + _ => continue, + }; // Don't point out the span of `WellFormed` predicates. if !matches!( @@ -1112,14 +1116,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(Node::Item(hir::Item { kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), .. - })) if matches!( - self_ty.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) || matches!( - of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind), - Some(ExpnKind::Macro(MacroKind::Derive, _)) - ) => + })) if let ( + ExpnKind::Macro(MacroKind::Derive, mac), _) + | (_, Some(ExpnKind::Macro(MacroKind::Derive, mac)) + ) = ( + self_ty.span.ctxt().outer_expn_data().kind, + of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind), + ) => { + impl_def_id_and_mac = Some((item_def_id, mac)); let span = self_ty.span.ctxt().outer_expn_data().call_site; let entry = spanned_predicates.entry(span); let entry = entry.or_insert_with(|| { @@ -1130,6 +1135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, "unsatisfied trait bound introduced in this `derive` macro", )); + self.extract_derive_bounds(self_ty, &mut entry.1, &mut bounds, pred_index, item_def_id); entry.2.push(p); skip_list.insert(p); } @@ -1250,6 +1256,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { unsatisfied_bounds = true; } + if let Some((impl_def_id, mac)) = impl_def_id_and_mac { + self.suggest_manual_impl_for_derive(&mut err, impl_def_id, mac, &bounds); + } + let mut suggested_bounds = UnordSet::default(); // The requirements that didn't have an `impl` span to show. let mut bound_list = unsatisfied_predicates @@ -1752,6 +1762,226 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit() } + fn extract_derive_bounds( + &self, + self_ty: &hir::Ty<'_>, + entry: &mut FxIndexSet<(Span, &str)>, + bounds: &mut FxIndexMap>, + pred_index: Option, + item_def_id: DefId, + ) { + let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = self_ty.kind else { return }; + let hir::def::Res::Def(_, def_id) = path.res else { return }; + let Some(index) = pred_index else { return }; + let Some((original_predicate, _)) = + self.tcx.predicates_of(item_def_id).predicates.get(index) + else { + return; + }; + let ty::ClauseKind::Trait(d) = original_predicate.kind().skip_binder() else { return }; + let self_ty = d.self_ty(); + let Some(Node::Item(item)) = self.tcx.hir_get_if_local(def_id) else { return }; + let ty_generics = self.tcx.generics_of(def_id); + for param in &ty_generics.own_params { + if let ty::GenericParamDefKind::Type { synthetic: false, .. } = param.kind { + let _ = bounds.entry(param.name.to_string()).or_default(); + } + if let ty::Param(p) = self_ty.kind() + && p.index == param.index + && p.name == param.name + { + // For every generic type parameter that had an implicit bound that + // couldn't be met, we point at it. If the implicit bound was met, we + // don't point at it as it is currently irrelevant. + let span = self.tcx.def_span(param.def_id); + entry.insert((span, "implicit unsatisfied bound on this type parameter")); + } + } + + let variants = match item.kind { + hir::ItemKind::Struct(_, _hir_generics, variant) => vec![variant], + hir::ItemKind::Enum(_, _hir_generics, def) => { + def.variants.iter().map(|v| v.data).collect() + } + _ => return, + }; + + for variant in variants { + for field in variant.fields() { + // For every field in the type, we look for those that reference to a type + // parameter of the type. For something like `struct Foo(Arc)`, we will + // find `Arc`. We check if `Arc` has an implementation for the macro being + // derived, and if so if it references `T` in any way. For example, when + // deriving `Clone`, `Arc: Clone` doesn't impose `T: Clone`, but when + // deriving `PartialEq`, `Arc: PartialEq` does impose `T: PartialEq`. With + // this information we can figure out what the appropriate "perfect derive" + // bounds could have been, to suggest a manual implementation of the trait. + + let mut my_visitor = TypeParamFinder { + places_found: vec![], + target: self_ty, + tcx: self.tcx, + generics: ty_generics, + }; + my_visitor.visit_ty_unambig(field.ty); + if !my_visitor.places_found.is_empty() { + // The current field references the type parameter being bound in the + // original predicate. + + let mut impls_trait_without_bounds = false; + match field.ty.kind { + hir::TyKind::Path(QPath::Resolved( + _, + hir::Path { res: hir::def::Res::Def(_, ty_def_id), .. }, + )) => { + // We have something like `struct Foo(Arc);`. We check if + // `impl Trait for Arc` places a bound on `K`. If it doesn't, + // it means that the perfect derive implementation wouldn't impose + // a bound on `T`, but rather on `Arc:: Trait`. + impls_trait_without_bounds = + self.type_implements_trait_without_bounds(*ty_def_id, d.def_id()); + } + _ => {} + } + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(field.ty.span) { + if impls_trait_without_bounds { + bounds + .entry(self_ty.to_string()) + .or_default() + .insert("placeholder".to_string()); + } else { + bounds + .entry(self_ty.to_string()) + .or_default() + .insert(format!("{snippet}")); + } + } + } + } + } + } + + /// Whether there is any `ty_def_id` implementation for `trait_def_id` that has no trait bound + /// on its type parameters that constrains it to `trait_def_id`. + /// + /// For example, `Option: Clone` has a `T: Clone` bound, hence this method would return + /// `false`, while `Arc: Clone` doesn't have a `T: Clone` bound, so this + /// method would return `true`. + fn type_implements_trait_without_bounds(&self, ty_def_id: DefId, trait_def_id: DefId) -> bool { + let generics = self.tcx.generics_of(ty_def_id); + // We skip defaulted params (like `A` in `struct Arc`) as a proxy for + // for that param not introducing additional bounds. This is not accurate, but good enough + // for std types like `Arc: Clone` (and it only affects suggestions). + let defaulted_params: FxHashSet = generics + .own_params + .iter() + .filter_map(|param| param.default_value(self.tcx).map(|_| param.name)) + .collect(); + let mut implements = false; + // Find `impl trait_def_id for ty_def_id`. + for impl_def_id in self.tcx.all_impls(trait_def_id) { + let Some(header) = self.tcx.impl_trait_header(impl_def_id) else { continue }; + let ty::Adt(def, _args) = header.trait_ref.skip_binder().self_ty().kind() else { + continue; + }; + if def.did() != ty_def_id { + // This impl isn't for `ty_def_id`. + continue; + } + // Look at all the bounds on the impl and see if there are any bounds for the same trait + // being implemented (like `impl Clone for Type where T: Clone`). + for (pred, _span) in self.tcx.predicates_of(impl_def_id).predicates { + let Some(trait_clause) = pred.as_trait_clause() else { continue }; + let clause = trait_clause.skip_binder(); + if clause.def_id() != trait_def_id { + continue; + } + let ty::Param(param) = clause.self_ty().kind() else { continue }; + if defaulted_params.contains(¶m.name) { + continue; + } + // We found a `Param: Trait` bound on the impl of `Trait`. + return false; + } + implements = true; + } + implements + } + + /// Given the `DefId` of a derived trait's impl, the macro name, and the evaluated bounds for + /// what "perfect derives" would have used in that impl, provide a message explaining the lack + /// of perfect derives and what the manual impl could look like. + fn suggest_manual_impl_for_derive( + &self, + err: &mut Diag<'_>, + impl_def_id: DefId, + mac: Symbol, + bounds: &FxIndexMap>, + ) { + // Whether all of the type parameters are bounded to `mac`. + let mut all_params_bounded_directly = true; + let mut where_bounds = bounds + .iter() + .filter_map(|(name, bounds)| { + if bounds.is_empty() || bounds.contains("direct") { + Some(format!("{name}: {mac}")) + } else { + if bounds.contains(name) { + // If we have `where Arc: Clone, T: Clone`, we only want the later. + Some(format!("{name}: {mac}")) + } else { + let bounds: Vec = + bounds.iter().filter(|b| *b != "placeholder").cloned().collect(); + all_params_bounded_directly = false; + if !bounds.is_empty() { + Some( + bounds + .iter() + .map(|bound| format!("{bound}: {mac}")) + .collect::>() + .join(", "), + ) + } else { + None + } + } + } + }) + .collect::>() + .join(", "); + if !where_bounds.is_empty() { + where_bounds = format!("where {where_bounds} "); + } + let msg = format!( + "`#[derive({mac})]` introduces `where`-bounds requiring every type parameter to \ + implement `{mac}`" + ); + if let Some(header) = self.tcx.impl_trait_header(impl_def_id) + && !all_params_bounded_directly + { + err.note(format!("{msg}, even when not strictly necessary")); + let impl_args = format!( + "{}", + self.tcx + .generics_of(impl_def_id) + .own_params + .iter() + .map(|param| param.name.to_string()) + .collect::>() + .join(", ") + ); + + let self_ty = header.trait_ref.skip_binder().self_ty(); + let msg = format!( + "you can manually implement `{mac}` with more targeted bounds: \ + `impl<{impl_args}> {mac} for {self_ty} {where_bounds}{{ /* .. */ }}`", + ); + err.help(msg); + } else { + err.note(msg); + } + } + /// If the predicate failure is caused by an unmet bound on a tuple, recheck if the bound would /// succeed if all the types on the tuple had no borrows. This is a common problem for libraries /// like Bevy and ORMs, which rely heavily on traits being implemented on tuples. @@ -4462,3 +4692,29 @@ fn print_disambiguation_help<'tcx>( }, ) } + +struct TypeParamFinder<'tcx> { + places_found: Vec, + target: Ty<'tcx>, + tcx: TyCtxt<'tcx>, + generics: &'tcx ty::Generics, +} + +impl<'v> Visitor<'v> for TypeParamFinder<'v> { + fn visit_ty(&mut self, t: &'v hir::Ty<'v, hir::AmbigArg>) { + if let hir::TyKind::Path(QPath::Resolved( + _, + hir::Path { res: hir::def::Res::Def(hir::def::DefKind::TyParam, def_id), .. }, + )) = &t.kind + { + let index = self.generics.param_def_id_to_index[def_id]; + let name = self.tcx.item_name(*def_id); + let ty = Ty::new_param(self.tcx, index, name); + if ty == self.target { + self.places_found.push(t.span); + return; + } + } + hir::intravisit::walk_ty(self, t); + } +} diff --git a/tests/ui/derives/derive-assoc-type-not-impl.stderr b/tests/ui/derives/derive-assoc-type-not-impl.stderr index 13ba80243a5eb..7c7eba5cd1e26 100644 --- a/tests/ui/derives/derive-assoc-type-not-impl.stderr +++ b/tests/ui/derives/derive-assoc-type-not-impl.stderr @@ -15,6 +15,10 @@ note: trait bound `NotClone: Clone` was not satisfied | LL | #[derive(Clone)] | ^^^^^ unsatisfied trait bound introduced in this `derive` macro +LL | struct Bar { + | - implicit unsatisfied bound on this type parameter + = note: `#[derive(Clone)]` introduces `where`-bounds requiring every type parameter to implement `Clone`, even when not strictly necessary + = help: you can manually implement `Clone` with more targeted bounds: `impl Clone for Bar where T::X: Clone { /* .. */ }` help: consider annotating `NotClone` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/derives/deriving-with-repr-packed-2.stderr b/tests/ui/derives/deriving-with-repr-packed-2.stderr index b62c67d9a9da1..d1a68c7851c91 100644 --- a/tests/ui/derives/deriving-with-repr-packed-2.stderr +++ b/tests/ui/derives/deriving-with-repr-packed-2.stderr @@ -25,6 +25,10 @@ note: the following trait bounds were not satisfied: | LL | #[derive(Copy, Clone, Default, PartialEq, Eq)] | ^^^^^ unsatisfied trait bound introduced in this `derive` macro +LL | #[repr(packed)] +LL | pub struct Foo(T, T, T); + | - implicit unsatisfied bound on this type parameter + = note: `#[derive(Clone)]` introduces `where`-bounds requiring every type parameter to implement `Clone` help: consider annotating `NonCopy` with `#[derive(Clone, Copy)]` | LL + #[derive(Clone, Copy)] diff --git a/tests/ui/derives/lack-of-perfect-derives.rs b/tests/ui/derives/lack-of-perfect-derives.rs new file mode 100644 index 0000000000000..101291782d8ad --- /dev/null +++ b/tests/ui/derives/lack-of-perfect-derives.rs @@ -0,0 +1,39 @@ +use std::rc::Rc; +use std::sync::Arc; +// Would work with perfect derives +#[derive(Clone, PartialEq)] +struct S(Arc, Rc, Arc, ()); + +// Wouldn't work with perfect derives, as T, K and V are used as fields directly +#[derive(Clone, PartialEq)] +struct N(Arc, T, Rc, K, Arc, V, ()); + +// Wouldn't work with perfect derives +#[derive(Clone, PartialEq)] +struct Z(T, K, Option); + +struct X; +#[derive(Clone, PartialEq)] +struct Y; + +fn foo() { + let s = S(Arc::new(X), Rc::new(X), Arc::new(X), ()); + let s2 = S(Arc::new(X), Rc::new(X), Arc::new(X), ()); + // FIXME(estebank): the current diagnostics for `==` don't have the same amount of context. + let _ = s == s2; //~ ERROR E0369 +} +fn main() { + let s = S(Arc::new(X), Rc::new(X), Arc::new(Y), ()); + let s2 = s.clone(); //~ ERROR E0599 + let s = S(Arc::new(X), Rc::new(X), Arc::new(X), ()); + let s2 = s.clone(); //~ ERROR E0599 + // FIXME(estebank): using `PartialEq::eq` instead of `==` because the later diagnostic doesn't + // currently have the same amount of context. + s.eq(s2); //~ ERROR E0599 + let n = N(Arc::new(X), X, Rc::new(Y), Y, Arc::new(X), X, ()); + let n2 = n.clone(); //~ ERROR E0599 + n.eq(n2); //~ ERROR E0599 + let z = Z(X, Y, Some(X)); + let z2 = z.clone(); //~ ERROR E0599 + n.eq(z2); //~ ERROR E0599 +} diff --git a/tests/ui/derives/lack-of-perfect-derives.stderr b/tests/ui/derives/lack-of-perfect-derives.stderr new file mode 100644 index 0000000000000..4892136871696 --- /dev/null +++ b/tests/ui/derives/lack-of-perfect-derives.stderr @@ -0,0 +1,240 @@ +error[E0369]: binary operation `==` cannot be applied to type `S` + --> $DIR/lack-of-perfect-derives.rs:23:15 + | +LL | let _ = s == s2; + | - ^^ -- S + | | + | S + | +note: an implementation of `PartialEq` might be missing for `X` + --> $DIR/lack-of-perfect-derives.rs:15:1 + | +LL | struct X; + | ^^^^^^^^ must implement `PartialEq` +help: consider annotating `X` with `#[derive(PartialEq)]` + | +LL + #[derive(PartialEq)] +LL | struct X; + | + +error[E0599]: the method `clone` exists for struct `S`, but its trait bounds were not satisfied + --> $DIR/lack-of-perfect-derives.rs:27:16 + | +LL | struct S(Arc, Rc, Arc, ()); + | ----------------- method `clone` not found for this struct because it doesn't satisfy `S: Clone` +... +LL | struct X; + | -------- doesn't satisfy `X: Clone` +... +LL | let s2 = s.clone(); + | ^^^^^ method cannot be called on `S` due to unsatisfied trait bounds + | +note: trait bound `X: Clone` was not satisfied + --> $DIR/lack-of-perfect-derives.rs:4:10 + | +LL | #[derive(Clone, PartialEq)] + | ^^^^^ unsatisfied trait bound introduced in this `derive` macro +LL | struct S(Arc, Rc, Arc, ()); + | - - implicit unsatisfied bound on this type parameter + | | + | implicit unsatisfied bound on this type parameter + = note: `#[derive(Clone)]` introduces `where`-bounds requiring every type parameter to implement `Clone`, even when not strictly necessary + = help: you can manually implement `Clone` with more targeted bounds: `impl Clone for S where V: Clone { /* .. */ }` +help: consider annotating `X` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct X; + | + +error[E0599]: the method `clone` exists for struct `S`, but its trait bounds were not satisfied + --> $DIR/lack-of-perfect-derives.rs:29:16 + | +LL | struct S(Arc, Rc, Arc, ()); + | ----------------- method `clone` not found for this struct because it doesn't satisfy `S: Clone` +... +LL | struct X; + | -------- doesn't satisfy `X: Clone` +... +LL | let s2 = s.clone(); + | ^^^^^ method cannot be called on `S` due to unsatisfied trait bounds + | +note: trait bound `X: Clone` was not satisfied + --> $DIR/lack-of-perfect-derives.rs:4:10 + | +LL | #[derive(Clone, PartialEq)] + | ^^^^^ unsatisfied trait bound introduced in this `derive` macro +LL | struct S(Arc, Rc, Arc, ()); + | - - - implicit unsatisfied bound on this type parameter + | | | + | | implicit unsatisfied bound on this type parameter + | implicit unsatisfied bound on this type parameter + = note: `#[derive(Clone)]` introduces `where`-bounds requiring every type parameter to implement `Clone`, even when not strictly necessary + = help: you can manually implement `Clone` with more targeted bounds: `impl Clone for S { /* .. */ }` +help: consider annotating `X` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct X; + | + +error[E0599]: the method `eq` exists for struct `S`, but its trait bounds were not satisfied + --> $DIR/lack-of-perfect-derives.rs:32:7 + | +LL | struct S(Arc, Rc, Arc, ()); + | ----------------- method `eq` not found for this struct because it doesn't satisfy `S: Iterator` or `S: PartialEq` +... +LL | struct X; + | -------- doesn't satisfy `X: PartialEq` +... +LL | s.eq(s2); + | ^^ method cannot be called on `S` due to unsatisfied trait bounds + | +note: trait bound `X: PartialEq` was not satisfied + --> $DIR/lack-of-perfect-derives.rs:4:17 + | +LL | #[derive(Clone, PartialEq)] + | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro +LL | struct S(Arc, Rc, Arc, ()); + | - - - implicit unsatisfied bound on this type parameter + | | | + | | implicit unsatisfied bound on this type parameter + | implicit unsatisfied bound on this type parameter + = note: `#[derive(PartialEq)]` introduces `where`-bounds requiring every type parameter to implement `PartialEq`, even when not strictly necessary + = help: you can manually implement `PartialEq` with more targeted bounds: `impl PartialEq for S where Arc: PartialEq, Rc: PartialEq, Arc: PartialEq { /* .. */ }` + = note: the following trait bounds were not satisfied: + `S: Iterator` + which is required by `&mut S: Iterator` +note: the trait `Iterator` must be implemented + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider annotating `X` with `#[derive(PartialEq)]` + | +LL + #[derive(PartialEq)] +LL | struct X; + | + +error[E0599]: the method `clone` exists for struct `N`, but its trait bounds were not satisfied + --> $DIR/lack-of-perfect-derives.rs:34:16 + | +LL | struct N(Arc, T, Rc, K, Arc, V, ()); + | ----------------- method `clone` not found for this struct because it doesn't satisfy `N: Clone` +... +LL | struct X; + | -------- doesn't satisfy `X: Clone` +... +LL | let n2 = n.clone(); + | ^^^^^ method cannot be called on `N` due to unsatisfied trait bounds + | +note: trait bound `X: Clone` was not satisfied + --> $DIR/lack-of-perfect-derives.rs:8:10 + | +LL | #[derive(Clone, PartialEq)] + | ^^^^^ unsatisfied trait bound introduced in this `derive` macro +LL | struct N(Arc, T, Rc, K, Arc, V, ()); + | - - implicit unsatisfied bound on this type parameter + | | + | implicit unsatisfied bound on this type parameter + = note: `#[derive(Clone)]` introduces `where`-bounds requiring every type parameter to implement `Clone` +help: consider annotating `X` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct X; + | + +error[E0599]: the method `eq` exists for struct `N`, but its trait bounds were not satisfied + --> $DIR/lack-of-perfect-derives.rs:35:7 + | +LL | struct N(Arc, T, Rc, K, Arc, V, ()); + | ----------------- method `eq` not found for this struct because it doesn't satisfy `N: Iterator` or `N: PartialEq` +... +LL | struct X; + | -------- doesn't satisfy `X: PartialEq` +... +LL | n.eq(n2); + | ^^ method cannot be called on `N` due to unsatisfied trait bounds + | +note: trait bound `X: PartialEq` was not satisfied + --> $DIR/lack-of-perfect-derives.rs:8:17 + | +LL | #[derive(Clone, PartialEq)] + | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro +LL | struct N(Arc, T, Rc, K, Arc, V, ()); + | - - implicit unsatisfied bound on this type parameter + | | + | implicit unsatisfied bound on this type parameter + = note: `#[derive(PartialEq)]` introduces `where`-bounds requiring every type parameter to implement `PartialEq` + = note: the following trait bounds were not satisfied: + `N: Iterator` + which is required by `&mut N: Iterator` +note: the trait `Iterator` must be implemented + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider annotating `X` with `#[derive(PartialEq)]` + | +LL + #[derive(PartialEq)] +LL | struct X; + | + +error[E0599]: the method `clone` exists for struct `Z`, but its trait bounds were not satisfied + --> $DIR/lack-of-perfect-derives.rs:37:16 + | +LL | struct Z(T, K, Option); + | ----------------- method `clone` not found for this struct because it doesn't satisfy `Z: Clone` +LL | +LL | struct X; + | -------- doesn't satisfy `X: Clone` +... +LL | let z2 = z.clone(); + | ^^^^^ method cannot be called on `Z` due to unsatisfied trait bounds + | +note: trait bound `X: Clone` was not satisfied + --> $DIR/lack-of-perfect-derives.rs:12:10 + | +LL | #[derive(Clone, PartialEq)] + | ^^^^^ unsatisfied trait bound introduced in this `derive` macro +LL | struct Z(T, K, Option); + | - - implicit unsatisfied bound on this type parameter + | | + | implicit unsatisfied bound on this type parameter + = note: `#[derive(Clone)]` introduces `where`-bounds requiring every type parameter to implement `Clone`, even when not strictly necessary + = help: you can manually implement `Clone` with more targeted bounds: `impl Clone for Z where T: Clone, K: Clone, Option: Clone { /* .. */ }` +help: consider annotating `X` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct X; + | + +error[E0599]: the method `eq` exists for struct `N`, but its trait bounds were not satisfied + --> $DIR/lack-of-perfect-derives.rs:38:7 + | +LL | struct N(Arc, T, Rc, K, Arc, V, ()); + | ----------------- method `eq` not found for this struct because it doesn't satisfy `N: Iterator` or `N: PartialEq` +... +LL | struct X; + | -------- doesn't satisfy `X: PartialEq` +... +LL | n.eq(z2); + | ^^ method cannot be called on `N` due to unsatisfied trait bounds + | +note: trait bound `X: PartialEq` was not satisfied + --> $DIR/lack-of-perfect-derives.rs:8:17 + | +LL | #[derive(Clone, PartialEq)] + | ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro +LL | struct N(Arc, T, Rc, K, Arc, V, ()); + | - - implicit unsatisfied bound on this type parameter + | | + | implicit unsatisfied bound on this type parameter + = note: `#[derive(PartialEq)]` introduces `where`-bounds requiring every type parameter to implement `PartialEq` + = note: the following trait bounds were not satisfied: + `N: Iterator` + which is required by `&mut N: Iterator` +note: the trait `Iterator` must be implemented + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider annotating `X` with `#[derive(PartialEq)]` + | +LL + #[derive(PartialEq)] +LL | struct X; + | + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0369, E0599. +For more information about an error, try `rustc --explain E0369`. diff --git a/tests/ui/union/union-derive-clone.stderr b/tests/ui/union/union-derive-clone.stderr index 679ab6a38e493..909168633bb61 100644 --- a/tests/ui/union/union-derive-clone.stderr +++ b/tests/ui/union/union-derive-clone.stderr @@ -29,6 +29,9 @@ note: trait bound `CloneNoCopy: Copy` was not satisfied | LL | #[derive(Clone, Copy)] | ^^^^^ unsatisfied trait bound introduced in this `derive` macro +LL | union U5 { + | - implicit unsatisfied bound on this type parameter + = note: `#[derive(Clone)]` introduces `where`-bounds requiring every type parameter to implement `Clone` help: consider annotating `CloneNoCopy` with `#[derive(Clone, Copy)]` | LL + #[derive(Clone, Copy)]