Skip to content

Commit 6225e98

Browse files
committed
handle mismatched generic parameter kinds
1 parent fed2c43 commit 6225e98

File tree

5 files changed

+264
-55
lines changed

5 files changed

+264
-55
lines changed

compiler/rustc_typeck/src/check/compare_method.rs

Lines changed: 160 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ crate fn compare_impl_method<'tcx>(
4848
return;
4949
}
5050

51+
if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m, trait_item_span) {
52+
return;
53+
}
54+
5155
if let Err(_) =
5256
compare_number_of_method_arguments(tcx, impl_m, impl_m_span, trait_m, trait_item_span)
5357
{
@@ -62,10 +66,6 @@ crate fn compare_impl_method<'tcx>(
6266
{
6367
return;
6468
}
65-
66-
if let Err(_) = compare_const_param_types(tcx, impl_m, trait_m, trait_item_span) {
67-
return;
68-
}
6969
}
7070

7171
fn compare_predicate_entailment<'tcx>(
@@ -914,62 +914,165 @@ fn compare_synthetic_generics<'tcx>(
914914
if let Some(reported) = error_found { Err(reported) } else { Ok(()) }
915915
}
916916

917-
fn compare_const_param_types<'tcx>(
917+
/// Checks that all parameters in the generics of a given assoc item in a trait impl have
918+
/// the same kind as the respective generic parameter in the trait def.
919+
///
920+
/// For example all 4 errors in the following code are emitted here:
921+
/// ```
922+
/// trait Foo {
923+
/// fn foo<const N: u8>();
924+
/// type bar<const N: u8>;
925+
/// fn baz<const N: u32>();
926+
/// type blah<T>;
927+
/// }
928+
///
929+
/// impl Foo for () {
930+
/// fn foo<const N: u64>() {}
931+
/// //~^ error
932+
/// type bar<const N: u64> {}
933+
/// //~^ error
934+
/// fn baz<T>() {}
935+
/// //~^ error
936+
/// type blah<const N: i64> = u32;
937+
/// //~^ error
938+
/// }
939+
/// ```
940+
///
941+
/// This function does not handle lifetime parameters
942+
fn compare_generic_param_kinds<'tcx>(
918943
tcx: TyCtxt<'tcx>,
919-
impl_m: &ty::AssocItem,
920-
trait_m: &ty::AssocItem,
944+
impl_item: &ty::AssocItem,
945+
trait_item: &ty::AssocItem,
921946
trait_item_span: Option<Span>,
922947
) -> Result<(), ErrorGuaranteed> {
923-
let const_params_of = |def_id| {
924-
tcx.generics_of(def_id).params.iter().filter_map(|param| match param.kind {
925-
GenericParamDefKind::Const { .. } => Some(param.def_id),
926-
_ => None,
948+
assert_eq!(impl_item.kind, trait_item.kind);
949+
950+
let ty_const_params_of = |def_id| {
951+
tcx.generics_of(def_id).params.iter().filter(|param| {
952+
matches!(
953+
param.kind,
954+
GenericParamDefKind::Const { .. } | GenericParamDefKind::Type { .. }
955+
)
927956
})
928957
};
929-
let const_params_impl = const_params_of(impl_m.def_id);
930-
let const_params_trait = const_params_of(trait_m.def_id);
931-
932-
for (const_param_impl, const_param_trait) in iter::zip(const_params_impl, const_params_trait) {
933-
let impl_ty = tcx.type_of(const_param_impl);
934-
let trait_ty = tcx.type_of(const_param_trait);
935-
if impl_ty != trait_ty {
936-
let (impl_span, impl_ident) = match tcx.hir().get_if_local(const_param_impl) {
937-
Some(hir::Node::GenericParam(hir::GenericParam { span, name, .. })) => (
938-
span,
939-
match name {
940-
hir::ParamName::Plain(ident) => Some(ident),
941-
_ => None,
942-
},
943-
),
944-
other => bug!(
945-
"expected GenericParam, found {:?}",
946-
other.map_or_else(|| "nothing".to_string(), |n| format!("{:?}", n))
947-
),
948-
};
949-
let trait_span = match tcx.hir().get_if_local(const_param_trait) {
950-
Some(hir::Node::GenericParam(hir::GenericParam { span, .. })) => Some(span),
951-
_ => None,
952-
};
953-
let mut err = struct_span_err!(
954-
tcx.sess,
955-
*impl_span,
956-
E0053,
957-
"method `{}` has an incompatible const parameter type for trait",
958-
trait_m.name
959-
);
960-
err.span_note(
961-
trait_span.map_or_else(|| trait_item_span.unwrap_or(*impl_span), |span| *span),
962-
&format!(
963-
"the const parameter{} has type `{}`, but the declaration \
964-
in trait `{}` has type `{}`",
965-
&impl_ident.map_or_else(|| "".to_string(), |ident| format!(" `{ident}`")),
966-
impl_ty,
967-
tcx.def_path_str(trait_m.def_id),
968-
trait_ty
969-
),
970-
);
971-
let reported = err.emit();
972-
return Err(reported);
958+
959+
let get_param_span = |param: &ty::GenericParamDef| match tcx.hir().get_if_local(param.def_id) {
960+
Some(hir::Node::GenericParam(hir::GenericParam { span, .. })) => Some(span),
961+
_ => None,
962+
};
963+
964+
let get_param_ident = |param: &ty::GenericParamDef| match tcx.hir().get_if_local(param.def_id) {
965+
Some(hir::Node::GenericParam(hir::GenericParam { name, .. })) => match name {
966+
hir::ParamName::Plain(ident) => Some(ident),
967+
_ => None,
968+
},
969+
other => bug!(
970+
"expected GenericParam, found {:?}",
971+
other.map_or_else(|| "nothing".to_string(), |n| format!("{:?}", n))
972+
),
973+
};
974+
975+
let ty_const_params_impl = ty_const_params_of(impl_item.def_id);
976+
let ty_const_params_trait = ty_const_params_of(trait_item.def_id);
977+
let assoc_item_str = assoc_item_kind_str(&impl_item);
978+
979+
for (param_impl, param_trait) in iter::zip(ty_const_params_impl, ty_const_params_trait) {
980+
use GenericParamDefKind::*;
981+
match (&param_impl.kind, &param_trait.kind) {
982+
(Const { .. }, Const { .. }) => {
983+
let impl_ty = tcx.type_of(param_impl.def_id);
984+
let trait_ty = tcx.type_of(param_trait.def_id);
985+
if impl_ty != trait_ty {
986+
let param_impl_span = get_param_span(param_impl).unwrap();
987+
let param_impl_ident = get_param_ident(param_impl);
988+
let param_trait_span = get_param_span(param_trait);
989+
990+
let mut err = struct_span_err!(
991+
tcx.sess,
992+
*param_impl_span,
993+
E0053,
994+
"{} `{}` has an incompatible const parameter type for trait",
995+
assoc_item_str,
996+
trait_item.name,
997+
);
998+
err.span_note(
999+
param_trait_span.map_or_else(
1000+
|| trait_item_span.unwrap_or(*param_impl_span),
1001+
|span| *span,
1002+
),
1003+
&format!(
1004+
"the const parameter{} has type `{}`, but the declaration \
1005+
in trait `{}` has type `{}`",
1006+
&param_impl_ident
1007+
.map_or_else(|| "".to_string(), |ident| format!(" `{ident}`")),
1008+
impl_ty,
1009+
tcx.def_path_str(trait_item.def_id),
1010+
trait_ty
1011+
),
1012+
);
1013+
let reported = err.emit();
1014+
return Err(reported);
1015+
}
1016+
}
1017+
(Const { .. }, Type { .. }) => {
1018+
let impl_ty = tcx.type_of(param_impl.def_id);
1019+
let param_impl_span = get_param_span(param_impl).unwrap();
1020+
let param_impl_ident = get_param_ident(param_impl);
1021+
let param_trait_span = get_param_span(param_trait);
1022+
1023+
let mut err = struct_span_err!(
1024+
tcx.sess,
1025+
*param_impl_span,
1026+
E0053,
1027+
"{} `{}` has an incompatible generic parameter for trait",
1028+
assoc_item_str,
1029+
trait_item.name,
1030+
);
1031+
err.span_note(
1032+
param_trait_span
1033+
.map_or_else(|| trait_item_span.unwrap_or(*param_impl_span), |span| *span),
1034+
&format!(
1035+
"the trait impl specifies{} a const parameter of type `{}`, but the declaration \
1036+
in trait `{}` requires it is a type parameter",
1037+
&param_impl_ident
1038+
.map_or_else(|| "".to_string(), |ident| format!(" `{ident}` is")),
1039+
impl_ty,
1040+
tcx.def_path_str(trait_item.def_id),
1041+
),
1042+
);
1043+
let reported = err.emit();
1044+
return Err(reported);
1045+
}
1046+
(Type { .. }, Const { .. }) => {
1047+
let trait_ty = tcx.type_of(param_trait.def_id);
1048+
let param_impl_span = get_param_span(param_impl).unwrap();
1049+
let param_impl_ident = get_param_ident(param_impl);
1050+
let param_trait_span = get_param_span(param_trait);
1051+
1052+
let mut err = struct_span_err!(
1053+
tcx.sess,
1054+
*param_impl_span,
1055+
E0053,
1056+
"{} `{}` has an incompatible generic parameter for trait",
1057+
assoc_item_str,
1058+
trait_item.name,
1059+
);
1060+
err.span_note(
1061+
param_trait_span
1062+
.map_or_else(|| trait_item_span.unwrap_or(*param_impl_span), |span| *span),
1063+
&format!(
1064+
"the trait impl specifies{} a type parameter, but the declaration \
1065+
in trait `{}` requires it is a const parameter of type `{}`",
1066+
&param_impl_ident
1067+
.map_or_else(|| "".to_string(), |ident| format!(" `{ident}` is")),
1068+
tcx.def_path_str(trait_item.def_id),
1069+
trait_ty,
1070+
),
1071+
);
1072+
let reported = err.emit();
1073+
return Err(reported);
1074+
}
1075+
_ => (),
9731076
}
9741077
}
9751078

@@ -1095,6 +1198,8 @@ crate fn compare_ty_impl<'tcx>(
10951198
let _: Result<(), ErrorGuaranteed> = (|| {
10961199
compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?;
10971200

1201+
compare_generic_param_kinds(tcx, impl_ty, trait_ty, trait_item_span)?;
1202+
10981203
let sp = tcx.def_span(impl_ty.def_id);
10991204
compare_type_predicate_entailment(tcx, impl_ty, sp, trait_ty, impl_trait_ref)?;
11001205

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
trait Trait {
2+
fn foo<U>() {}
3+
}
4+
impl Trait for () {
5+
fn foo<const M: u64>() {}
6+
//~^ error: method `foo` has an incompatble generic parameter for trait
7+
}
8+
9+
trait Other {
10+
fn bar<const M: u8>() {}
11+
}
12+
impl Other for () {
13+
fn bar<T>() {}
14+
//~^ error: method `bar` has an incompatible generic parameter for trait
15+
}
16+
17+
trait Uwu {
18+
fn baz<const N: u32>() {}
19+
}
20+
impl Uwu for () {
21+
fn baz<const N: i32>() {}
22+
//~^ error: method `baz` has an incompatible generic parameter for trait
23+
}
24+
25+
fn main() {}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error[E0049]: method `foo` has 0 type parameters but its trait declaration has 1 type parameter
2+
--> $DIR/mismatched_ty_const_in_trait_impl.rs:5:12
3+
|
4+
LL | fn foo<U>() {}
5+
| - expected 1 type parameter
6+
...
7+
LL | fn foo<const M: u64>() {}
8+
| ^^^^^^^^^^^^ found 0 type parameters
9+
10+
error[E0049]: method `foo` has 1 const parameter but its trait declaration has 0 const parameters
11+
--> $DIR/mismatched_ty_const_in_trait_impl.rs:5:12
12+
|
13+
LL | fn foo<U>() {}
14+
| - expected 0 const parameters
15+
...
16+
LL | fn foo<const M: u64>() {}
17+
| ^^^^^^^^^^^^ found 1 const parameter
18+
19+
error[E0049]: method `bar` has 1 type parameter but its trait declaration has 0 type parameters
20+
--> $DIR/mismatched_ty_const_in_trait_impl.rs:13:12
21+
|
22+
LL | fn bar<const M: u8>() {}
23+
| ----------- expected 0 type parameters
24+
...
25+
LL | fn bar<T>() {}
26+
| ^ found 1 type parameter
27+
28+
error[E0049]: method `bar` has 0 const parameters but its trait declaration has 1 const parameter
29+
--> $DIR/mismatched_ty_const_in_trait_impl.rs:13:12
30+
|
31+
LL | fn bar<const M: u8>() {}
32+
| ----------- expected 1 const parameter
33+
...
34+
LL | fn bar<T>() {}
35+
| ^ found 0 const parameters
36+
37+
error[E0053]: method `baz` has an incompatible const parameter type for trait
38+
--> $DIR/mismatched_ty_const_in_trait_impl.rs:21:12
39+
|
40+
LL | fn baz<const N: i32>() {}
41+
| ^^^^^^^^^^^^
42+
|
43+
note: the const parameter `N` has type `i32`, but the declaration in trait `Uwu::baz` has type `u32`
44+
--> $DIR/mismatched_ty_const_in_trait_impl.rs:18:12
45+
|
46+
LL | fn baz<const N: u32>() {}
47+
| ^^^^^^^^^^^^
48+
49+
error: aborting due to 5 previous errors
50+
51+
Some errors have detailed explanations: E0049, E0053.
52+
For more information about an error, try `rustc --explain E0049`.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![feature(generic_associated_types)]
2+
3+
trait Trait {
4+
type Foo<const N: u8>;
5+
}
6+
7+
impl Trait for () {
8+
type Foo<const N: u64> = u32;
9+
//~^ error: associated type `Foo` has an incompatible const parameter type
10+
}
11+
12+
fn main() {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0053]: associated type `Foo` has an incompatible const parameter type for trait
2+
--> $DIR/const_params_have_right_type.rs:8:14
3+
|
4+
LL | type Foo<const N: u64> = u32;
5+
| ^^^^^^^^^^^^
6+
|
7+
note: the const parameter `N` has type `u64`, but the declaration in trait `Trait::Foo` has type `u8`
8+
--> $DIR/const_params_have_right_type.rs:4:14
9+
|
10+
LL | type Foo<const N: u8>;
11+
| ^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0053`.

0 commit comments

Comments
 (0)