Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
*err.long_ty_path() = long_ty_file;

let mut suggested = false;
let mut noted_missing_impl = false;
if is_try_conversion || is_question_mark {
suggested = self.try_conversion_context(&obligation, main_trait_predicate, &mut err);
(suggested, noted_missing_impl) = self.try_conversion_context(&obligation, main_trait_predicate, &mut err);
}

if let Some(ret_span) = self.return_type_span(&obligation) {
Expand Down Expand Up @@ -335,6 +336,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
return err.emit();
}

let ty_span = match leaf_trait_predicate.self_ty().skip_binder().kind() {
ty::Adt(def, _) if def.did().is_local()
&& !self.can_suggest_derive(&obligation, leaf_trait_predicate) => self.tcx.def_span(def.did()),
_ => DUMMY_SP,
};
if let Some(s) = label {
// If it has a custom `#[rustc_on_unimplemented]`
// error message, let's display it as the label!
Expand All @@ -347,15 +353,28 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// Don't say "the trait `FromResidual<Option<Infallible>>` is
// not implemented for `Result<T, E>`".
{
err.help(explanation);
// We do this just so that the JSON output's `help` position is the
// right one and not `file.rs:1:1`. The render is the same.
if ty_span == DUMMY_SP {
err.help(explanation);
} else {
err.span_help(ty_span, explanation);
}
}
} else if let Some(custom_explanation) = safe_transmute_explanation {
err.span_label(span, custom_explanation);
} else if explanation.len() > self.tcx.sess.diagnostic_width() {
} else if (explanation.len() > self.tcx.sess.diagnostic_width() || ty_span != DUMMY_SP) && !noted_missing_impl {
// Really long types don't look good as span labels, instead move it
// to a `help`.
err.span_label(span, "unsatisfied trait bound");
err.help(explanation);

// We do this just so that the JSON output's `help` position is the
// right one and not `file.rs:1:1`. The render is the same.
if ty_span == DUMMY_SP {
err.help(explanation);
} else {
err.span_help(ty_span, explanation);
}
} else {
err.span_label(span, explanation);
}
Expand Down Expand Up @@ -939,7 +958,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
obligation: &PredicateObligation<'tcx>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
err: &mut Diag<'_>,
) -> bool {
) -> (bool, bool) {
let span = obligation.cause.span;
/// Look for the (direct) sub-expr of `?`, and return it if it's a `.` method call.
struct FindMethodSubexprOfTry {
Expand All @@ -959,21 +978,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id);
let Some(body_id) = self.tcx.hir_node(hir_id).body_id() else { return false };
let Some(body_id) = self.tcx.hir_node(hir_id).body_id() else { return (false, false) };
let ControlFlow::Break(expr) =
(FindMethodSubexprOfTry { search_span: span }).visit_body(self.tcx.hir_body(body_id))
else {
return false;
return (false, false);
};
let Some(typeck) = &self.typeck_results else {
return false;
return (false, false);
};
let ObligationCauseCode::QuestionMark = obligation.cause.code().peel_derives() else {
return false;
return (false, false);
};
let self_ty = trait_pred.skip_binder().self_ty();
let found_ty = trait_pred.skip_binder().trait_ref.args.get(1).and_then(|a| a.as_type());
self.note_missing_impl_for_question_mark(err, self_ty, found_ty, trait_pred);
let noted_missing_impl =
self.note_missing_impl_for_question_mark(err, self_ty, found_ty, trait_pred);

let mut prev_ty = self.resolve_vars_if_possible(
typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)),
Expand Down Expand Up @@ -1137,7 +1157,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
prev = Some(err_ty);
}
suggested
(suggested, noted_missing_impl)
}

fn note_missing_impl_for_question_mark(
Expand All @@ -1146,7 +1166,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
self_ty: Ty<'_>,
found_ty: Option<Ty<'_>>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
) -> bool {
match (self_ty.kind(), found_ty) {
(ty::Adt(def, _), Some(ty))
if let ty::Adt(found, _) = ty.kind()
Expand Down Expand Up @@ -1187,8 +1207,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
format!("`{ty}` needs to implement `Into<{self_ty}>`"),
);
}
_ => {}
_ => return false,
}
true
}

fn report_const_param_not_wf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3853,59 +3853,71 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}

