diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 6d78a863d54cf..a83924d4636c5 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -7,10 +7,10 @@ use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_infer::traits::util; -use rustc_middle::ty; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; use rustc_middle::ty::util::ExplicitSelf; +use rustc_middle::ty::{self, DefIdTree}; use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; @@ -48,6 +48,10 @@ crate fn compare_impl_method<'tcx>( return; } + if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m) { + return; + } + if let Err(_) = compare_number_of_method_arguments(tcx, impl_m, impl_m_span, trait_m, trait_item_span) { @@ -62,10 +66,6 @@ crate fn compare_impl_method<'tcx>( { return; } - - if let Err(_) = compare_const_param_types(tcx, impl_m, trait_m, trait_item_span) { - return; - } } fn compare_predicate_entailment<'tcx>( @@ -579,6 +579,27 @@ fn compare_self_type<'tcx>( Ok(()) } +/// Checks that the number of generics on a given assoc item in a trait impl is the same +/// as the number of generics on the respective assoc item in the trait definition. +/// +/// For example this code emits the errors in the following code: +/// ``` +/// trait Trait { +/// fn foo(); +/// type Assoc; +/// } +/// +/// impl Trait for () { +/// fn foo() {} +/// //~^ error +/// type Assoc = u32; +/// //~^ error +/// } +/// ``` +/// +/// Notably this does not error on `foo` implemented as `foo` or +/// `foo` implemented as `foo`. This is handled in +/// [`compare_generic_param_kinds`]. This function also does not handle lifetime parameters fn compare_number_of_generics<'tcx>( tcx: TyCtxt<'tcx>, impl_: &ty::AssocItem, @@ -589,6 +610,15 @@ fn compare_number_of_generics<'tcx>( let trait_own_counts = tcx.generics_of(trait_.def_id).own_counts(); let impl_own_counts = tcx.generics_of(impl_.def_id).own_counts(); + // This avoids us erroring on `foo` implemented as `foo` as this is implemented + // in `compare_generic_param_kinds` which will give a nicer error message than something like: + // "expected 1 type parameter, found 0 type parameters" + if (trait_own_counts.types + trait_own_counts.consts) + == (impl_own_counts.types + impl_own_counts.consts) + { + return Ok(()); + } + let matchings = [ ("type", trait_own_counts.types, impl_own_counts.types), ("const", trait_own_counts.consts, impl_own_counts.consts), @@ -914,60 +944,93 @@ fn compare_synthetic_generics<'tcx>( if let Some(reported) = error_found { Err(reported) } else { Ok(()) } } -fn compare_const_param_types<'tcx>( +/// Checks that all parameters in the generics of a given assoc item in a trait impl have +/// the same kind as the respective generic parameter in the trait def. +/// +/// For example all 4 errors in the following code are emitted here: +/// ``` +/// trait Foo { +/// fn foo(); +/// type bar; +/// fn baz(); +/// type blah; +/// } +/// +/// impl Foo for () { +/// fn foo() {} +/// //~^ error +/// type bar {} +/// //~^ error +/// fn baz() {} +/// //~^ error +/// type blah = u32; +/// //~^ error +/// } +/// ``` +/// +/// This function does not handle lifetime parameters +fn compare_generic_param_kinds<'tcx>( tcx: TyCtxt<'tcx>, - impl_m: &ty::AssocItem, - trait_m: &ty::AssocItem, - trait_item_span: Option, + impl_item: &ty::AssocItem, + trait_item: &ty::AssocItem, ) -> Result<(), ErrorGuaranteed> { - let const_params_of = |def_id| { - tcx.generics_of(def_id).params.iter().filter_map(|param| match param.kind { - GenericParamDefKind::Const { .. } => Some(param.def_id), - _ => None, + assert_eq!(impl_item.kind, trait_item.kind); + + let ty_const_params_of = |def_id| { + tcx.generics_of(def_id).params.iter().filter(|param| { + matches!( + param.kind, + GenericParamDefKind::Const { .. } | GenericParamDefKind::Type { .. } + ) }) }; - let const_params_impl = const_params_of(impl_m.def_id); - let const_params_trait = const_params_of(trait_m.def_id); - - for (const_param_impl, const_param_trait) in iter::zip(const_params_impl, const_params_trait) { - let impl_ty = tcx.type_of(const_param_impl); - let trait_ty = tcx.type_of(const_param_trait); - if impl_ty != trait_ty { - let (impl_span, impl_ident) = match tcx.hir().get_if_local(const_param_impl) { - Some(hir::Node::GenericParam(hir::GenericParam { span, name, .. })) => ( - span, - match name { - hir::ParamName::Plain(ident) => Some(ident), - _ => None, - }, - ), - other => bug!( - "expected GenericParam, found {:?}", - other.map_or_else(|| "nothing".to_string(), |n| format!("{:?}", n)) - ), - }; - let trait_span = match tcx.hir().get_if_local(const_param_trait) { - Some(hir::Node::GenericParam(hir::GenericParam { span, .. })) => Some(span), - _ => None, - }; + + for (param_impl, param_trait) in + iter::zip(ty_const_params_of(impl_item.def_id), ty_const_params_of(trait_item.def_id)) + { + use GenericParamDefKind::*; + if match (¶m_impl.kind, ¶m_trait.kind) { + (Const { .. }, Const { .. }) + if tcx.type_of(param_impl.def_id) != tcx.type_of(param_trait.def_id) => + { + true + } + (Const { .. }, Type { .. }) | (Type { .. }, Const { .. }) => true, + // this is exhaustive so that anyone adding new generic param kinds knows + // to make sure this error is reported for them. + (Const { .. }, Const { .. }) | (Type { .. }, Type { .. }) => false, + (Lifetime { .. }, _) | (_, Lifetime { .. }) => unreachable!(), + } { + let param_impl_span = tcx.def_span(param_impl.def_id); + let param_trait_span = tcx.def_span(param_trait.def_id); + let mut err = struct_span_err!( tcx.sess, - *impl_span, + param_impl_span, E0053, - "method `{}` has an incompatible const parameter type for trait", - trait_m.name - ); - err.span_note( - trait_span.map_or_else(|| trait_item_span.unwrap_or(*impl_span), |span| *span), - &format!( - "the const parameter{} has type `{}`, but the declaration \ - in trait `{}` has type `{}`", - &impl_ident.map_or_else(|| "".to_string(), |ident| format!(" `{ident}`")), - impl_ty, - tcx.def_path_str(trait_m.def_id), - trait_ty - ), + "{} `{}` has an incompatible generic parameter for trait `{}`", + assoc_item_kind_str(&impl_item), + trait_item.name, + &tcx.def_path_str(tcx.parent(trait_item.def_id)) ); + + let make_param_message = |prefix: &str, param: &ty::GenericParamDef| match param.kind { + Const { .. } => { + format!("{} const parameter of type `{}`", prefix, tcx.type_of(param.def_id)) + } + Type { .. } => format!("{} type parameter", prefix), + Lifetime { .. } => unreachable!(), + }; + + let trait_header_span = tcx.def_ident_span(tcx.parent(trait_item.def_id)).unwrap(); + err.span_label(trait_header_span, ""); + err.span_label(param_trait_span, make_param_message("expected", param_trait)); + + let impl_header_span = + tcx.sess.source_map().guess_head_span(tcx.def_span(tcx.parent(impl_item.def_id))); + err.span_label(impl_header_span, ""); + err.span_label(param_impl_span, make_param_message("found", param_impl)); + let reported = err.emit(); return Err(reported); } @@ -1095,6 +1158,8 @@ crate fn compare_ty_impl<'tcx>( let _: Result<(), ErrorGuaranteed> = (|| { compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?; + compare_generic_param_kinds(tcx, impl_ty, trait_ty)?; + let sp = tcx.def_span(impl_ty.def_id); compare_type_predicate_entailment(tcx, impl_ty, sp, trait_ty, impl_trait_ref)?; diff --git a/src/test/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.rs b/src/test/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.rs new file mode 100644 index 0000000000000..5c9323261a973 --- /dev/null +++ b/src/test/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.rs @@ -0,0 +1,41 @@ +trait Trait { + fn foo() {} +} +impl Trait for () { + fn foo() {} + //~^ error: method `foo` has an incompatible generic parameter for trait +} + +trait Other { + fn bar() {} +} +impl Other for () { + fn bar() {} + //~^ error: method `bar` has an incompatible generic parameter for trait +} + +trait Uwu { + fn baz() {} +} +impl Uwu for () { + fn baz() {} + //~^ error: method `baz` has an incompatible generic parameter for trait +} + +trait Aaaaaa { + fn bbbb() {} +} +impl Aaaaaa for () { + fn bbbb() {} + //~^ error: method `bbbb` has an incompatible generic parameter for trait +} + +trait Names { + fn abcd() {} +} +impl Names for () { + fn abcd() {} + //~^ error: method `abcd` has an incompatible generic parameter for trait +} + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.stderr b/src/test/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.stderr new file mode 100644 index 0000000000000..3455f2c8ea97b --- /dev/null +++ b/src/test/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.stderr @@ -0,0 +1,68 @@ +error[E0053]: method `foo` has an incompatible generic parameter for trait `Trait` + --> $DIR/mismatched_ty_const_in_trait_impl.rs:5:12 + | +LL | trait Trait { + | ----- +LL | fn foo() {} + | - expected type parameter +LL | } +LL | impl Trait for () { + | ----------------- +LL | fn foo() {} + | ^^^^^^^^^^^^ found const parameter of type `u64` + +error[E0053]: method `bar` has an incompatible generic parameter for trait `Other` + --> $DIR/mismatched_ty_const_in_trait_impl.rs:13:12 + | +LL | trait Other { + | ----- +LL | fn bar() {} + | ----------- expected const parameter of type `u8` +LL | } +LL | impl Other for () { + | ----------------- +LL | fn bar() {} + | ^ found type parameter + +error[E0053]: method `baz` has an incompatible generic parameter for trait `Uwu` + --> $DIR/mismatched_ty_const_in_trait_impl.rs:21:12 + | +LL | trait Uwu { + | --- +LL | fn baz() {} + | ------------ expected const parameter of type `u32` +LL | } +LL | impl Uwu for () { + | --------------- +LL | fn baz() {} + | ^^^^^^^^^^^^ found const parameter of type `i32` + +error[E0053]: method `bbbb` has an incompatible generic parameter for trait `Aaaaaa` + --> $DIR/mismatched_ty_const_in_trait_impl.rs:29:13 + | +LL | trait Aaaaaa { + | ------ +LL | fn bbbb() {} + | ------------ expected const parameter of type `u32` +LL | } +LL | impl Aaaaaa for () { + | ------------------ +LL | fn bbbb() {} + | ^ found type parameter + +error[E0053]: method `abcd` has an incompatible generic parameter for trait `Names` + --> $DIR/mismatched_ty_const_in_trait_impl.rs:37:13 + | +LL | trait Names { + | ----- +LL | fn abcd() {} + | - expected type parameter +LL | } +LL | impl Names for () { + | ----------------- +LL | fn abcd() {} + | ^^^^^^^^^^^^ found const parameter of type `u32` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0053`. diff --git a/src/test/ui/const-generics/issues/issue-86820.rs b/src/test/ui/const-generics/issues/issue-86820.rs index 04650403c6baf..ae4bd943fd415 100644 --- a/src/test/ui/const-generics/issues/issue-86820.rs +++ b/src/test/ui/const-generics/issues/issue-86820.rs @@ -1,6 +1,6 @@ // Regression test for the ICE described in #86820. -#![allow(unused,dead_code)] +#![allow(unused, dead_code)] use std::ops::BitAnd; const C: fn() = || is_set(); @@ -9,13 +9,12 @@ fn is_set() { } trait Bits { - fn bit(self) -> bool; - //~^ NOTE: the const parameter `I` has type `usize`, but the declaration in trait `Bits::bit` has type `u8` + fn bit(self) -> bool; } impl Bits for u8 { - fn bit(self) -> bool { - //~^ ERROR: method `bit` has an incompatible const parameter type for trait [E0053] + fn bit(self) -> bool { + //~^ ERROR: method `bit` has an incompatible generic parameter for trait `Bits` [E0053] let i = 1 << I; let mask = u8::from(i); mask & self == mask diff --git a/src/test/ui/const-generics/issues/issue-86820.stderr b/src/test/ui/const-generics/issues/issue-86820.stderr index f7b8d80eeca7f..3a9cd957f35e7 100644 --- a/src/test/ui/const-generics/issues/issue-86820.stderr +++ b/src/test/ui/const-generics/issues/issue-86820.stderr @@ -1,14 +1,15 @@ -error[E0053]: method `bit` has an incompatible const parameter type for trait - --> $DIR/issue-86820.rs:17:12 +error[E0053]: method `bit` has an incompatible generic parameter for trait `Bits` + --> $DIR/issue-86820.rs:16:12 | -LL | fn bit(self) -> bool { - | ^^^^^^^^^^^^^^^ - | -note: the const parameter `I` has type `usize`, but the declaration in trait `Bits::bit` has type `u8` - --> $DIR/issue-86820.rs:12:12 - | -LL | fn bit(self) -> bool; - | ^^^^^^^^^^^^ +LL | trait Bits { + | ---- +LL | fn bit(self) -> bool; + | ----------- expected const parameter of type `u8` +... +LL | impl Bits for u8 { + | ---------------- +LL | fn bit(self) -> bool { + | ^^^^^^^^^^^^^^ found const parameter of type `usize` error: aborting due to previous error diff --git a/src/test/ui/generic-associated-types/const_params_have_right_type.rs b/src/test/ui/generic-associated-types/const_params_have_right_type.rs new file mode 100644 index 0000000000000..6bed8e3aff975 --- /dev/null +++ b/src/test/ui/generic-associated-types/const_params_have_right_type.rs @@ -0,0 +1,12 @@ +#![feature(generic_associated_types)] + +trait Trait { + type Foo; +} + +impl Trait for () { + type Foo = u32; + //~^ error: type `Foo` has an incompatible generic parameter for trait +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/const_params_have_right_type.stderr b/src/test/ui/generic-associated-types/const_params_have_right_type.stderr new file mode 100644 index 0000000000000..89c993dee5e69 --- /dev/null +++ b/src/test/ui/generic-associated-types/const_params_have_right_type.stderr @@ -0,0 +1,16 @@ +error[E0053]: type `Foo` has an incompatible generic parameter for trait `Trait` + --> $DIR/const_params_have_right_type.rs:8:14 + | +LL | trait Trait { + | ----- +LL | type Foo; + | ----------- expected const parameter of type `u8` +... +LL | impl Trait for () { + | ----------------- +LL | type Foo = u32; + | ^^^^^^^^^^^^ found const parameter of type `u64` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0053`.