Rollup merge of #86843 - FabianWolff:issue-86820, r=lcnr

Check that const parameters of trait methods have compatible types

This PR fixes #86820. The problem is that this currently passes the type checker:
```rust
trait Tr {
    fn foo<const N: u8>(self) -> u8;
}

impl Tr for f32 {
    fn foo<const N: bool>(self) -> u8 { 42 }
}
```
i.e. the type checker fails to check whether const parameters in `impl` methods have the same type as the corresponding declaration in the trait. With my changes, I get, for the above code:
```
error[E0053]: method `foo` has an incompatible const parameter type for trait
 --> test.rs:6:18
  |
6 |     fn foo<const N: bool>(self) -> u8 { 42 }
  |                  ^
  |
note: the const parameter `N` has type `bool`, but the declaration in trait `Tr::foo` has type `u8`
 --> test.rs:2:18
  |
2 |     fn foo<const N: u8>(self) -> u8;
  |                  ^

error: aborting due to previous error
```
This fixes #86820, where an ICE happens later on because the trait method is declared with a const parameter of type `u8`, but the `impl` uses one of type `usize`:
> `expected int of size 8, but got size 1`
This commit is contained in:
Yuki Okushi 2021-07-18 14:21:54 +09:00 committed by GitHub
commit 783efd29ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 106 additions and 0 deletions

View File

@ -66,6 +66,10 @@ crate fn compare_impl_method<'tcx>(
{
return;
}
if let Err(ErrorReported) = compare_const_param_types(tcx, impl_m, trait_m, trait_item_span) {
return;
}
}
fn compare_predicate_entailment<'tcx>(
@ -929,6 +933,68 @@ fn compare_synthetic_generics<'tcx>(
if error_found { Err(ErrorReported) } else { Ok(()) }
}
fn compare_const_param_types<'tcx>(
tcx: TyCtxt<'tcx>,
impl_m: &ty::AssocItem,
trait_m: &ty::AssocItem,
trait_item_span: Option<Span>,
) -> Result<(), ErrorReported> {
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,
})
};
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.ident
);
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
),
);
err.emit();
return Err(ErrorReported);
}
}
Ok(())
}
crate fn compare_const_impl<'tcx>(
tcx: TyCtxt<'tcx>,
impl_c: &ty::AssocItem,

View File

@ -0,0 +1,25 @@
// Regression test for the ICE described in #86820.
#![allow(unused,dead_code)]
use std::ops::BitAnd;
const C: fn() = || is_set();
fn is_set() {
0xffu8.bit::<0>();
}
trait Bits {
fn bit<const I : u8>(self) -> bool;
//~^ NOTE: the const parameter `I` has type `usize`, but the declaration in trait `Bits::bit` has type `u8`
}
impl Bits for u8 {
fn bit<const I : usize>(self) -> bool {
//~^ ERROR: method `bit` has an incompatible const parameter type for trait [E0053]
let i = 1 << I;
let mask = u8::from(i);
mask & self == mask
}
}
fn main() {}

View File

@ -0,0 +1,15 @@
error[E0053]: method `bit` has an incompatible const parameter type for trait
--> $DIR/issue-86820.rs:17:18
|
LL | fn bit<const I : usize>(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:18
|
LL | fn bit<const I : u8>(self) -> bool;
| ^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0053`.