Skip to content

Commit

Permalink
Rollup merge of rust-lang#106208 - compiler-errors:compare-item-regio…
Browse files Browse the repository at this point in the history
…n-err, r=estebank

Make trait/impl `where` clause mismatch on region error a bit more actionable

Improve `where` clause suggestions for GATs/methods that have incompatible region predicates in their `where` clauses.

Also addresses this diagnostic that went away rust-lang#106129 (comment)
  • Loading branch information
matthiaskrgr authored Dec 29, 2022
2 parents 0f86c76 + 6e794dc commit f9fab1f
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 42 deletions.
123 changes: 84 additions & 39 deletions compiler/rustc_infer/src/infer/error_reporting/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ use crate::errors::RegionOriginNote;
use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
use crate::infer::{self, SubregionOrigin};
use rustc_errors::{
fluent, struct_span_err, AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
fluent, struct_span_err, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder,
ErrorGuaranteed,
};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::{self, Region};
use rustc_middle::ty::{self, IsSuggestable, Region};
use rustc_span::symbol::kw;

use super::ObligationCauseAsDiagArg;

Expand Down Expand Up @@ -313,55 +316,38 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
);
err
}
infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => self
.report_extra_impl_obligation(
infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
let mut err = self.report_extra_impl_obligation(
span,
impl_item_def_id,
trait_item_def_id,
&format!("`{}: {}`", sup, sub),
),
);
// We should only suggest rewriting the `where` clause if the predicate is within that `where` clause
if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id)
&& generics.where_clause_span.contains(span)
{
self.suggest_copy_trait_method_bounds(
trait_item_def_id,
impl_item_def_id,
&mut err,
);
}
err
}
infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
let mut err = self.report_concrete_failure(*parent, sub, sup);

let trait_item_span = self.tcx.def_span(trait_item_def_id);
let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
err.span_label(
trait_item_span,
format!("definition of `{}` from trait", item_name),
);

let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id);
let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id);

let impl_predicates: rustc_data_structures::fx::FxHashSet<_> =
impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect();
let clauses: Vec<_> = trait_predicates
.predicates
.into_iter()
.filter(|&(pred, _)| !impl_predicates.contains(pred))
.map(|(pred, _)| format!("{}", pred))
.collect();

if !clauses.is_empty() {
let generics = self.tcx.hir().get_generics(impl_item_def_id).unwrap();
let where_clause_span = generics.tail_span_for_predicate_suggestion();

let suggestion = format!(
"{} {}",
generics.add_where_or_trailing_comma(),
clauses.join(", "),
);
err.span_suggestion(
where_clause_span,
&format!(
"try copying {} from the trait",
if clauses.len() > 1 { "these clauses" } else { "this clause" }
),
suggestion,
rustc_errors::Applicability::MaybeIncorrect,
);
}

self.suggest_copy_trait_method_bounds(
trait_item_def_id,
impl_item_def_id,
&mut err,
);
err
}
infer::AscribeUserTypeProvePredicate(span) => {
Expand All @@ -388,6 +374,65 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}

pub fn suggest_copy_trait_method_bounds(
&self,
trait_item_def_id: DefId,
impl_item_def_id: LocalDefId,
err: &mut Diagnostic,
) {
// FIXME(compiler-errors): Right now this is only being used for region
// predicate mismatches. Ideally, we'd use it for *all* predicate mismatches,
// but right now it's not really very smart when it comes to implicit `Sized`
// predicates and bounds on the trait itself.

let Some(impl_def_id) =
self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx) else { return; };
let Some(trait_ref) = self
.tcx
.impl_trait_ref(impl_def_id)
else { return; };
let trait_substs = trait_ref
// Replace the explicit self type with `Self` for better suggestion rendering
.with_self_ty(self.tcx, self.tcx.mk_ty_param(0, kw::SelfUpper))
.substs;
let trait_item_substs =
ty::InternalSubsts::identity_for_item(self.tcx, impl_item_def_id.to_def_id())
.rebase_onto(self.tcx, impl_def_id, trait_substs);

let Ok(trait_predicates) = self
.tcx
.bound_explicit_predicates_of(trait_item_def_id)
.map_bound(|p| p.predicates)
.subst_iter_copied(self.tcx, trait_item_substs)
.map(|(pred, _)| {
if pred.is_suggestable(self.tcx, false) {
Ok(pred.to_string())
} else {
Err(())
}
})
.collect::<Result<Vec<_>, ()>>() else { return; };

let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { return; };

if trait_predicates.is_empty() {
err.span_suggestion_verbose(
generics.where_clause_span,
"remove the `where` clause",
String::new(),
Applicability::MachineApplicable,
);
} else {
let space = if generics.where_clause_span.is_empty() { " " } else { "" };
err.span_suggestion_verbose(
generics.where_clause_span,
"copy the `where` clause predicates from the trait",
format!("{space}where {}", trait_predicates.join(", ")),
Applicability::MachineApplicable,
);
}
}

