Provide more context on recursive impl evaluation overflow

When an associated type `Self::Assoc` is part of a `where` clause,
we end up unable to evaluate the requirement and emit a E0275.

We now point at the associated type if specified in the `impl`. If
so, we also suggest using that type instead of `Self::Assoc`.
Otherwise, we explain that these are not allowed.

```
error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _`
  --> $DIR/impl-wf-cycle-1.rs:15:1
   |
LL | / impl<T: Grault> Grault for (T,)
LL | |
LL | | where
LL | |     Self::A: Baz,
LL | |     Self::B: Fiz,
   | |_________________^
LL |   {
LL |       type A = ();
   |       ------ associated type `<(T,) as Grault>::A` is specified here
   |
note: required for `(T,)` to implement `Grault`
  --> $DIR/impl-wf-cycle-1.rs:15:17
   |
LL | impl<T: Grault> Grault for (T,)
   |                 ^^^^^^     ^^^^
...
LL |     Self::A: Baz,
   |              --- unsatisfied trait bound introduced here
   = note: 1 redundant requirement hidden
   = note: required for `(T,)` to implement `Grault`
help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound
   |
LL -     Self::A: Baz,
LL +     ,
   |
```
```
error[E0275]: overflow evaluating the requirement `<T as B>::Type == <T as B>::Type`
  --> $DIR/impl-wf-cycle-3.rs:7:1
   |
LL | / impl<T> B for T
LL | | where
LL | |     T: A<Self::Type>,
   | |_____________________^
LL |   {
LL |       type Type = bool;
   |       --------- associated type `<T as B>::Type` is specified here
   |
note: required for `T` to implement `B`
  --> $DIR/impl-wf-cycle-3.rs:7:9
   |
LL | impl<T> B for T
   |         ^     ^
LL | where
LL |     T: A<Self::Type>,
   |        ------------- unsatisfied trait bound introduced here
help: replace the associated type with the type specified in this `impl`
   |
LL |     T: A<bool>,
   |          ~~~~
```
```
error[E0275]: overflow evaluating the requirement `<T as Filter>::ToMatch == <T as Filter>::ToMatch`
  --> $DIR/impl-wf-cycle-4.rs:5:1
   |
LL | / impl<T> Filter for T
LL | | where
LL | |     T: Fn(Self::ToMatch),
   | |_________________________^
   |
note: required for `T` to implement `Filter`
  --> $DIR/impl-wf-cycle-4.rs:5:9
   |
LL | impl<T> Filter for T
   |         ^^^^^^     ^
LL | where
LL |     T: Fn(Self::ToMatch),
   |        ----------------- unsatisfied trait bound introduced here
note: associated types for the current `impl` cannot be restricted in `where` clauses
  --> $DIR/impl-wf-cycle-4.rs:7:11
   |
LL |     T: Fn(Self::ToMatch),
   |           ^^^^^^^^^^^^^
```

Fix #116925
This commit is contained in:
Esteban Küber 2023-12-28 19:09:04 +00:00
parent 88189a71e4
commit 2c2f3ed2c4
7 changed files with 252 additions and 45 deletions

View file

@ -7,7 +7,7 @@
use crate::errors;
use crate::infer::InferCtxt;
use crate::traits::{NormalizeExt, ObligationCtxt};
use crate::traits::{ImplDerivedObligationCause, NormalizeExt, ObligationCtxt};
use hir::def::CtorOf;
use rustc_data_structures::fx::FxHashSet;
@ -2973,7 +2973,7 @@ fn note_obligation_cause_code<T>(
| ObligationCauseCode::ObjectTypeBound(..) => {}
ObligationCauseCode::RustCall => {
if let Some(pred) = predicate.to_opt_poly_trait_pred()
&& Some(pred.def_id()) == self.tcx.lang_items().sized_trait()
&& Some(pred.def_id()) == tcx.lang_items().sized_trait()
{
err.note("argument required to be sized due to `extern \"rust-call\"` ABI");
}
@ -3022,15 +3022,15 @@ fn note_obligation_cause_code<T>(
let def_id = trait_pred.def_id();
let visible_item = if let Some(local) = def_id.as_local() {
// Check for local traits being reachable.
let vis = &self.tcx.resolutions(()).effective_visibilities;
let vis = &tcx.resolutions(()).effective_visibilities;
// Account for non-`pub` traits in the root of the local crate.
let is_locally_reachable = self.tcx.parent(def_id).is_crate_root();
let is_locally_reachable = tcx.parent(def_id).is_crate_root();
vis.is_reachable(local) || is_locally_reachable
} else {
// Check for foreign traits being reachable.
self.tcx.visible_parent_map(()).get(&def_id).is_some()
tcx.visible_parent_map(()).get(&def_id).is_some()
};
if Some(def_id) == self.tcx.lang_items().sized_trait()
if Some(def_id) == tcx.lang_items().sized_trait()
&& let Some(hir::Node::TraitItem(hir::TraitItem {
ident,
kind: hir::TraitItemKind::Type(bounds, None),
@ -3039,7 +3039,7 @@ fn note_obligation_cause_code<T>(
// Do not suggest relaxing if there is an explicit `Sized` obligation.
&& !bounds.iter()
.filter_map(|bound| bound.trait_ref())
.any(|tr| tr.trait_def_id() == self.tcx.lang_items().sized_trait())
.any(|tr| tr.trait_def_id() == tcx.lang_items().sized_trait())
{
let (span, separator) = if let [.., last] = bounds {
(last.span().shrink_to_hi(), " +")
@ -3102,10 +3102,8 @@ fn note_obligation_cause_code<T>(
}
ObligationCauseCode::Coercion { source, target } => {
let mut file = None;
let source =
self.tcx.short_ty_string(self.resolve_vars_if_possible(source), &mut file);
let target =
self.tcx.short_ty_string(self.resolve_vars_if_possible(target), &mut file);
let source = tcx.short_ty_string(self.resolve_vars_if_possible(source), &mut file);
let target = tcx.short_ty_string(self.resolve_vars_if_possible(target), &mut file);
err.note(with_forced_trimmed_paths!(format!(
"required for the cast from `{source}` to `{target}`",
)));
@ -3154,7 +3152,7 @@ fn note_obligation_cause_code<T>(
);
}
if self.tcx.sess.is_nightly_build()
if tcx.sess.is_nightly_build()
&& matches!(is_constable, IsConstable::Fn | IsConstable::Ctor)
{
err.help(
@ -3164,8 +3162,8 @@ fn note_obligation_cause_code<T>(
}
}
ObligationCauseCode::VariableType(hir_id) => {
let parent_node = self.tcx.hir().parent_id(hir_id);
match self.tcx.opt_hir_node(parent_node) {
let parent_node = tcx.hir().parent_id(hir_id);
match tcx.opt_hir_node(parent_node) {
Some(Node::Local(hir::Local { ty: Some(ty), .. })) => {
err.span_suggestion_verbose(
ty.span.shrink_to_lo(),
@ -3203,7 +3201,7 @@ fn note_obligation_cause_code<T>(
err.note("all local variables must have a statically known size");
}
}
if !self.tcx.features().unsized_locals {
if !tcx.features().unsized_locals {
err.help("unsized locals are gated as an unstable feature");
}
}
@ -3285,7 +3283,7 @@ fn note_obligation_cause_code<T>(
err.note("all function arguments must have a statically known size");
}
if tcx.sess.opts.unstable_features.is_nightly_build()
&& !self.tcx.features().unsized_fn_params
&& !tcx.features().unsized_fn_params
{
err.help("unsized fn params are gated as an unstable feature");
}
@ -3354,7 +3352,7 @@ fn note_obligation_cause_code<T>(
"all values captured by value by a closure must have a statically known size",
);
let hir::ExprKind::Closure(closure) =
self.tcx.hir_node_by_def_id(closure_def_id).expect_expr().kind
tcx.hir_node_by_def_id(closure_def_id).expect_expr().kind
else {
bug!("expected closure in SizedClosureCapture obligation");
};
@ -3365,7 +3363,7 @@ fn note_obligation_cause_code<T>(
}
}
ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => {
let what = match self.tcx.coroutine_kind(coroutine_def_id) {
let what = match tcx.coroutine_kind(coroutine_def_id) {
None
| Some(hir::CoroutineKind::Coroutine(_))
| Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => {
@ -3416,10 +3414,10 @@ fn note_obligation_cause_code<T>(
'print: {
if !is_upvar_tys_infer_tuple {
let mut file = None;
let ty_str = self.tcx.short_ty_string(ty, &mut file);
let ty_str = tcx.short_ty_string(ty, &mut file);
let msg = format!("required because it appears within the type `{ty_str}`");
match ty.kind() {
ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) {
ty::Adt(def, _) => match tcx.opt_item_ident(def.did()) {
Some(ident) => err.span_note(ident.span, msg),
None => err.note(msg),
},
@ -3442,7 +3440,7 @@ fn note_obligation_cause_code<T>(
{
break 'print;
}
err.span_note(self.tcx.def_span(def_id), msg)
err.span_note(tcx.def_span(def_id), msg)
}
ty::CoroutineWitness(def_id, args) => {
use std::fmt::Write;
@ -3459,7 +3457,7 @@ fn note_obligation_cause_code<T>(
err.note(msg.trim_end_matches(", ").to_string())
}
ty::Coroutine(def_id, _) => {
let sp = self.tcx.def_span(def_id);
let sp = tcx.def_span(def_id);
// Special-case this to say "async block" instead of `[static coroutine]`.
let kind = tcx.coroutine_kind(def_id).unwrap();
@ -3471,7 +3469,7 @@ fn note_obligation_cause_code<T>(
)
}
ty::Closure(def_id, _) => err.span_note(
self.tcx.def_span(def_id),
tcx.def_span(def_id),
"required because it's used within this closure",
),
ty::Str => err.note("`str` is considered to contain a `[u8]` slice for auto trait purposes"),
@ -3515,14 +3513,12 @@ fn note_obligation_cause_code<T>(
self.resolve_vars_if_possible(data.derived.parent_trait_pred);
let parent_def_id = parent_trait_pred.def_id();
let mut file = None;
let self_ty =
self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file);
let msg = format!(
"required for `{self_ty}` to implement `{}`",
parent_trait_pred.print_modifiers_and_trait_path()
);
let self_ty_str =
tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file);
let trait_name = parent_trait_pred.print_modifiers_and_trait_path().to_string();
let msg = format!("required for `{self_ty_str}` to implement `{trait_name}`");
let mut is_auto_trait = false;
match self.tcx.hir().get_if_local(data.impl_or_alias_def_id) {
match tcx.hir().get_if_local(data.impl_or_alias_def_id) {
Some(Node::Item(hir::Item {
kind: hir::ItemKind::Trait(is_auto, ..),
ident,
@ -3534,7 +3530,7 @@ fn note_obligation_cause_code<T>(
err.span_note(ident.span, msg);
}
Some(Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }),
kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, generics, .. }),
..
})) => {
let mut spans = Vec::with_capacity(2);
@ -3561,6 +3557,15 @@ fn note_obligation_cause_code<T>(
);
}
err.span_note(spans, msg);
point_at_assoc_type_restriction(
tcx,
err,
&self_ty_str,
&trait_name,
predicate,
&generics,
&data,
);
}
_ => {
err.note(msg);
@ -3614,9 +3619,8 @@ fn note_obligation_cause_code<T>(
pluralize!(count)
));
let mut file = None;
let self_ty = self
.tcx
.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file);
let self_ty =
tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file);
err.note(format!(
"required for `{self_ty}` to implement `{}`",
parent_trait_pred.print_modifiers_and_trait_path()
@ -3674,10 +3678,7 @@ fn note_obligation_cause_code<T>(
multispan.push_span_label(span, "required by this bound");
err.span_note(
multispan,
format!(
"required by a bound on the type alias `{}`",
self.infcx.tcx.item_name(def_id)
),
format!("required by a bound on the type alias `{}`", tcx.item_name(def_id)),
);
}
ObligationCauseCode::FunctionArgumentObligation {
@ -3708,25 +3709,23 @@ fn note_obligation_cause_code<T>(
});
}
ObligationCauseCode::CompareImplItemObligation { trait_item_def_id, kind, .. } => {
let item_name = self.tcx.item_name(trait_item_def_id);
let item_name = tcx.item_name(trait_item_def_id);
let msg = format!(
"the requirement `{predicate}` appears on the `impl`'s {kind} \
`{item_name}` but not on the corresponding trait's {kind}",
);
let sp = self
.tcx
let sp = tcx
.opt_item_ident(trait_item_def_id)
.map(|i| i.span)
.unwrap_or_else(|| self.tcx.def_span(trait_item_def_id));
.unwrap_or_else(|| tcx.def_span(trait_item_def_id));
let mut assoc_span: MultiSpan = sp.into();
assoc_span.push_span_label(
sp,
format!("this trait's {kind} doesn't have the requirement `{predicate}`"),
);
if let Some(ident) = self
.tcx
if let Some(ident) = tcx
.opt_associated_item(trait_item_def_id)
.and_then(|i| self.tcx.opt_item_ident(i.container_id(self.tcx)))
.and_then(|i| tcx.opt_item_ident(i.container_id(tcx)))
{
assoc_span.push_span_label(ident.span, "in this trait");
}
@ -4816,6 +4815,29 @@ fn hint_missing_borrow<'tcx>(
}
}
/// Collect all the paths that reference `Self`.
/// Used to suggest replacing associated types with an explicit type in `where` clauses.
#[derive(Debug)]
pub struct SelfVisitor<'v> {
pub paths: Vec<&'v hir::Ty<'v>>,
pub name: Option<Symbol>,
}
impl<'v> Visitor<'v> for SelfVisitor<'v> {
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
if let hir::TyKind::Path(path) = ty.kind
&& let hir::QPath::TypeRelative(inner_ty, segment) = path
&& (Some(segment.ident.name) == self.name || self.name.is_none())
&& let hir::TyKind::Path(inner_path) = inner_ty.kind
&& let hir::QPath::Resolved(None, inner_path) = inner_path
&& let Res::SelfTyAlias { .. } = inner_path.res
{
self.paths.push(ty);
}
hir::intravisit::walk_ty(self, ty);
}
}
/// Collect all the returned expressions within the input expression.
/// Used to point at the return spans when we want to suggest some change to them.
#[derive(Default)]
@ -5060,6 +5082,95 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>(
Some(sugg)
}
/// On `impl` evaluation cycles, look for `Self::AssocTy` restrictions in `where` clauses, explain
/// they are not allowed and if possible suggest alternatives.
fn point_at_assoc_type_restriction(
tcx: TyCtxt<'_>,
err: &mut Diagnostic,
self_ty_str: &str,
trait_name: &str,
predicate: ty::Predicate<'_>,
generics: &hir::Generics<'_>,
data: &ImplDerivedObligationCause<'_>,
) {
let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() else {
return;
};
let ty::ClauseKind::Projection(proj) = clause else {
return;
};
let name = tcx.item_name(proj.projection_ty.def_id);
for pred in generics.predicates {
let hir::WherePredicate::BoundPredicate(pred) = pred else {
continue;
};
for bound in pred.bounds {
let Some(trait_ref) = bound.trait_ref() else {
continue;
};
if bound.span() != data.span {
continue;
}
if let hir::TyKind::Path(path) = pred.bounded_ty.kind
&& let hir::QPath::TypeRelative(ty, segment) = path
&& segment.ident.name == name
&& let hir::TyKind::Path(inner_path) = ty.kind
&& let hir::QPath::Resolved(None, inner_path) = inner_path
&& let Res::SelfTyAlias { .. } = inner_path.res
{
err.span_suggestion_verbose(
pred.span, // FIXME: include the trailing comma.
"associated type for the current `impl` cannot be restricted in `where` \
clauses, remove this bound",
"",
Applicability::MaybeIncorrect,
);
}
if let Some(new) =
tcx.associated_items(data.impl_or_alias_def_id).find_by_name_and_kind(
tcx,
Ident::with_dummy_span(name),
ty::AssocKind::Type,
data.impl_or_alias_def_id,
)
{
// The associated type is specified in the `impl` we're
// looking at. Point at it.
let span = tcx.def_span(new.def_id);
err.span_label(
span,
format!(
"associated type `<{self_ty_str} as {trait_name}>::{name}` is specified \
here",
),
);
// Search for the associated type `Self::{name}`, get
// its type and suggest replacing the bound with it.
let mut visitor = SelfVisitor { paths: vec![], name: Some(name) };
visitor.visit_trait_ref(trait_ref);
for path in visitor.paths {
err.span_suggestion_verbose(
path.span,
"replace the associated type with the type specified in this `impl`",
format!("{}", tcx.type_of(new.def_id).skip_binder(),),
Applicability::MachineApplicable,
);
}
} else {
let mut visitor = SelfVisitor { paths: vec![], name: None };
visitor.visit_trait_ref(trait_ref);
let span: MultiSpan =
visitor.paths.iter().map(|p| p.span).collect::<Vec<Span>>().into();
err.span_note(
span,
"associated types for the current `impl` cannot be restricted in `where` \
clauses",
);
}
}
}
}
fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) {
let mut refs = vec![];

View file

@ -7,6 +7,9 @@ LL | | where
LL | | Self::A: Baz,
LL | | Self::B: Fiz,
| |_________________^
LL | {
LL | type A = ();
| ------ associated type `<(T,) as Grault>::A` is specified here
|
note: required for `(T,)` to implement `Grault`
--> $DIR/impl-wf-cycle-1.rs:15:17
@ -18,6 +21,11 @@ LL | Self::A: Baz,
| --- unsatisfied trait bound introduced here
= note: 1 redundant requirement hidden
= note: required for `(T,)` to implement `Grault`
help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound
|
LL - Self::A: Baz,
LL + ,
|
error: aborting due to 1 previous error

View file

@ -6,6 +6,9 @@ LL | |
LL | | where
LL | | Self::A: Copy,
| |__________________^
LL | {
LL | type A = ();
| ------ associated type `<(T,) as Grault>::A` is specified here
|
note: required for `(T,)` to implement `Grault`
--> $DIR/impl-wf-cycle-2.rs:7:17
@ -15,6 +18,11 @@ LL | impl<T: Grault> Grault for (T,)
...
LL | Self::A: Copy,
| ---- unsatisfied trait bound introduced here
help: associated type for the current `impl` cannot be restricted in `where` clauses, remove this bound
|
LL - Self::A: Copy,
LL + ,
|
error: aborting due to 1 previous error

View file

@ -0,0 +1,13 @@
trait A<T> {}
trait B {
type Type;
}
impl<T> B for T //~ ERROR overflow evaluating the requirement
where
T: A<Self::Type>,
{
type Type = bool;
}
fn main() {}

View file

@ -0,0 +1,27 @@
error[E0275]: overflow evaluating the requirement `<T as B>::Type == <T as B>::Type`
--> $DIR/impl-wf-cycle-3.rs:7:1
|
LL | / impl<T> B for T
LL | | where
LL | | T: A<Self::Type>,
| |_____________________^
LL | {
LL | type Type = bool;
| --------- associated type `<T as B>::Type` is specified here
|
note: required for `T` to implement `B`
--> $DIR/impl-wf-cycle-3.rs:7:9
|
LL | impl<T> B for T
| ^ ^
LL | where
LL | T: A<Self::Type>,
| ------------- unsatisfied trait bound introduced here
help: replace the associated type with the type specified in this `impl`
|
LL | T: A<bool>,
| ~~~~
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0275`.

View file

@ -0,0 +1,15 @@
trait Filter {
type ToMatch;
}
impl<T> Filter for T //~ ERROR overflow evaluating the requirement
where
T: Fn(Self::ToMatch),
{
}
struct JustFilter<F: Filter> {
filter: F,
}
fn main() {}

View file

@ -0,0 +1,25 @@
error[E0275]: overflow evaluating the requirement `<T as Filter>::ToMatch == <T as Filter>::ToMatch`
--> $DIR/impl-wf-cycle-4.rs:5:1
|
LL | / impl<T> Filter for T
LL | | where
LL | | T: Fn(Self::ToMatch),
| |_________________________^
|
note: required for `T` to implement `Filter`
--> $DIR/impl-wf-cycle-4.rs:5:9
|
LL | impl<T> Filter for T
| ^^^^^^ ^
LL | where
LL | T: Fn(Self::ToMatch),
| ----------------- unsatisfied trait bound introduced here
note: associated types for the current `impl` cannot be restricted in `where` clauses
--> $DIR/impl-wf-cycle-4.rs:7:11
|
LL | T: Fn(Self::ToMatch),
| ^^^^^^^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0275`.