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;
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)
{
{
return;
}
-
- if let Err(_) = compare_const_param_types(tcx, impl_m, trait_m, trait_item_span) {
- return;
- }
}
fn compare_predicate_entailment<'tcx>(
let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
debug!("compare_impl_method: impl_fty={:?}", impl_fty);
- // First liberate late bound regions and subst placeholders
- let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, tcx.fn_sig(trait_m.def_id));
- let trait_sig = trait_sig.subst(tcx, trait_to_placeholder_substs);
+ let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
+ let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
let trait_sig =
inh.normalize_associated_types_in(impl_m_span, impl_m_hir_id, param_env, trait_sig);
// Add the resulting inputs and output as well-formed.
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<T>;
+/// }
+///
+/// impl Trait for () {
+/// fn foo<T>() {}
+/// //~^ error
+/// type Assoc = u32;
+/// //~^ error
+/// }
+/// ```
+///
+/// Notably this does not error on `foo<T>` implemented as `foo<const N: u8>` or
+/// `foo<const N: u8>` implemented as `foo<const N: u32>`. 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,
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<T>` implemented as `foo<const N: u8>` 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),
iter::zip(impl_m_type_params, trait_m_type_params)
{
if impl_synthetic != trait_synthetic {
- let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id.expect_local());
+ let impl_def_id = impl_def_id.expect_local();
+ let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id);
let impl_span = tcx.hir().span(impl_hir_id);
let trait_span = tcx.def_span(trait_def_id);
let mut err = struct_span_err!(
hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs,
_ => unreachable!(),
};
- struct Visitor(Option<Span>, hir::def_id::DefId);
+ struct Visitor(Option<Span>, hir::def_id::LocalDefId);
impl<'v> intravisit::Visitor<'v> for Visitor {
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
intravisit::walk_ty(self, ty);
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) =
ty.kind
&& let Res::Def(DefKind::TyParam, def_id) = path.res
- && def_id == self.1
+ && def_id == self.1.to_def_id()
{
self.0 = Some(ty.span);
}
}
let span = visitor.0?;
- let bounds =
- impl_m.generics.params.iter().find_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => None,
- GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
- if param.hir_id == impl_hir_id {
- Some(¶m.bounds)
- } else {
- None
- }
- }
- })?;
+ let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
let bounds = bounds.first()?.span().to(bounds.last()?.span());
let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?;
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<const N: u8>();
+/// type bar<const N: u8>;
+/// fn baz<const N: u32>();
+/// type blah<T>;
+/// }
+///
+/// impl Foo for () {
+/// fn foo<const N: u64>() {}
+/// //~^ error
+/// type bar<const N: u64> {}
+/// //~^ error
+/// fn baz<T>() {}
+/// //~^ error
+/// type blah<const N: i64> = 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<Span>,
+ 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);
}
// Compute placeholder form of impl and trait const tys.
let impl_ty = tcx.type_of(impl_c.def_id);
- let trait_ty = tcx.type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs);
+ let trait_ty = tcx.bound_type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs);
let mut cause = ObligationCause::new(
impl_c_span,
impl_c_hir_id,
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)?;
};
let obligations = tcx
- .explicit_item_bounds(trait_ty.def_id)
- .iter()
- .map(|&(bound, span)| {
+ .bound_explicit_item_bounds(trait_ty.def_id)
+ .transpose_iter()
+ .map(|e| e.map_bound(|e| *e).transpose_tuple2())
+ .map(|(bound, span)| {
debug!(?bound);
let concrete_ty_bound = bound.subst(tcx, rebased_substs);
debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
- traits::Obligation::new(mk_cause(span), param_env, concrete_ty_bound)
+ traits::Obligation::new(mk_cause(span.0), param_env, concrete_ty_bound)
})
.collect();
debug!("check_type_bounds: item_bounds={:?}", obligations);