pub(super) fn report_placeholder_failure(
&self,
placeholder_origin: SubregionOrigin<'tcx>,
Expand Down
5 changes: 5 additions & 0 deletions src/test/ui/compare-method/region-extra-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ LL | fn renew<'b: 'a>(self) -> &'b mut [T];
...
LL | fn renew<'b: 'a>(self) -> &'b mut [T] where 'a: 'b {
| ^^ impl has extra requirement `'a: 'b`
|
help: copy the `where` clause predicates from the trait
|
LL | fn renew<'b: 'a>(self) -> &'b mut [T] where 'b: 'a {
| ~~~~~~~~~~~~

error: aborting due to previous error

Expand Down
6 changes: 6 additions & 0 deletions src/test/ui/compare-method/region-extra.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ LL | fn foo();
...
LL | fn foo() where 'a: 'b { }
| ^^ impl has extra requirement `'a: 'b`
|
help: remove the `where` clause
|
LL - fn foo() where 'a: 'b { }
LL + fn foo() { }
|

error: aborting due to previous error

Expand Down
5 changes: 5 additions & 0 deletions src/test/ui/generic-associated-types/impl_bounds.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ LL | type B<'a, 'b> where 'a: 'b;
...
LL | type B<'a, 'b> = (&'a(), &'b ()) where 'b: 'a;
| ^^ impl has extra requirement `'b: 'a`
|
help: copy the `where` clause predicates from the trait
|
LL | type B<'a, 'b> = (&'a(), &'b ()) where 'a: 'b;
| ~~~~~~~~~~~~

error[E0277]: the trait bound `T: Copy` is not satisfied
--> $DIR/impl_bounds.rs:18:33
Expand Down
6 changes: 5 additions & 1 deletion src/test/ui/generic-associated-types/issue-90014.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ LL | type Fut<'a> where Self: 'a;
| ------------ definition of `Fut` from trait
...
LL | type Fut<'a> = impl Future<Output = ()>;
| ^^^^^^^^^^^^^^^^^^^^^^^^- help: try copying this clause from the trait: `where Self: 'a`
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
note: type must outlive the lifetime `'a` as defined here
--> $DIR/issue-90014.rs:13:14
|
LL | type Fut<'a> = impl Future<Output = ()>;
| ^^
help: copy the `where` clause predicates from the trait
|
LL | type Fut<'a> = impl Future<Output = ()> where Self: 'a;
| ++++++++++++++

error: aborting due to previous error

Expand Down
6 changes: 5 additions & 1 deletion src/test/ui/generic-associated-types/issue-91883.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ LL | type Cursor<'tx>: Cursor<'tx>
| ----------------------------- definition of `Cursor` from trait
...
LL | type Cursor<'tx> = CursorImpl<'tx>;
| ^^^^^^^^^^^^^^^- help: try copying these clauses from the trait: `where 'db: 'tx, Self: 'tx`
| ^^^^^^^^^^^^^^^
|
note: lifetime parameter instantiated with the lifetime `'db` as defined here
--> $DIR/issue-91883.rs:29:6
Expand All @@ -17,6 +17,10 @@ note: but lifetime parameter must outlive the lifetime `'tx` as defined here
|
LL | type Cursor<'tx> = CursorImpl<'tx>;
| ^^^
help: copy the `where` clause predicates from the trait
|
LL | type Cursor<'tx> = CursorImpl<'tx> where 'db: 'tx, Self: 'tx;
| +++++++++++++++++++++++++

error: aborting due to previous error

Expand Down
6 changes: 5 additions & 1 deletion src/test/ui/generic-associated-types/issue-92033.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ LL | type TextureIter<'a>: Iterator<Item = &'a Texture>
| -------------------------------------------------- definition of `TextureIter` from trait
...
LL | type TextureIter<'a> = std::option::IntoIter<&'a Texture>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try copying this clause from the trait: `where Self: 'a`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: type must outlive the lifetime `'a` as defined here
--> $DIR/issue-92033.rs:20:22
|
LL | type TextureIter<'a> = std::option::IntoIter<&'a Texture>;
| ^^
help: copy the `where` clause predicates from the trait
|
LL | type TextureIter<'a> = std::option::IntoIter<&'a Texture> where Self: 'a;
| ++++++++++++++

error: aborting due to previous error

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
trait Foo {
type T<'a1, 'b1>
where
'a1: 'b1;
}

impl Foo for () {
type T<'a2, 'b2> = () where 'b2: 'a2;
//~^ ERROR impl has stricter requirements than trait
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0276]: impl has stricter requirements than trait
--> $DIR/mismatched-where-clause-regions.rs:8:38
|
LL | type T<'a1, 'b1>
| ---------------- definition of `T` from trait
...
LL | type T<'a2, 'b2> = () where 'b2: 'a2;
| ^^^ impl has extra requirement `'b2: 'a2`
|
help: copy the `where` clause predicates from the trait
|
LL | type T<'a2, 'b2> = () where 'a2: 'b2;
| ~~~~~~~~~~~~~~

error: aborting due to previous error

For more information about this error, try `rustc --explain E0276`.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ LL | type Assoc<'a, 'b>;
...
LL | type Assoc<'a, 'b> = () where 'a: 'b;
| ^^ impl has extra requirement `'a: 'b`
|
help: remove the `where` clause
|
LL - type Assoc<'a, 'b> = () where 'a: 'b;
LL + type Assoc<'a, 'b> = () ;
|

error: aborting due to previous error

Expand Down

0 comments on commit f9fab1f

Please sign in to comment.