From 6225e980bf0376986c10943216fb0b7779d29e58 Mon Sep 17 00:00:00 2001 From: Ellen Date: Thu, 5 May 2022 12:23:42 +0100 Subject: [PATCH] handle mismatched generic parameter kinds --- .../rustc_typeck/src/check/compare_method.rs | 213 +++++++++++++----- .../mismatched_ty_const_in_trait_impl.rs | 25 ++ .../mismatched_ty_const_in_trait_impl.stderr | 52 +++++ .../const_params_have_right_type.rs | 12 + .../const_params_have_right_type.stderr | 15 ++ 5 files changed, 263 insertions(+), 54 deletions(-) create mode 100644 src/test/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.rs create mode 100644 src/test/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.stderr create mode 100644 src/test/ui/generic-associated-types/const_params_have_right_type.rs create mode 100644 src/test/ui/generic-associated-types/const_params_have_right_type.stderr diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 6d78a863d54..c42280a8208 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -48,6 +48,10 @@ crate fn compare_impl_method<'tcx>( return; } + if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m, trait_item_span) { + 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>( @@ -914,62 +914,165 @@ 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, + impl_item: &ty::AssocItem, + trait_item: &ty::AssocItem, trait_item_span: Option, ) -> 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, - }; - let mut err = struct_span_err!( - tcx.sess, - *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 - ), - ); - let reported = err.emit(); - return Err(reported); + let get_param_span = |param: &ty::GenericParamDef| match tcx.hir().get_if_local(param.def_id) { + Some(hir::Node::GenericParam(hir::GenericParam { span, .. })) => Some(span), + _ => None, + }; + + let get_param_ident = |param: &ty::GenericParamDef| match tcx.hir().get_if_local(param.def_id) { + Some(hir::Node::GenericParam(hir::GenericParam { name, .. })) => 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 ty_const_params_impl = ty_const_params_of(impl_item.def_id); + let ty_const_params_trait = ty_const_params_of(trait_item.def_id); + let assoc_item_str = assoc_item_kind_str(&impl_item); + + for (param_impl, param_trait) in iter::zip(ty_const_params_impl, ty_const_params_trait) { + use GenericParamDefKind::*; + match (¶m_impl.kind, ¶m_trait.kind) { + (Const { .. }, Const { .. }) => { + let impl_ty = tcx.type_of(param_impl.def_id); + let trait_ty = tcx.type_of(param_trait.def_id); + if impl_ty != trait_ty { + let param_impl_span = get_param_span(param_impl).unwrap(); + let param_impl_ident = get_param_ident(param_impl); + let param_trait_span = get_param_span(param_trait); + + let mut err = struct_span_err!( + tcx.sess, + *param_impl_span, + E0053, + "{} `{}` has an incompatible const parameter type for trait", + assoc_item_str, + trait_item.name, + ); + err.span_note( + param_trait_span.map_or_else( + || trait_item_span.unwrap_or(*param_impl_span), + |span| *span, + ), + &format!( + "the const parameter{} has type `{}`, but the declaration \ + in trait `{}` has type `{}`", + ¶m_impl_ident + .map_or_else(|| "".to_string(), |ident| format!(" `{ident}`")), + impl_ty, + tcx.def_path_str(trait_item.def_id), + trait_ty + ), + ); + let reported = err.emit(); + return Err(reported); + } + } + (Const { .. }, Type { .. }) => { + let impl_ty = tcx.type_of(param_impl.def_id); + let param_impl_span = get_param_span(param_impl).unwrap(); + let param_impl_ident = get_param_ident(param_impl); + let param_trait_span = get_param_span(param_trait); + + let mut err = struct_span_err!( + tcx.sess, + *param_impl_span, + E0053, + "{} `{}` has an incompatible generic parameter for trait", + assoc_item_str, + trait_item.name, + ); + err.span_note( + param_trait_span + .map_or_else(|| trait_item_span.unwrap_or(*param_impl_span), |span| *span), + &format!( + "the trait impl specifies{} a const parameter of type `{}`, but the declaration \ + in trait `{}` requires it is a type parameter", + ¶m_impl_ident + .map_or_else(|| "".to_string(), |ident| format!(" `{ident}` is")), + impl_ty, + tcx.def_path_str(trait_item.def_id), + ), + ); + let reported = err.emit(); + return Err(reported); + } + (Type { .. }, Const { .. }) => { + let trait_ty = tcx.type_of(param_trait.def_id); + let param_impl_span = get_param_span(param_impl).unwrap(); + let param_impl_ident = get_param_ident(param_impl); + let param_trait_span = get_param_span(param_trait); + + let mut err = struct_span_err!( + tcx.sess, + *param_impl_span, + E0053, + "{} `{}` has an incompatible generic parameter for trait", + assoc_item_str, + trait_item.name, + ); + err.span_note( + param_trait_span + .map_or_else(|| trait_item_span.unwrap_or(*param_impl_span), |span| *span), + &format!( + "the trait impl specifies{} a type parameter, but the declaration \ + in trait `{}` requires it is a const parameter of type `{}`", + ¶m_impl_ident + .map_or_else(|| "".to_string(), |ident| format!(" `{ident}` is")), + tcx.def_path_str(trait_item.def_id), + trait_ty, + ), + ); + let reported = err.emit(); + return Err(reported); + } + _ => (), } } @@ -1095,6 +1198,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, trait_item_span)?; + 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 00000000000..0c6669fa9b6 --- /dev/null +++ b/src/test/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.rs @@ -0,0 +1,25 @@ +trait Trait { + fn foo() {} +} +impl Trait for () { + fn foo() {} + //~^ error: method `foo` has an incompatble 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 +} + +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 00000000000..d400c216590 --- /dev/null +++ b/src/test/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.stderr @@ -0,0 +1,52 @@ +error[E0049]: method `foo` has 0 type parameters but its trait declaration has 1 type parameter + --> $DIR/mismatched_ty_const_in_trait_impl.rs:5:12 + | +LL | fn foo() {} + | - expected 1 type parameter +... +LL | fn foo() {} + | ^^^^^^^^^^^^ found 0 type parameters + +error[E0049]: method `foo` has 1 const parameter but its trait declaration has 0 const parameters + --> $DIR/mismatched_ty_const_in_trait_impl.rs:5:12 + | +LL | fn foo() {} + | - expected 0 const parameters +... +LL | fn foo() {} + | ^^^^^^^^^^^^ found 1 const parameter + +error[E0049]: method `bar` has 1 type parameter but its trait declaration has 0 type parameters + --> $DIR/mismatched_ty_const_in_trait_impl.rs:13:12 + | +LL | fn bar() {} + | ----------- expected 0 type parameters +... +LL | fn bar() {} + | ^ found 1 type parameter + +error[E0049]: method `bar` has 0 const parameters but its trait declaration has 1 const parameter + --> $DIR/mismatched_ty_const_in_trait_impl.rs:13:12 + | +LL | fn bar() {} + | ----------- expected 1 const parameter +... +LL | fn bar() {} + | ^ found 0 const parameters + +error[E0053]: method `baz` has an incompatible const parameter type for trait + --> $DIR/mismatched_ty_const_in_trait_impl.rs:21:12 + | +LL | fn baz() {} + | ^^^^^^^^^^^^ + | +note: the const parameter `N` has type `i32`, but the declaration in trait `Uwu::baz` has type `u32` + --> $DIR/mismatched_ty_const_in_trait_impl.rs:18:12 + | +LL | fn baz() {} + | ^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0049, E0053. +For more information about an error, try `rustc --explain E0049`. 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 00000000000..675132587bf --- /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: associated type `Foo` has an incompatible const parameter type +} + +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 00000000000..62353180c67 --- /dev/null +++ b/src/test/ui/generic-associated-types/const_params_have_right_type.stderr @@ -0,0 +1,15 @@ +error[E0053]: associated type `Foo` has an incompatible const parameter type for trait + --> $DIR/const_params_have_right_type.rs:8:14 + | +LL | type Foo = u32; + | ^^^^^^^^^^^^ + | +note: the const parameter `N` has type `u64`, but the declaration in trait `Trait::Foo` has type `u8` + --> $DIR/const_params_have_right_type.rs:4:14 + | +LL | type Foo; + | ^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0053`.