Skip to content

Commit 77030b7

Browse files
authored
Rollup merge of rust-lang#96717 - BoxyUwU:gats_const_param_types_mismatch_err, r=lcnr
Handle mismatched generic param kinds in trait impls betterly - Check that generic params on a generic associated type are the same as in the trait definition - Check that const generics are not used in place of type generics (and the other way round too) r? `@lcnr`
2 parents c51871c + e4b8ed5 commit 77030b7

File tree

7 files changed

+268
-66
lines changed

7 files changed

+268
-66
lines changed

compiler/rustc_typeck/src/check/compare_method.rs

+116-51
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use rustc_hir::intravisit;
77
use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind};
88
use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
99
use rustc_infer::traits::util;
10-
use rustc_middle::ty;
1110
use rustc_middle::ty::error::{ExpectedFound, TypeError};
1211
use rustc_middle::ty::subst::{InternalSubsts, Subst};
1312
use rustc_middle::ty::util::ExplicitSelf;
13+
use rustc_middle::ty::{self, DefIdTree};
1414
use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt};
1515
use rustc_span::Span;
1616
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
@@ -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) {
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>(
@@ -579,6 +579,27 @@ fn compare_self_type<'tcx>(
579579
Ok(())
580580
}
581581

582+
/// Checks that the number of generics on a given assoc item in a trait impl is the same
583+
/// as the number of generics on the respective assoc item in the trait definition.
584+
///
585+
/// For example this code emits the errors in the following code:
586+
/// ```
587+
/// trait Trait {
588+
/// fn foo();
589+
/// type Assoc<T>;
590+
/// }
591+
///
592+
/// impl Trait for () {
593+
/// fn foo<T>() {}
594+
/// //~^ error
595+
/// type Assoc = u32;
596+
/// //~^ error
597+
/// }
598+
/// ```
599+
///
600+
/// Notably this does not error on `foo<T>` implemented as `foo<const N: u8>` or
601+
/// `foo<const N: u8>` implemented as `foo<const N: u32>`. This is handled in
602+
/// [`compare_generic_param_kinds`]. This function also does not handle lifetime parameters
582603
fn compare_number_of_generics<'tcx>(
583604
tcx: TyCtxt<'tcx>,
584605
impl_: &ty::AssocItem,
@@ -589,6 +610,15 @@ fn compare_number_of_generics<'tcx>(
589610
let trait_own_counts = tcx.generics_of(trait_.def_id).own_counts();
590611
let impl_own_counts = tcx.generics_of(impl_.def_id).own_counts();
591612

613+
// This avoids us erroring on `foo<T>` implemented as `foo<const N: u8>` as this is implemented
614+
// in `compare_generic_param_kinds` which will give a nicer error message than something like:
615+
// "expected 1 type parameter, found 0 type parameters"
616+
if (trait_own_counts.types + trait_own_counts.consts)
617+
== (impl_own_counts.types + impl_own_counts.consts)
618+
{
619+
return Ok(());
620+
}
621+
592622
let matchings = [
593623
("type", trait_own_counts.types, impl_own_counts.types),
594624
("const", trait_own_counts.consts, impl_own_counts.consts),
@@ -914,60 +944,93 @@ fn compare_synthetic_generics<'tcx>(
914944
if let Some(reported) = error_found { Err(reported) } else { Ok(()) }
915945
}
916946

917-
fn compare_const_param_types<'tcx>(
947+
/// Checks that all parameters in the generics of a given assoc item in a trait impl have
948+
/// the same kind as the respective generic parameter in the trait def.
949+
///
950+
/// For example all 4 errors in the following code are emitted here:
951+
/// ```
952+
/// trait Foo {
953+
/// fn foo<const N: u8>();
954+
/// type bar<const N: u8>;
955+
/// fn baz<const N: u32>();
956+
/// type blah<T>;
957+
/// }
958+
///
959+
/// impl Foo for () {
960+
/// fn foo<const N: u64>() {}
961+
/// //~^ error
962+
/// type bar<const N: u64> {}
963+
/// //~^ error
964+
/// fn baz<T>() {}
965+
/// //~^ error
966+
/// type blah<const N: i64> = u32;
967+
/// //~^ error
968+
/// }
969+
/// ```
970+
///
971+
/// This function does not handle lifetime parameters
972+
fn compare_generic_param_kinds<'tcx>(
918973
tcx: TyCtxt<'tcx>,
919-
impl_m: &ty::AssocItem,
920-
trait_m: &ty::AssocItem,
921-
trait_item_span: Option<Span>,
974+
impl_item: &ty::AssocItem,
975+
trait_item: &ty::AssocItem,
922976
) -> 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,
977+
assert_eq!(impl_item.kind, trait_item.kind);
978+
979+
let ty_const_params_of = |def_id| {
980+
tcx.generics_of(def_id).params.iter().filter(|param| {
981+
matches!(
982+
param.kind,
983+
GenericParamDefKind::Const { .. } | GenericParamDefKind::Type { .. }
984+
)
927985
})
928986
};
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-
};
987+
988+
for (param_impl, param_trait) in
989+
iter::zip(ty_const_params_of(impl_item.def_id), ty_const_params_of(trait_item.def_id))
990+
{
991+
use GenericParamDefKind::*;
992+
if match (&param_impl.kind, &param_trait.kind) {
993+
(Const { .. }, Const { .. })
994+
if tcx.type_of(param_impl.def_id) != tcx.type_of(param_trait.def_id) =>
995+
{
996+
true
997+
}
998+
(Const { .. }, Type { .. }) | (Type { .. }, Const { .. }) => true,
999+
// this is exhaustive so that anyone adding new generic param kinds knows
1000+
// to make sure this error is reported for them.
1001+
(Const { .. }, Const { .. }) | (Type { .. }, Type { .. }) => false,
1002+
(Lifetime { .. }, _) | (_, Lifetime { .. }) => unreachable!(),
1003+
} {
1004+
let param_impl_span = tcx.def_span(param_impl.def_id);
1005+
let param_trait_span = tcx.def_span(param_trait.def_id);
1006+
9531007
let mut err = struct_span_err!(
9541008
tcx.sess,
955-
*impl_span,
1009+
param_impl_span,
9561010
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-
),
1011+
"{} `{}` has an incompatible generic parameter for trait `{}`",
1012+
assoc_item_kind_str(&impl_item),
1013+
trait_item.name,
1014+
&tcx.def_path_str(tcx.parent(trait_item.def_id))
9701015
);
1016+
1017+
let make_param_message = |prefix: &str, param: &ty::GenericParamDef| match param.kind {
1018+
Const { .. } => {
1019+
format!("{} const parameter of type `{}`", prefix, tcx.type_of(param.def_id))
1020+
}
1021+
Type { .. } => format!("{} type parameter", prefix),
1022+
Lifetime { .. } => unreachable!(),
1023+
};
1024+
1025+
let trait_header_span = tcx.def_ident_span(tcx.parent(trait_item.def_id)).unwrap();
1026+
err.span_label(trait_header_span, "");
1027+
err.span_label(param_trait_span, make_param_message("expected", param_trait));
1028+
1029+
let impl_header_span =
1030+
tcx.sess.source_map().guess_head_span(tcx.def_span(tcx.parent(impl_item.def_id)));
1031+
err.span_label(impl_header_span, "");
1032+
err.span_label(param_impl_span, make_param_message("found", param_impl));
1033+
9711034
let reported = err.emit();
9721035
return Err(reported);
9731036
}
@@ -1095,6 +1158,8 @@ crate fn compare_ty_impl<'tcx>(
10951158
let _: Result<(), ErrorGuaranteed> = (|| {
10961159
compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?;
10971160

1161+
compare_generic_param_kinds(tcx, impl_ty, trait_ty)?;
1162+
10981163
let sp = tcx.def_span(impl_ty.def_id);
10991164
compare_type_predicate_entailment(tcx, impl_ty, sp, trait_ty, impl_trait_ref)?;
11001165

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
trait Trait {
2+
fn foo<U>() {}
3+
}
4+
impl Trait for () {
5+
fn foo<const M: u64>() {}
6+
//~^ error: method `foo` has an incompatible 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+
trait Aaaaaa {
26+
fn bbbb<const N: u32, T>() {}
27+
}
28+
impl Aaaaaa for () {
29+
fn bbbb<T, const N: u32>() {}
30+
//~^ error: method `bbbb` has an incompatible generic parameter for trait
31+
}
32+
33+
trait Names {
34+
fn abcd<T, const N: u32>() {}
35+
}
36+
impl Names for () {
37+
fn abcd<const N: u32, T>() {}
38+
//~^ error: method `abcd` has an incompatible generic parameter for trait
39+
}
40+
41+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
error[E0053]: method `foo` has an incompatible generic parameter for trait `Trait`
2+
--> $DIR/mismatched_ty_const_in_trait_impl.rs:5:12
3+
|
4+
LL | trait Trait {
5+
| -----
6+
LL | fn foo<U>() {}
7+
| - expected type parameter
8+
LL | }
9+
LL | impl Trait for () {
10+
| -----------------
11+
LL | fn foo<const M: u64>() {}
12+
| ^^^^^^^^^^^^ found const parameter of type `u64`
13+
14+
error[E0053]: method `bar` has an incompatible generic parameter for trait `Other`
15+
--> $DIR/mismatched_ty_const_in_trait_impl.rs:13:12
16+
|
17+
LL | trait Other {
18+
| -----
19+
LL | fn bar<const M: u8>() {}
20+
| ----------- expected const parameter of type `u8`
21+
LL | }
22+
LL | impl Other for () {
23+
| -----------------
24+
LL | fn bar<T>() {}
25+
| ^ found type parameter
26+
27+
error[E0053]: method `baz` has an incompatible generic parameter for trait `Uwu`
28+
--> $DIR/mismatched_ty_const_in_trait_impl.rs:21:12
29+
|
30+
LL | trait Uwu {
31+
| ---
32+
LL | fn baz<const N: u32>() {}
33+
| ------------ expected const parameter of type `u32`
34+
LL | }
35+
LL | impl Uwu for () {
36+
| ---------------
37+
LL | fn baz<const N: i32>() {}
38+
| ^^^^^^^^^^^^ found const parameter of type `i32`
39+
40+
error[E0053]: method `bbbb` has an incompatible generic parameter for trait `Aaaaaa`
41+
--> $DIR/mismatched_ty_const_in_trait_impl.rs:29:13
42+
|
43+
LL | trait Aaaaaa {
44+
| ------
45+
LL | fn bbbb<const N: u32, T>() {}
46+
| ------------ expected const parameter of type `u32`
47+
LL | }
48+
LL | impl Aaaaaa for () {
49+
| ------------------
50+
LL | fn bbbb<T, const N: u32>() {}
51+
| ^ found type parameter
52+
53+
error[E0053]: method `abcd` has an incompatible generic parameter for trait `Names`
54+
--> $DIR/mismatched_ty_const_in_trait_impl.rs:37:13
55+
|
56+
LL | trait Names {
57+
| -----
58+
LL | fn abcd<T, const N: u32>() {}
59+
| - expected type parameter
60+
LL | }
61+
LL | impl Names for () {
62+
| -----------------
63+
LL | fn abcd<const N: u32, T>() {}
64+
| ^^^^^^^^^^^^ found const parameter of type `u32`
65+
66+
error: aborting due to 5 previous errors
67+
68+
For more information about this error, try `rustc --explain E0053`.

src/test/ui/const-generics/issues/issue-86820.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Regression test for the ICE described in #86820.
22

3-
#![allow(unused,dead_code)]
3+
#![allow(unused, dead_code)]
44
use std::ops::BitAnd;
55

66
const C: fn() = || is_set();
@@ -9,13 +9,12 @@ fn is_set() {
99
}
1010

1111
trait Bits {
12-
fn bit<const I : u8>(self) -> bool;
13-
//~^ NOTE: the const parameter `I` has type `usize`, but the declaration in trait `Bits::bit` has type `u8`
12+
fn bit<const I: u8>(self) -> bool;
1413
}
1514

1615
impl Bits for u8 {
17-
fn bit<const I : usize>(self) -> bool {
18-
//~^ ERROR: method `bit` has an incompatible const parameter type for trait [E0053]
16+
fn bit<const I: usize>(self) -> bool {
17+
//~^ ERROR: method `bit` has an incompatible generic parameter for trait `Bits` [E0053]
1918
let i = 1 << I;
2019
let mask = u8::from(i);
2120
mask & self == mask

src/test/ui/const-generics/issues/issue-86820.stderr

+11-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
error[E0053]: method `bit` has an incompatible const parameter type for trait
2-
--> $DIR/issue-86820.rs:17:12
1+
error[E0053]: method `bit` has an incompatible generic parameter for trait `Bits`
2+
--> $DIR/issue-86820.rs:16:12
33
|
4-
LL | fn bit<const I : usize>(self) -> bool {
5-
| ^^^^^^^^^^^^^^^
6-
|
7-
note: the const parameter `I` has type `usize`, but the declaration in trait `Bits::bit` has type `u8`
8-
--> $DIR/issue-86820.rs:12:12
9-
|
10-
LL | fn bit<const I : u8>(self) -> bool;
11-
| ^^^^^^^^^^^^
4+
LL | trait Bits {
5+
| ----
6+
LL | fn bit<const I: u8>(self) -> bool;
7+
| ----------- expected const parameter of type `u8`
8+
...
9+
LL | impl Bits for u8 {
10+
| ----------------
11+
LL | fn bit<const I: usize>(self) -> bool {
12+
| ^^^^^^^^^^^^^^ found const parameter of type `usize`
1213

1314
error: aborting due to previous error
1415

0 commit comments

Comments
 (0)