Rollup merge of #104214 - Nilstrieb:returns_impl_Ice, r=compiler-errors

Emit error in `collecting_trait_impl_trait_tys` on mismatched signatures

Previously, a `delay_span_bug` was isssued, failing normalization. This create a `TyKind::Error` in the signature, which caused `compare_predicate_entailment` to swallow its signature mismatch error, causing ICEs because no error was emitted.

fixes #104183

r? ``@compiler-errors``
This commit is contained in:
Dylan DPC 2022-11-12 12:02:52 +05:30 committed by GitHub
commit 35816ff52a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 277 additions and 105 deletions

View File

@ -9,14 +9,15 @@
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<Self>`, the
// span points only at the type `Box<Self`>, 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 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'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<Self>`, the
// span points only at the type `Box<Self`>, 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,

View File

@ -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() {}

View File

@ -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`.