diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs index 04bf7c83b3208..7c99896b45714 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_method.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -9,14 +9,15 @@ use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{self, TyCtxtInferExt}; +use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::util::ExplicitSelf; -use rustc_middle::ty::InternalSubsts; use rustc_middle::ty::{ - self, AssocItem, DefIdTree, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, + self, AssocItem, DefIdTree, TraitRef, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitable, }; +use rustc_middle::ty::{FnSig, InternalSubsts}; use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; @@ -303,102 +304,19 @@ fn compare_predicate_entailment<'tcx>( } if let Err(terr) = result { - debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); + debug!(?terr, "sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); - let (impl_err_span, trait_err_span) = - extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m); - - cause.span = impl_err_span; - - let mut diag = struct_span_err!( - tcx.sess, - cause.span(), - E0053, - "method `{}` has an incompatible type for trait", - trait_m.name - ); - match &terr { - TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) - if trait_m.fn_has_self_parameter => - { - let ty = trait_sig.inputs()[0]; - let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) { - ExplicitSelf::ByValue => "self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), - _ => format!("self: {ty}"), - }; - - // When the `impl` receiver is an arbitrary self type, like `self: Box`, the - // span points only at the type `Box, but we want to cover the whole - // argument pattern and type. - let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { - ImplItemKind::Fn(ref sig, body) => tcx - .hir() - .body_param_names(body) - .zip(sig.decl.inputs.iter()) - .map(|(param, ty)| param.span.to(ty.span)) - .next() - .unwrap_or(impl_err_span), - _ => bug!("{:?} is not a method", impl_m), - }; - - diag.span_suggestion( - span, - "change the self-receiver type to match the trait", - sugg, - Applicability::MachineApplicable, - ); - } - TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => { - if trait_sig.inputs().len() == *i { - // Suggestion to change output type. We do not suggest in `async` functions - // to avoid complex logic or incorrect output. - match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { - ImplItemKind::Fn(ref sig, _) - if sig.header.asyncness == hir::IsAsync::NotAsync => - { - let msg = "change the output type to match the trait"; - let ap = Applicability::MachineApplicable; - match sig.decl.output { - hir::FnRetTy::DefaultReturn(sp) => { - let sugg = format!("-> {} ", trait_sig.output()); - diag.span_suggestion_verbose(sp, msg, sugg, ap); - } - hir::FnRetTy::Return(hir_ty) => { - let sugg = trait_sig.output(); - diag.span_suggestion(hir_ty.span, msg, sugg, ap); - } - }; - } - _ => {} - }; - } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { - diag.span_suggestion( - impl_err_span, - "change the parameter type to match the trait", - trait_ty, - Applicability::MachineApplicable, - ); - } - } - _ => {} - } - - infcx.err_ctxt().note_type_err( - &mut diag, - &cause, - trait_err_span.map(|sp| (sp, "type in trait".to_owned())), - Some(infer::ValuePairs::Terms(ExpectedFound { - expected: trait_fty.into(), - found: impl_fty.into(), - })), + let emitted = report_trait_method_mismatch( + tcx, + &mut cause, + &infcx, terr, - false, - false, + (trait_m, trait_fty), + (impl_m, impl_fty), + &trait_sig, + &impl_trait_ref, ); - - return Err(diag.emit()); + return Err(emitted); } // Check that all obligations are satisfied by the implementation's @@ -424,6 +342,7 @@ fn compare_predicate_entailment<'tcx>( Ok(()) } +#[instrument(skip(tcx), level = "debug", ret)] pub fn collect_trait_impl_trait_tys<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, @@ -437,7 +356,7 @@ pub fn collect_trait_impl_trait_tys<'tcx>( let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); let return_span = tcx.hir().fn_decl_by_hir_id(impl_m_hir_id).unwrap().output.span(); - let cause = ObligationCause::new( + let mut cause = ObligationCause::new( return_span, impl_m_hir_id, ObligationCauseCode::CompareImplItemObligation { @@ -514,23 +433,35 @@ pub fn collect_trait_impl_trait_tys<'tcx>( } } + debug!(?trait_sig, ?impl_sig, "equating function signatures"); + + let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)); + let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)); + // Unify the whole function signature. We need to do this to fully infer // the lifetimes of the return type, but do this after unifying just the // return types, since we want to avoid duplicating errors from // `compare_predicate_entailment`. - match infcx - .at(&cause, param_env) - .eq(tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)), tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig))) - { + match infcx.at(&cause, param_env).eq(trait_fty, impl_fty) { Ok(infer::InferOk { value: (), obligations }) => { ocx.register_obligations(obligations); } Err(terr) => { - let guar = tcx.sess.delay_span_bug( - return_span, - format!("could not unify `{trait_sig}` and `{impl_sig}`: {terr:?}"), + // This function gets called during `compare_predicate_entailment` when normalizing a + // signature that contains RPITIT. When the method signatures don't match, we have to + // emit an error now because `compare_predicate_entailment` will not report the error + // when normalization fails. + let emitted = report_trait_method_mismatch( + tcx, + &mut cause, + infcx, + terr, + (trait_m, trait_fty), + (impl_m, impl_fty), + &trait_sig, + &impl_trait_ref, ); - return Err(guar); + return Err(emitted); } } @@ -690,6 +621,112 @@ impl<'tcx> TypeFolder<'tcx> for ImplTraitInTraitCollector<'_, 'tcx> { } } +fn report_trait_method_mismatch<'tcx>( + tcx: TyCtxt<'tcx>, + cause: &mut ObligationCause<'tcx>, + infcx: &InferCtxt<'tcx>, + terr: TypeError<'tcx>, + (trait_m, trait_fty): (&AssocItem, Ty<'tcx>), + (impl_m, impl_fty): (&AssocItem, Ty<'tcx>), + trait_sig: &FnSig<'tcx>, + impl_trait_ref: &TraitRef<'tcx>, +) -> ErrorGuaranteed { + let (impl_err_span, trait_err_span) = + extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m); + + cause.span = impl_err_span; + + let mut diag = struct_span_err!( + tcx.sess, + cause.span(), + E0053, + "method `{}` has an incompatible type for trait", + trait_m.name + ); + match &terr { + TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) + if trait_m.fn_has_self_parameter => + { + let ty = trait_sig.inputs()[0]; + let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) { + ExplicitSelf::ByValue => "self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), + _ => format!("self: {ty}"), + }; + + // When the `impl` receiver is an arbitrary self type, like `self: Box`, the + // span points only at the type `Box, but we want to cover the whole + // argument pattern and type. + let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { + ImplItemKind::Fn(ref sig, body) => tcx + .hir() + .body_param_names(body) + .zip(sig.decl.inputs.iter()) + .map(|(param, ty)| param.span.to(ty.span)) + .next() + .unwrap_or(impl_err_span), + _ => bug!("{:?} is not a method", impl_m), + }; + + diag.span_suggestion( + span, + "change the self-receiver type to match the trait", + sugg, + Applicability::MachineApplicable, + ); + } + TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => { + if trait_sig.inputs().len() == *i { + // Suggestion to change output type. We do not suggest in `async` functions + // to avoid complex logic or incorrect output. + match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { + ImplItemKind::Fn(ref sig, _) + if sig.header.asyncness == hir::IsAsync::NotAsync => + { + let msg = "change the output type to match the trait"; + let ap = Applicability::MachineApplicable; + match sig.decl.output { + hir::FnRetTy::DefaultReturn(sp) => { + let sugg = format!("-> {} ", trait_sig.output()); + diag.span_suggestion_verbose(sp, msg, sugg, ap); + } + hir::FnRetTy::Return(hir_ty) => { + let sugg = trait_sig.output(); + diag.span_suggestion(hir_ty.span, msg, sugg, ap); + } + }; + } + _ => {} + }; + } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { + diag.span_suggestion( + impl_err_span, + "change the parameter type to match the trait", + trait_ty, + Applicability::MachineApplicable, + ); + } + } + _ => {} + } + + infcx.err_ctxt().note_type_err( + &mut diag, + &cause, + trait_err_span.map(|sp| (sp, "type in trait".to_owned())), + Some(infer::ValuePairs::Terms(ExpectedFound { + expected: trait_fty.into(), + found: impl_fty.into(), + })), + terr, + false, + false, + ); + + return diag.emit(); +} + fn check_region_bounds_on_impl_item<'tcx>( tcx: TyCtxt<'tcx>, impl_m: &ty::AssocItem, diff --git a/src/test/ui/impl-trait/in-trait/method-signature-matches.rs b/src/test/ui/impl-trait/in-trait/method-signature-matches.rs new file mode 100644 index 0000000000000..c848ee3f643de --- /dev/null +++ b/src/test/ui/impl-trait/in-trait/method-signature-matches.rs @@ -0,0 +1,51 @@ +// edition: 2021 + +#![feature(return_position_impl_trait_in_trait, async_fn_in_trait)] +#![allow(incomplete_features)] + +trait Uwu { + fn owo(x: ()) -> impl Sized; +} + +impl Uwu for () { + fn owo(_: u8) {} + //~^ ERROR method `owo` has an incompatible type for trait +} + +trait AsyncUwu { + async fn owo(x: ()) {} +} + +impl AsyncUwu for () { + async fn owo(_: u8) {} + //~^ ERROR method `owo` has an incompatible type for trait +} + +trait TooMuch { + fn calm_down_please() -> impl Sized; +} + +impl TooMuch for () { + fn calm_down_please(_: (), _: (), _: ()) {} + //~^ ERROR method `calm_down_please` has 3 parameters but the declaration in trait `TooMuch::calm_down_please` has 0 +} + +trait TooLittle { + fn come_on_a_little_more_effort(_: (), _: (), _: ()) -> impl Sized; +} + +impl TooLittle for () { + fn come_on_a_little_more_effort() {} + //~^ ERROR method `come_on_a_little_more_effort` has 0 parameters but the declaration in trait `TooLittle::come_on_a_little_more_effort` has 3 +} + +trait Lifetimes { + fn early<'early, T>(x: &'early T) -> impl Sized; +} + +impl Lifetimes for () { + fn early<'late, T>(_: &'late ()) {} + //~^ ERROR method `early` has an incompatible type for trait +} + +fn main() {} diff --git a/src/test/ui/impl-trait/in-trait/method-signature-matches.stderr b/src/test/ui/impl-trait/in-trait/method-signature-matches.stderr new file mode 100644 index 0000000000000..2b32c52c829ec --- /dev/null +++ b/src/test/ui/impl-trait/in-trait/method-signature-matches.stderr @@ -0,0 +1,84 @@ +error[E0053]: method `owo` has an incompatible type for trait + --> $DIR/method-signature-matches.rs:11:15 + | +LL | fn owo(_: u8) {} + | ^^ + | | + | expected `()`, found `u8` + | help: change the parameter type to match the trait: `()` + | +note: type in trait + --> $DIR/method-signature-matches.rs:7:15 + | +LL | fn owo(x: ()) -> impl Sized; + | ^^ + = note: expected fn pointer `fn(())` + found fn pointer `fn(u8)` + +error[E0053]: method `owo` has an incompatible type for trait + --> $DIR/method-signature-matches.rs:20:21 + | +LL | async fn owo(_: u8) {} + | ^^ + | | + | expected `()`, found `u8` + | help: change the parameter type to match the trait: `()` + | +note: while checking the return type of the `async fn` + --> $DIR/method-signature-matches.rs:20:25 + | +LL | async fn owo(_: u8) {} + | ^ checked the `Output` of this `async fn`, expected opaque type +note: while checking the return type of the `async fn` + --> $DIR/method-signature-matches.rs:20:25 + | +LL | async fn owo(_: u8) {} + | ^ checked the `Output` of this `async fn`, found opaque type +note: type in trait + --> $DIR/method-signature-matches.rs:16:21 + | +LL | async fn owo(x: ()) {} + | ^^ + = note: expected fn pointer `fn(()) -> _` + found fn pointer `fn(u8) -> _` + +error[E0050]: method `calm_down_please` has 3 parameters but the declaration in trait `TooMuch::calm_down_please` has 0 + --> $DIR/method-signature-matches.rs:29:28 + | +LL | fn calm_down_please() -> impl Sized; + | ------------------------------------ trait requires 0 parameters +... +LL | fn calm_down_please(_: (), _: (), _: ()) {} + | ^^^^^^^^^^^^^^^^ expected 0 parameters, found 3 + +error[E0050]: method `come_on_a_little_more_effort` has 0 parameters but the declaration in trait `TooLittle::come_on_a_little_more_effort` has 3 + --> $DIR/method-signature-matches.rs:38:5 + | +LL | fn come_on_a_little_more_effort(_: (), _: (), _: ()) -> impl Sized; + | ---------------- trait requires 3 parameters +... +LL | fn come_on_a_little_more_effort() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 3 parameters, found 0 + +error[E0053]: method `early` has an incompatible type for trait + --> $DIR/method-signature-matches.rs:47:27 + | +LL | fn early<'late, T>(_: &'late ()) {} + | - ^^^^^^^^^ + | | | + | | expected type parameter `T`, found `()` + | | help: change the parameter type to match the trait: `&'early T` + | this type parameter + | +note: type in trait + --> $DIR/method-signature-matches.rs:43:28 + | +LL | fn early<'early, T>(x: &'early T) -> impl Sized; + | ^^^^^^^^^ + = note: expected fn pointer `fn(&'early T)` + found fn pointer `fn(&())` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0050, E0053. +For more information about an error, try `rustc --explain E0050`.