pub fn suggest_derive(
pub fn can_suggest_derive(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
) -> bool {
if trait_pred.polarity() == ty::PredicatePolarity::Negative {
return;
return false;
}
let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else {
return;
return false;
};
let (adt, args) = match trait_pred.skip_binder().self_ty().kind() {
ty::Adt(adt, args) if adt.did().is_local() => (adt, args),
_ => return,
_ => return false,
};
let can_derive = {
let is_derivable_trait = match diagnostic_name {
sym::Default => !adt.is_enum(),
sym::PartialEq | sym::PartialOrd => {
let rhs_ty = trait_pred.skip_binder().trait_ref.args.type_at(1);
trait_pred.skip_binder().self_ty() == rhs_ty
}
sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true,
_ => false,
};
is_derivable_trait &&
// Ensure all fields impl the trait.
adt.all_fields().all(|field| {
let field_ty = ty::GenericArg::from(field.ty(self.tcx, args));
let trait_args = match diagnostic_name {
sym::PartialEq | sym::PartialOrd => {
Some(field_ty)
}
_ => None,
};
let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate {
trait_ref: ty::TraitRef::new(self.tcx,
trait_pred.def_id(),
[field_ty].into_iter().chain(trait_args),
),
..*tr
});
let field_obl = Obligation::new(
self.tcx,
obligation.cause.clone(),
obligation.param_env,
trait_pred,
);
self.predicate_must_hold_modulo_regions(&field_obl)
})
let is_derivable_trait = match diagnostic_name {
sym::Default => !adt.is_enum(),
sym::PartialEq | sym::PartialOrd => {
let rhs_ty = trait_pred.skip_binder().trait_ref.args.type_at(1);
trait_pred.skip_binder().self_ty() == rhs_ty
}
sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true,
_ => false,
};
is_derivable_trait &&
// Ensure all fields impl the trait.
adt.all_fields().all(|field| {
let field_ty = ty::GenericArg::from(field.ty(self.tcx, args));
let trait_args = match diagnostic_name {
sym::PartialEq | sym::PartialOrd => {
Some(field_ty)
}
_ => None,
};
let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate {
trait_ref: ty::TraitRef::new(self.tcx,
trait_pred.def_id(),
[field_ty].into_iter().chain(trait_args),
),
..*tr
});
let field_obl = Obligation::new(
self.tcx,
obligation.cause.clone(),
obligation.param_env,
trait_pred,
);
self.predicate_must_hold_modulo_regions(&field_obl)
})
}

