diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 985c45e225388..bc7c1aa3ea9e8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -185,10 +185,12 @@ use rustc_ast::ptr::P; use rustc_ast::{self as ast, BinOpKind, EnumDef, Expr, Generics, PatKind}; use rustc_ast::{GenericArg, GenericParamKind, VariantData}; use rustc_attr as attr; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::map_in_place::MapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; +use smallvec::{smallvec, SmallVec}; use ty::{Bounds, Path, Ptr, PtrTy, Self_, Ty}; @@ -337,6 +339,26 @@ struct TypeParameter { ty: P, } +impl TypeParameter { + /// Returns the `Ident`s that comprise the `Path` for this type parameter if it is unqualified + /// and has no bound generic params or segments with generic arguments. + fn to_simple_path(&self) -> Option> { + let TypeParameter { bound_generic_params, ty } = self; + if !bound_generic_params.is_empty() { + return None; + } + + let ast::TyKind::Path(None, path) = &ty.kind else { return None }; + + if path.segments.iter().any(|seg| seg.args.is_some()) { + return None; + } + + let path = path.segments.iter().map(|seg| seg.ident).collect(); + Some(path) + } +} + /// This method helps to extract all the type parameters referenced from a /// type. For a type parameter ``, it looks for either a `TyPath` that /// is not global and starts with `T`, or a `TyQPath`. @@ -636,49 +658,52 @@ impl<'a> TraitDef<'a> { } })); - { - // Extra scope required here so ty_params goes out of scope before params is moved - - let mut ty_params = params - .iter() - .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) - .peekable(); - - if ty_params.peek().is_some() { - let ty_param_names: Vec = - ty_params.map(|ty_param| ty_param.ident.name).collect(); - - for field_ty in field_tys { - let field_ty_params = find_type_parameters(&field_ty, &ty_param_names, cx); - - for field_ty_param in field_ty_params { - // if we have already handled this type, skip it - if let ast::TyKind::Path(_, ref p) = field_ty_param.ty.kind { - if p.segments.len() == 1 - && ty_param_names.contains(&p.segments[0].ident.name) - { - continue; - }; - } - let mut bounds: Vec<_> = self - .additional_bounds - .iter() - .map(|p| cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))) - .collect(); + // If this type declaration has type parameters, look for any mention of types *derived* from + // those parameters in the declaration (e.g. `T::Item`). Those derived types need bounds as + // well. - // require the current trait - bounds.push(cx.trait_bound(trait_path.clone())); + let mut ty_params = params + .iter() + .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) + .peekable(); + + if ty_params.peek().is_some() { + let ty_param_names: Vec = + ty_params.clone().map(|ty_param| ty_param.ident.name).collect(); - let predicate = ast::WhereBoundPredicate { - span: self.span, - bound_generic_params: field_ty_param.bound_generic_params, - bounded_ty: field_ty_param.ty, - bounds, - }; + let mut seen_simple_field_ty_params: FxHashSet<_> = + ty_params.map(|ty_param| smallvec![ty_param.ident]).collect(); - let predicate = ast::WherePredicate::BoundPredicate(predicate); - where_clause.predicates.push(predicate); + for field_ty in field_tys { + let field_ty_params = find_type_parameters(&field_ty, &ty_param_names, cx); + + for field_ty_param in field_ty_params { + // if we have already handled this type, skip it + if let Some(field_ty_param_path) = field_ty_param.to_simple_path() { + let changed = seen_simple_field_ty_params.insert(field_ty_param_path); + if !changed { + continue; + } } + + let mut bounds: Vec<_> = self + .additional_bounds + .iter() + .map(|p| cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))) + .collect(); + + // require the current trait + bounds.push(cx.trait_bound(trait_path.clone())); + + let predicate = ast::WhereBoundPredicate { + span: self.span, + bound_generic_params: field_ty_param.bound_generic_params, + bounded_ty: field_ty_param.ty, + bounds, + }; + + let predicate = ast::WherePredicate::BoundPredicate(predicate); + where_clause.predicates.push(predicate); } } } diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 8c3ef2864f485..3cbe9a07482a1 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -6,6 +6,7 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] +#![feature(let_else)] #![feature(nll)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)]