From ee713f3d4350c75bae85749619b0e1508f433e02 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 29 Sep 2022 21:22:34 +0000 Subject: [PATCH] Check generic argument compatibility when projecting assoc ty --- .../src/traits/project.rs | 42 ++++++++++++++++++- .../generic-associated-types/issue-102114.rs | 16 +++++++ .../issue-102114.stderr | 12 ++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/generic-associated-types/issue-102114.rs create mode 100644 src/test/ui/generic-associated-types/issue-102114.stderr diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 07b65a9748d7c..29172d6caaff6 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2146,10 +2146,10 @@ fn confirm_impl_candidate<'cx, 'tcx>( } else { ty.map_bound(|ty| ty.into()) }; - if substs.len() != tcx.generics_of(assoc_ty.item.def_id).count() { + if !check_substs_compatible(tcx, &assoc_ty.item, substs) { let err = tcx.ty_error_with_message( obligation.cause.span, - "impl item and trait item have different parameter counts", + "impl item and trait item have different parameters", ); Progress { term: err.into(), obligations: nested } } else { @@ -2158,6 +2158,44 @@ fn confirm_impl_candidate<'cx, 'tcx>( } } +// Verify that the trait item and its implementation have compatible substs lists +fn check_substs_compatible<'tcx>( + tcx: TyCtxt<'tcx>, + assoc_ty: &ty::AssocItem, + substs: ty::SubstsRef<'tcx>, +) -> bool { + fn check_substs_compatible_inner<'tcx>( + tcx: TyCtxt<'tcx>, + generics: &'tcx ty::Generics, + args: &'tcx [ty::GenericArg<'tcx>], + ) -> bool { + if generics.count() != args.len() { + return false; + } + + let (parent_args, own_args) = args.split_at(generics.parent_count); + + if let Some(parent) = generics.parent + && let parent_generics = tcx.generics_of(parent) + && !check_substs_compatible_inner(tcx, parent_generics, parent_args) { + return false; + } + + for (param, arg) in std::iter::zip(&generics.params, own_args) { + match (¶m.kind, arg.unpack()) { + (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_)) + | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_)) + | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {} + _ => return false, + } + } + + true + } + + check_substs_compatible_inner(tcx, tcx.generics_of(assoc_ty.def_id), substs.as_slice()) +} + fn confirm_impl_trait_in_trait_candidate<'tcx>( selcx: &mut SelectionContext<'_, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, diff --git a/src/test/ui/generic-associated-types/issue-102114.rs b/src/test/ui/generic-associated-types/issue-102114.rs new file mode 100644 index 0000000000000..de31737efef52 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-102114.rs @@ -0,0 +1,16 @@ +trait A { + type B<'b>; + fn a() -> Self::B<'static>; +} + +struct C; + +struct Wrapper(T); + +impl A for C { + type B = Wrapper; + //~^ ERROR type `B` has 1 type parameter but its trait declaration has 0 type parameters + fn a() -> Self::B<'static> {} +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-102114.stderr b/src/test/ui/generic-associated-types/issue-102114.stderr new file mode 100644 index 0000000000000..8e41dee54d7e4 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-102114.stderr @@ -0,0 +1,12 @@ +error[E0049]: type `B` has 1 type parameter but its trait declaration has 0 type parameters + --> $DIR/issue-102114.rs:11:12 + | +LL | type B<'b>; + | -- expected 0 type parameters +... +LL | type B = Wrapper; + | ^ found 1 type parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0049`.