pub fn suggest_derive(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else {
return;
};
let adt = match trait_pred.skip_binder().self_ty().kind() {
ty::Adt(adt, _) if adt.did().is_local() => adt,
_ => return,
};
if can_derive {
if self.can_suggest_derive(obligation, trait_pred) {
err.span_suggestion_verbose(
self.tcx.def_span(adt.did()).shrink_to_lo(),
format!(
Expand Down
7 changes: 6 additions & 1 deletion tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ error[E0277]: the trait bound `NotAValidResultType: VisitorResult` is not satisf
--> $DIR/rustc-dev-remap.rs:LL:COL
|
LL | type Result = NotAValidResultType;
| ^^^^^^^^^^^^^^^^^^^ the trait `VisitorResult` is not implemented for `NotAValidResultType`
| ^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
help: the trait `VisitorResult` is not implemented for `NotAValidResultType`
--> $DIR/rustc-dev-remap.rs:LL:COL
|
LL | struct NotAValidResultType;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: the following other types implement trait `VisitorResult`:
()
ControlFlow<T>
Expand Down
7 changes: 6 additions & 1 deletion tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ error[E0277]: the trait bound `NotAValidResultType: VisitorResult` is not satisf
--> $DIR/rustc-dev-remap.rs:LL:COL
|
LL | type Result = NotAValidResultType;
| ^^^^^^^^^^^^^^^^^^^ the trait `VisitorResult` is not implemented for `NotAValidResultType`
| ^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
help: the trait `VisitorResult` is not implemented for `NotAValidResultType`
--> $DIR/rustc-dev-remap.rs:LL:COL
|
LL | struct NotAValidResultType;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: the following other types implement trait `VisitorResult`:
()
ControlFlow<T>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ LL | #[derive(Diagnostic)]
| ---------- required by a bound introduced by this call
...
LL | arg: NotIntoDiagArg,
| ^^^^^^^^^^^^^^ the trait `IntoDiagArg` is not implemented for `NotIntoDiagArg`
| ^^^^^^^^^^^^^^ unsatisfied trait bound
|
help: the trait `IntoDiagArg` is not implemented for `NotIntoDiagArg`
--> $DIR/diagnostic-derive-doc-comment-field.rs:28:1
|
LL | struct NotIntoDiagArg;
| ^^^^^^^^^^^^^^^^^^^^^
= help: normalized in stderr
note: required by a bound in `Diag::<'a, G>::arg`
--> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC
Expand All @@ -19,8 +24,13 @@ LL | #[derive(Subdiagnostic)]
| ------------- required by a bound introduced by this call
...
LL | arg: NotIntoDiagArg,
| ^^^^^^^^^^^^^^ the trait `IntoDiagArg` is not implemented for `NotIntoDiagArg`
| ^^^^^^^^^^^^^^ unsatisfied trait bound
|
help: the trait `IntoDiagArg` is not implemented for `NotIntoDiagArg`
--> $DIR/diagnostic-derive-doc-comment-field.rs:28:1
|
LL | struct NotIntoDiagArg;
| ^^^^^^^^^^^^^^^^^^^^^
= help: normalized in stderr
note: required by a bound in `Diag::<'a, G>::arg`
--> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -637,8 +637,13 @@ LL | #[derive(Diagnostic)]
| ---------- required by a bound introduced by this call
...
LL | other: Hello,
| ^^^^^ the trait `IntoDiagArg` is not implemented for `Hello`
| ^^^^^ unsatisfied trait bound
|
help: the trait `IntoDiagArg` is not implemented for `Hello`
--> $DIR/diagnostic-derive.rs:40:1
|
LL | struct Hello {}
| ^^^^^^^^^^^^
= help: normalized in stderr
note: required by a bound in `Diag::<'a, G>::arg`
--> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC
Expand Down
14 changes: 12 additions & 2 deletions tests/ui/associated-consts/issue-105330.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,13 @@ error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied
--> $DIR/issue-105330.rs:12:11
|
LL | foo::<Demo>()();
| ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo`
| ^^^^ unsatisfied trait bound
|
help: the trait `TraitWAssocConst` is not implemented for `Demo`
--> $DIR/issue-105330.rs:4:1
|
LL | pub struct Demo {}
| ^^^^^^^^^^^^^^^
note: required by a bound in `foo`
--> $DIR/issue-105330.rs:11:11
|
Expand All @@ -75,8 +80,13 @@ error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied
--> $DIR/issue-105330.rs:20:11
|
LL | foo::<Demo>();
| ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo`
| ^^^^ unsatisfied trait bound
|
help: the trait `TraitWAssocConst` is not implemented for `Demo`
--> $DIR/issue-105330.rs:4:1
|
LL | pub struct Demo {}
| ^^^^^^^^^^^^^^^
note: required by a bound in `foo`
--> $DIR/issue-105330.rs:11:11
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,13 @@ error[E0277]: the trait bound `NotClone: IsU8<NotClone>` is not satisfied
--> $DIR/defaults-suitability.rs:59:18
|
LL | type Assoc = NotClone;
| ^^^^^^^^ the trait `IsU8<NotClone>` is not implemented for `NotClone`
| ^^^^^^^^ unsatisfied trait bound
|
help: the trait `IsU8<NotClone>` is not implemented for `NotClone`
--> $DIR/defaults-suitability.rs:12:1
|
LL | struct NotClone;
| ^^^^^^^^^^^^^^^
note: required by a bound in `D::Assoc`
--> $DIR/defaults-suitability.rs:56:18
|
Expand Down
7 changes: 6 additions & 1 deletion tests/ui/associated-types/defaults-suitability.next.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,13 @@ error[E0277]: the trait bound `NotClone: IsU8<NotClone>` is not satisfied
--> $DIR/defaults-suitability.rs:59:18
|
LL | type Assoc = NotClone;
| ^^^^^^^^ the trait `IsU8<NotClone>` is not implemented for `NotClone`
| ^^^^^^^^ unsatisfied trait bound
|
help: the trait `IsU8<NotClone>` is not implemented for `NotClone`
--> $DIR/defaults-suitability.rs:12:1
|
LL | struct NotClone;
| ^^^^^^^^^^^^^^^
note: required by a bound in `D::Assoc`
--> $DIR/defaults-suitability.rs:56:18
|
Expand Down
7 changes: 6 additions & 1 deletion tests/ui/associated-types/issue-64855.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ error[E0277]: the trait bound `Bar<T>: Foo` is not satisfied
--> $DIR/issue-64855.rs:9:19
|
LL | pub struct Bar<T>(<Self as Foo>::Type) where Self: ;
| ^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bar<T>`
| ^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
help: the trait `Foo` is not implemented for `Bar<T>`
--> $DIR/issue-64855.rs:9:1
|
LL | pub struct Bar<T>(<Self as Foo>::Type) where Self: ;
| ^^^^^^^^^^^^^^^^^
help: this trait has no implementations, consider adding one
--> $DIR/issue-64855.rs:5:1
|
Expand Down
Loading
Loading