mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Rollup merge of #106477 - Nathan-Fenner:nathanf/refined-error-span-trait-impl, r=compiler-errors
Refine error spans for "The trait bound `T: Trait` is not satisfied" when passing literal structs/tuples This PR adds a new heuristic which refines the error span reported for "`T: Trait` is not satisfied" errors, by "drilling down" into individual fields of structs/enums/tuples to point to the "problematic" value. Here's a self-contained example of the difference in error span: ```rs struct Burrito<Filling> { filling: Filling, } impl <Filling: Delicious> Delicious for Burrito<Filling> {} fn eat_delicious_food<Food: Delicious>(food: Food) {} fn will_type_error() { eat_delicious_food(Burrito { filling: Kale }); // ^~~~~~~~~~~~~~~~~~~~~~~~~ (before) The trait bound `Kale: Delicious` is not satisfied // ^~~~ (after) The trait bound `Kale: Delicious` is not satisfied } ``` (kale is fine, this is just a silly food-based example) Before this PR, the error span is identified as the entire argument to the generic function `eat_delicious_food`. However, since only `Kale` is the "problematic" part, we can point at it specifically. In particular, the primary error message itself mentions the missing `Kale: Delicious` trait bound, so it's much clearer if this part is called out explicitly. --- The _existing_ heuristic tries to label the right function argument in `point_at_arg_if_possible`. It goes something like this: - Look at the broken base trait `Food: Delicious` and find which generics it mentions (in this case, only `Food`) - Look at the parameter type definitions and find which of them mention `Filling` (in this case, only `food`) - If there is exactly one relevant parameter, label the corresponding argument with the error span, instead of the entire call This PR extends this heuristic by further refining the resulting expression span in the new `point_at_specific_expr_if_possible` function. For each `impl` in the (broken) chain, we apply the following strategy: The strategy to determine this span involves connecting information about our generic `impl` with information about our (struct) type and the (struct) literal expression: - Find the `impl` (`impl <Filling: Delicious> Delicious for Burrito<Filling>`) that links our obligation (`Kale: Delicious`) with the parent obligation (`Burrito<Kale>: Delicious`) - Find the "original" predicate constraint in the impl (`Filling: Delicious`) which produced our obligation. - Find all of the generics that are mentioned in the predicate (`Filling`). - Examine the `Self` type in the `impl`, and see which of its type argument(s) mention any of those generics. - Examing the definition for the `Self` type, and identify (for each of its variants) if there's a unique field which uses those generic arguments. - If there is a unique field mentioning the "blameable" arguments, use that field for the error span. Before we do any of this logic, we recursively call `point_at_specific_expr_if_possible` on the parent obligation. Hence we refine the `expr` "outwards-in" and bail at the first kind of expression/impl we don't recognize. This function returns a `Result<&Expr, &Expr>` - either way, it returns the `Expr` whose span should be reported as an error. If it is `Ok`, then it means it refined successfull. If it is `Err`, then it may be only a partial success - but it cannot be refined even further. --- I added a new test file which exercises this new behavior. A few existing tests were affected, since their error spans are now different. In one case, this leads to a different code suggestion for the autofix - although the new suggestion isn't _wrong_, it is different from what used to be. This change doesn't create any new errors or remove any existing ones, it just adjusts the spans where they're presented. --- Some considerations: right now, this check occurs in addition to some similar logic in `adjust_fulfillment_error_for_expr_obligation` function, which tidies up various kinds of error spans (not just trait-fulfillment error). It's possible that this new code would be better integrated into that function (or another one) - but I haven't looked into this yet. Although this code only occurs when there's a type error, it's definitely not as efficient as possible. In particular, there are definitely some cases where it degrades to quadratic performance (e.g. for a trait `impl` with 100+ generic parameters or 100 levels deep nesting of generic types). I'm not sure if these are realistic enough to worry about optimizing yet. There's also still a lot of repetition in some of the logic, where the behavior for different types (namely, `struct` vs `enum` variant) is _similar_ but not the same. --- I think the biggest win here is better targeting for tuples; in particular, if you're using tuples + traits to express variadic-like functions, the compiler can't tell you which part of a tuple has the wrong type, since the span will cover the entire argument. This change allows the individual field in the tuple to be highlighted, as in this example: ``` // NEW LL | want(Wrapper { value: (3, q) }); | ---- ^ the trait `T3` is not implemented for `Q` // OLD LL | want(Wrapper { value: (3, q) }); | ---- ^~~~~~~~~~~~~~~~~~~~~~~~~ the trait `T3` is not implemented for `Q` ``` Especially with large tuples, the existing error spans are not very effective at quickly narrowing down the source of the problem.
This commit is contained in:
commit
800221b5b8
@ -0,0 +1,457 @@
|
||||
use crate::FnCtxt;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_middle::ty::{self, DefIdTree, Ty};
|
||||
use rustc_trait_selection::traits;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/**
|
||||
* Recursively searches for the most-specific blamable expression.
|
||||
* For example, if you have a chain of constraints like:
|
||||
* - want `Vec<i32>: Copy`
|
||||
* - because `Option<Vec<i32>>: Copy` needs `Vec<i32>: Copy` because `impl <T: Copy> Copy for Option<T>`
|
||||
* - because `(Option<Vec<i32>, bool)` needs `Option<Vec<i32>>: Copy` because `impl <A: Copy, B: Copy> Copy for (A, B)`
|
||||
* then if you pass in `(Some(vec![1, 2, 3]), false)`, this helper `point_at_specific_expr_if_possible`
|
||||
* will find the expression `vec![1, 2, 3]` as the "most blameable" reason for this missing constraint.
|
||||
*
|
||||
* This function only updates the error span.
|
||||
*/
|
||||
pub fn blame_specific_expr_if_possible(
|
||||
&self,
|
||||
error: &mut traits::FulfillmentError<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) {
|
||||
// Whether it succeeded or failed, it likely made some amount of progress.
|
||||
// In the very worst case, it's just the same `expr` we originally passed in.
|
||||
let expr = match self.blame_specific_expr_if_possible_for_obligation_cause_code(
|
||||
&error.obligation.cause.code(),
|
||||
expr,
|
||||
) {
|
||||
Ok(expr) => expr,
|
||||
Err(expr) => expr,
|
||||
};
|
||||
|
||||
// Either way, use this expression to update the error span.
|
||||
// If it doesn't overlap the existing span at all, use the original span.
|
||||
// FIXME: It would possibly be better to do this more continuously, at each level...
|
||||
error.obligation.cause.span = expr
|
||||
.span
|
||||
.find_ancestor_in_same_ctxt(error.obligation.cause.span)
|
||||
.unwrap_or(error.obligation.cause.span);
|
||||
}
|
||||
|
||||
fn blame_specific_expr_if_possible_for_obligation_cause_code(
|
||||
&self,
|
||||
obligation_cause_code: &traits::ObligationCauseCode<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> {
|
||||
match obligation_cause_code {
|
||||
traits::ObligationCauseCode::ExprBindingObligation(_, _, _, _) => {
|
||||
// This is the "root"; we assume that the `expr` is already pointing here.
|
||||
// Therefore, we return `Ok` so that this `expr` can be refined further.
|
||||
Ok(expr)
|
||||
}
|
||||
traits::ObligationCauseCode::ImplDerivedObligation(impl_derived) => self
|
||||
.blame_specific_expr_if_possible_for_derived_predicate_obligation(
|
||||
impl_derived,
|
||||
expr,
|
||||
),
|
||||
_ => {
|
||||
// We don't recognize this kind of constraint, so we cannot refine the expression
|
||||
// any further.
|
||||
Err(expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// We want to achieve the error span in the following example:
|
||||
///
|
||||
/// ```ignore (just for demonstration)
|
||||
/// struct Burrito<Filling> {
|
||||
/// filling: Filling,
|
||||
/// }
|
||||
/// impl <Filling: Delicious> Delicious for Burrito<Filling> {}
|
||||
/// fn eat_delicious_food<Food: Delicious>(_food: Food) {}
|
||||
///
|
||||
/// fn will_type_error() {
|
||||
/// eat_delicious_food(Burrito { filling: Kale });
|
||||
/// } // ^--- The trait bound `Kale: Delicious`
|
||||
/// // is not satisfied
|
||||
/// ```
|
||||
///
|
||||
/// Without calling this function, the error span will cover the entire argument expression.
|
||||
///
|
||||
/// Before we do any of this logic, we recursively call `point_at_specific_expr_if_possible` on the parent
|
||||
/// obligation. Hence we refine the `expr` "outwards-in" and bail at the first kind of expression/impl we don't recognize.
|
||||
///
|
||||
/// This function returns a `Result<&Expr, &Expr>` - either way, it returns the `Expr` whose span should be
|
||||
/// reported as an error. If it is `Ok`, then it means it refined successfull. If it is `Err`, then it may be
|
||||
/// only a partial success - but it cannot be refined even further.
|
||||
fn blame_specific_expr_if_possible_for_derived_predicate_obligation(
|
||||
&self,
|
||||
obligation: &traits::ImplDerivedObligationCause<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> {
|
||||
// First, we attempt to refine the `expr` for our span using the parent obligation.
|
||||
// If this cannot be done, then we are already stuck, so we stop early (hence the use
|
||||
// of the `?` try operator here).
|
||||
let expr = self.blame_specific_expr_if_possible_for_obligation_cause_code(
|
||||
&*obligation.derived.parent_code,
|
||||
expr,
|
||||
)?;
|
||||
|
||||
// This is the "trait" (meaning, the predicate "proved" by this `impl`) which provides the `Self` type we care about.
|
||||
// For the purposes of this function, we hope that it is a `struct` type, and that our current `expr` is a literal of
|
||||
// that struct type.
|
||||
let impl_trait_self_ref: Option<ty::TraitRef<'tcx>> =
|
||||
self.tcx.impl_trait_ref(obligation.impl_def_id).map(|impl_def| impl_def.skip_binder());
|
||||
|
||||
let Some(impl_trait_self_ref) = impl_trait_self_ref else {
|
||||
// It is possible that this is absent. In this case, we make no progress.
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
// We only really care about the `Self` type itself, which we extract from the ref.
|
||||
let impl_self_ty: Ty<'tcx> = impl_trait_self_ref.self_ty();
|
||||
|
||||
let impl_predicates: ty::GenericPredicates<'tcx> =
|
||||
self.tcx.predicates_of(obligation.impl_def_id);
|
||||
let Some(impl_predicate_index) = obligation.impl_def_predicate_index else {
|
||||
// We don't have the index, so we can only guess.
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
if impl_predicate_index >= impl_predicates.predicates.len() {
|
||||
// This shouldn't happen, but since this is only a diagnostic improvement, avoid breaking things.
|
||||
return Err(expr);
|
||||
}
|
||||
let relevant_broken_predicate: ty::PredicateKind<'tcx> =
|
||||
impl_predicates.predicates[impl_predicate_index].0.kind().skip_binder();
|
||||
|
||||
match relevant_broken_predicate {
|
||||
ty::PredicateKind::Clause(ty::Clause::Trait(broken_trait)) => {
|
||||
// ...
|
||||
self.blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
broken_trait.trait_ref.self_ty().into(),
|
||||
expr,
|
||||
impl_self_ty.into(),
|
||||
)
|
||||
}
|
||||
_ => Err(expr),
|
||||
}
|
||||
}
|
||||
|
||||
/// Drills into `expr` to arrive at the equivalent location of `find_generic_param` in `in_ty`.
|
||||
/// For example, given
|
||||
/// - expr: `(Some(vec![1, 2, 3]), false)`
|
||||
/// - param: `T`
|
||||
/// - in_ty: `(Option<Vec<T>, bool)`
|
||||
/// we would drill until we arrive at `vec![1, 2, 3]`.
|
||||
///
|
||||
/// If successful, we return `Ok(refined_expr)`. If unsuccesful, we return `Err(partially_refined_expr`),
|
||||
/// which will go as far as possible. For example, given `(foo(), false)` instead, we would drill to
|
||||
/// `foo()` and then return `Err("foo()")`.
|
||||
///
|
||||
/// This means that you can (and should) use the `?` try operator to chain multiple calls to this
|
||||
/// function with different types, since you can only continue drilling the second time if you
|
||||
/// succeeded the first time.
|
||||
fn blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
&self,
|
||||
param: ty::GenericArg<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
in_ty: ty::GenericArg<'tcx>,
|
||||
) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> {
|
||||
if param == in_ty {
|
||||
// The types match exactly, so we have drilled as far as we can.
|
||||
return Ok(expr);
|
||||
}
|
||||
|
||||
let ty::GenericArgKind::Type(in_ty) = in_ty.unpack() else {
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
if let (hir::ExprKind::Tup(expr_elements), ty::Tuple(in_ty_elements)) =
|
||||
(&expr.kind, in_ty.kind())
|
||||
{
|
||||
if in_ty_elements.len() != expr_elements.len() {
|
||||
return Err(expr);
|
||||
}
|
||||
// Find out which of `in_ty_elements` refer to `param`.
|
||||
// FIXME: It may be better to take the first if there are multiple,
|
||||
// just so that the error points to a smaller expression.
|
||||
let Some((drill_expr, drill_ty)) = Self::is_iterator_singleton(expr_elements.iter().zip( in_ty_elements.iter()).filter(|(_expr_elem, in_ty_elem)| {
|
||||
Self::find_param_in_ty((*in_ty_elem).into(), param)
|
||||
})) else {
|
||||
// The param is not mentioned, or it is mentioned in multiple indexes.
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
return self.blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
param,
|
||||
drill_expr,
|
||||
drill_ty.into(),
|
||||
);
|
||||
}
|
||||
|
||||
if let (
|
||||
hir::ExprKind::Struct(expr_struct_path, expr_struct_fields, _expr_struct_rest),
|
||||
ty::Adt(in_ty_adt, in_ty_adt_generic_args),
|
||||
) = (&expr.kind, in_ty.kind())
|
||||
{
|
||||
// First, confirm that this struct is the same one as in the types, and if so,
|
||||
// find the right variant.
|
||||
let Res::Def(expr_struct_def_kind, expr_struct_def_id) = self.typeck_results.borrow().qpath_res(expr_struct_path, expr.hir_id) else {
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
let variant_def_id = match expr_struct_def_kind {
|
||||
hir::def::DefKind::Struct => {
|
||||
if in_ty_adt.did() != expr_struct_def_id {
|
||||
// FIXME: Deal with type aliases?
|
||||
return Err(expr);
|
||||
}
|
||||
expr_struct_def_id
|
||||
}
|
||||
hir::def::DefKind::Variant => {
|
||||
// If this is a variant, its parent is the type definition.
|
||||
if in_ty_adt.did() != self.tcx.parent(expr_struct_def_id) {
|
||||
// FIXME: Deal with type aliases?
|
||||
return Err(expr);
|
||||
}
|
||||
expr_struct_def_id
|
||||
}
|
||||
_ => {
|
||||
return Err(expr);
|
||||
}
|
||||
};
|
||||
|
||||
// We need to know which of the generic parameters mentions our target param.
|
||||
// We expect that at least one of them does, since it is expected to be mentioned.
|
||||
let Some((drill_generic_index, generic_argument_type)) =
|
||||
Self::is_iterator_singleton(
|
||||
in_ty_adt_generic_args.iter().enumerate().filter(
|
||||
|(_index, in_ty_generic)| {
|
||||
Self::find_param_in_ty(*in_ty_generic, param)
|
||||
},
|
||||
),
|
||||
) else {
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
let struct_generic_parameters: &ty::Generics = self.tcx.generics_of(in_ty_adt.did());
|
||||
if drill_generic_index >= struct_generic_parameters.params.len() {
|
||||
return Err(expr);
|
||||
}
|
||||
|
||||
let param_to_point_at_in_struct = self.tcx.mk_param_from_def(
|
||||
struct_generic_parameters.param_at(drill_generic_index, self.tcx),
|
||||
);
|
||||
|
||||
// We make 3 steps:
|
||||
// Suppose we have a type like
|
||||
// ```ignore (just for demonstration)
|
||||
// struct ExampleStruct<T> {
|
||||
// enabled: bool,
|
||||
// item: Option<(usize, T, bool)>,
|
||||
// }
|
||||
//
|
||||
// f(ExampleStruct {
|
||||
// enabled: false,
|
||||
// item: Some((0, Box::new(String::new()), 1) }, true)),
|
||||
// });
|
||||
// ```
|
||||
// Here, `f` is passed a `ExampleStruct<Box<String>>`, but it wants
|
||||
// for `String: Copy`, which isn't true here.
|
||||
//
|
||||
// (1) First, we drill into `.item` and highlight that expression
|
||||
// (2) Then we use the template type `Option<(usize, T, bool)>` to
|
||||
// drill into the `T`, arriving at a `Box<String>` expression.
|
||||
// (3) Then we keep going, drilling into this expression using our
|
||||
// outer contextual information.
|
||||
|
||||
// (1) Find the (unique) field which mentions the type in our constraint:
|
||||
let (field_expr, field_type) = self
|
||||
.point_at_field_if_possible(
|
||||
in_ty_adt.did(),
|
||||
param_to_point_at_in_struct,
|
||||
variant_def_id,
|
||||
expr_struct_fields,
|
||||
)
|
||||
.ok_or(expr)?;
|
||||
|
||||
// (2) Continue drilling into the struct, ignoring the struct's
|
||||
// generic argument types.
|
||||
let expr = self.blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
param_to_point_at_in_struct,
|
||||
field_expr,
|
||||
field_type.into(),
|
||||
)?;
|
||||
|
||||
// (3) Continue drilling into the expression, having "passed
|
||||
// through" the struct entirely.
|
||||
return self.blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
param,
|
||||
expr,
|
||||
generic_argument_type,
|
||||
);
|
||||
}
|
||||
|
||||
if let (
|
||||
hir::ExprKind::Call(expr_callee, expr_args),
|
||||
ty::Adt(in_ty_adt, in_ty_adt_generic_args),
|
||||
) = (&expr.kind, in_ty.kind())
|
||||
{
|
||||
let hir::ExprKind::Path(expr_callee_path) = &expr_callee.kind else {
|
||||
// FIXME: This case overlaps with another one worth handling,
|
||||
// which should happen above since it applies to non-ADTs:
|
||||
// we can drill down into regular generic functions.
|
||||
return Err(expr);
|
||||
};
|
||||
// This is (possibly) a constructor call, like `Some(...)` or `MyStruct(a, b, c)`.
|
||||
|
||||
let Res::Def(expr_struct_def_kind, expr_ctor_def_id) = self.typeck_results.borrow().qpath_res(expr_callee_path, expr_callee.hir_id) else {
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
let variant_def_id = match expr_struct_def_kind {
|
||||
hir::def::DefKind::Ctor(hir::def::CtorOf::Struct, hir::def::CtorKind::Fn) => {
|
||||
if in_ty_adt.did() != self.tcx.parent(expr_ctor_def_id) {
|
||||
// FIXME: Deal with type aliases?
|
||||
return Err(expr);
|
||||
}
|
||||
self.tcx.parent(expr_ctor_def_id)
|
||||
}
|
||||
hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, hir::def::CtorKind::Fn) => {
|
||||
// If this is a variant, its parent is the type definition.
|
||||
if in_ty_adt.did() != self.tcx.parent(expr_ctor_def_id) {
|
||||
// FIXME: Deal with type aliases?
|
||||
return Err(expr);
|
||||
}
|
||||
expr_ctor_def_id
|
||||
}
|
||||
_ => {
|
||||
return Err(expr);
|
||||
}
|
||||
};
|
||||
|
||||
// We need to know which of the generic parameters mentions our target param.
|
||||
// We expect that at least one of them does, since it is expected to be mentioned.
|
||||
let Some((drill_generic_index, generic_argument_type)) =
|
||||
Self::is_iterator_singleton(
|
||||
in_ty_adt_generic_args.iter().enumerate().filter(
|
||||
|(_index, in_ty_generic)| {
|
||||
Self::find_param_in_ty(*in_ty_generic, param)
|
||||
},
|
||||
),
|
||||
) else {
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
let struct_generic_parameters: &ty::Generics = self.tcx.generics_of(in_ty_adt.did());
|
||||
if drill_generic_index >= struct_generic_parameters.params.len() {
|
||||
return Err(expr);
|
||||
}
|
||||
|
||||
let param_to_point_at_in_struct = self.tcx.mk_param_from_def(
|
||||
struct_generic_parameters.param_at(drill_generic_index, self.tcx),
|
||||
);
|
||||
|
||||
// We make 3 steps:
|
||||
// Suppose we have a type like
|
||||
// ```ignore (just for demonstration)
|
||||
// struct ExampleStruct<T> {
|
||||
// enabled: bool,
|
||||
// item: Option<(usize, T, bool)>,
|
||||
// }
|
||||
//
|
||||
// f(ExampleStruct {
|
||||
// enabled: false,
|
||||
// item: Some((0, Box::new(String::new()), 1) }, true)),
|
||||
// });
|
||||
// ```
|
||||
// Here, `f` is passed a `ExampleStruct<Box<String>>`, but it wants
|
||||
// for `String: Copy`, which isn't true here.
|
||||
//
|
||||
// (1) First, we drill into `.item` and highlight that expression
|
||||
// (2) Then we use the template type `Option<(usize, T, bool)>` to
|
||||
// drill into the `T`, arriving at a `Box<String>` expression.
|
||||
// (3) Then we keep going, drilling into this expression using our
|
||||
// outer contextual information.
|
||||
|
||||
// (1) Find the (unique) field index which mentions the type in our constraint:
|
||||
let Some((field_index, field_type)) = Self::is_iterator_singleton(
|
||||
in_ty_adt
|
||||
.variant_with_id(variant_def_id)
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| field.ty(self.tcx, *in_ty_adt_generic_args))
|
||||
.enumerate()
|
||||
.filter(|(_index, field_type)| Self::find_param_in_ty((*field_type).into(), param))
|
||||
) else {
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
if field_index >= expr_args.len() {
|
||||
return Err(expr);
|
||||
}
|
||||
|
||||
// (2) Continue drilling into the struct, ignoring the struct's
|
||||
// generic argument types.
|
||||
let expr = self.blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
param_to_point_at_in_struct,
|
||||
&expr_args[field_index],
|
||||
field_type.into(),
|
||||
)?;
|
||||
|
||||
// (3) Continue drilling into the expression, having "passed
|
||||
// through" the struct entirely.
|
||||
return self.blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
param,
|
||||
expr,
|
||||
generic_argument_type,
|
||||
);
|
||||
}
|
||||
|
||||
// At this point, none of the basic patterns matched.
|
||||
// One major possibility which remains is that we have a function call.
|
||||
// In this case, it's often possible to dive deeper into the call to find something to blame,
|
||||
// but this is not always possible.
|
||||
|
||||
Err(expr)
|
||||
}
|
||||
|
||||
// FIXME: This can be made into a private, non-impl function later.
|
||||
/// Traverses the given ty (either a `ty::Ty` or a `ty::GenericArg`) and searches for references
|
||||
/// to the given `param_to_point_at`. Returns `true` if it finds any use of the param.
|
||||
pub fn find_param_in_ty(
|
||||
ty: ty::GenericArg<'tcx>,
|
||||
param_to_point_at: ty::GenericArg<'tcx>,
|
||||
) -> bool {
|
||||
let mut walk = ty.walk();
|
||||
while let Some(arg) = walk.next() {
|
||||
if arg == param_to_point_at {
|
||||
return true;
|
||||
} else if let ty::GenericArgKind::Type(ty) = arg.unpack()
|
||||
&& let ty::Alias(ty::Projection, ..) = ty.kind()
|
||||
{
|
||||
// This logic may seem a bit strange, but typically when
|
||||
// we have a projection type in a function signature, the
|
||||
// argument that's being passed into that signature is
|
||||
// not actually constraining that projection's substs in
|
||||
// a meaningful way. So we skip it, and see improvements
|
||||
// in some UI tests.
|
||||
walk.skip_current_subtree();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// FIXME: This can be made into a private, non-impl function later.
|
||||
/// Returns `Some(iterator.next())` if it has exactly one item, and `None` otherwise.
|
||||
pub fn is_iterator_singleton<T>(mut iterator: impl Iterator<Item = T>) -> Option<T> {
|
||||
match (iterator.next(), iterator.next()) {
|
||||
(_, Some(_)) => None,
|
||||
(first, _) => first,
|
||||
}
|
||||
}
|
||||
}
|
@ -34,9 +34,10 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}
|
||||
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
use std::ops::ControlFlow;
|
||||
use std::slice;
|
||||
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub(in super::super) fn check_casts(&mut self) {
|
||||
// don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors
|
||||
@ -1843,7 +1844,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
if self.point_at_arg_if_possible(
|
||||
if self.blame_specific_arg_if_possible(
|
||||
error,
|
||||
def_id,
|
||||
param,
|
||||
@ -1873,7 +1874,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
if self.point_at_arg_if_possible(
|
||||
if self.blame_specific_arg_if_possible(
|
||||
error,
|
||||
def_id,
|
||||
param,
|
||||
@ -1898,16 +1899,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
for param in
|
||||
[param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
|
||||
{
|
||||
if let Some(param) = param
|
||||
&& self.point_at_field_if_possible(
|
||||
error,
|
||||
if let Some(param) = param {
|
||||
let refined_expr = self.point_at_field_if_possible(
|
||||
def_id,
|
||||
param,
|
||||
variant_def_id,
|
||||
fields,
|
||||
)
|
||||
{
|
||||
return true;
|
||||
);
|
||||
|
||||
match refined_expr {
|
||||
None => {}
|
||||
Some((refined_expr, _)) => {
|
||||
error.obligation.cause.span = refined_expr
|
||||
.span
|
||||
.find_ancestor_in_same_ctxt(error.obligation.cause.span)
|
||||
.unwrap_or(refined_expr.span);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1940,7 +1949,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn point_at_arg_if_possible(
|
||||
/// - `blame_specific_*` means that the function will recursively traverse the expression,
|
||||
/// looking for the most-specific-possible span to blame.
|
||||
///
|
||||
/// - `point_at_*` means that the function will only go "one level", pointing at the specific
|
||||
/// expression mentioned.
|
||||
///
|
||||
/// `blame_specific_arg_if_possible` will find the most-specific expression anywhere inside
|
||||
/// the provided function call expression, and mark it as responsible for the fullfillment
|
||||
/// error.
|
||||
fn blame_specific_arg_if_possible(
|
||||
&self,
|
||||
error: &mut traits::FulfillmentError<'tcx>,
|
||||
def_id: DefId,
|
||||
@ -1959,13 +1977,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.inputs()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, ty)| find_param_in_ty(**ty, param_to_point_at))
|
||||
.filter(|(_, ty)| Self::find_param_in_ty((**ty).into(), param_to_point_at))
|
||||
.collect();
|
||||
// If there's one field that references the given generic, great!
|
||||
if let [(idx, _)] = args_referencing_param.as_slice()
|
||||
&& let Some(arg) = receiver
|
||||
.map_or(args.get(*idx), |rcvr| if *idx == 0 { Some(rcvr) } else { args.get(*idx - 1) }) {
|
||||
|
||||
error.obligation.cause.span = arg.span.find_ancestor_in_same_ctxt(error.obligation.cause.span).unwrap_or(arg.span);
|
||||
|
||||
if let hir::Node::Expr(arg_expr) = self.tcx.hir().get(arg.hir_id) {
|
||||
// This is more specific than pointing at the entire argument.
|
||||
self.blame_specific_expr_if_possible(error, arg_expr)
|
||||
}
|
||||
|
||||
error.obligation.cause.map_code(|parent_code| {
|
||||
ObligationCauseCode::FunctionArgumentObligation {
|
||||
arg_hir_id: arg.hir_id,
|
||||
@ -1983,14 +2008,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
false
|
||||
}
|
||||
|
||||
fn point_at_field_if_possible(
|
||||
// FIXME: Make this private and move to mod adjust_fulfillment_errors
|
||||
pub fn point_at_field_if_possible(
|
||||
&self,
|
||||
error: &mut traits::FulfillmentError<'tcx>,
|
||||
def_id: DefId,
|
||||
param_to_point_at: ty::GenericArg<'tcx>,
|
||||
variant_def_id: DefId,
|
||||
expr_fields: &[hir::ExprField<'tcx>],
|
||||
) -> bool {
|
||||
) -> Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)> {
|
||||
let def = self.tcx.adt_def(def_id);
|
||||
|
||||
let identity_substs = ty::InternalSubsts::identity_for_item(self.tcx, def_id);
|
||||
@ -2000,7 +2025,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.iter()
|
||||
.filter(|field| {
|
||||
let field_ty = field.ty(self.tcx, identity_substs);
|
||||
find_param_in_ty(field_ty, param_to_point_at)
|
||||
Self::find_param_in_ty(field_ty.into(), param_to_point_at)
|
||||
})
|
||||
.collect();
|
||||
|
||||
@ -2010,17 +2035,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// same rules that check_expr_struct uses for macro hygiene.
|
||||
if self.tcx.adjust_ident(expr_field.ident, variant_def_id) == field.ident(self.tcx)
|
||||
{
|
||||
error.obligation.cause.span = expr_field
|
||||
.expr
|
||||
.span
|
||||
.find_ancestor_in_same_ctxt(error.obligation.cause.span)
|
||||
.unwrap_or(expr_field.span);
|
||||
return true;
|
||||
return Some((expr_field.expr, self.tcx.type_of(field.did)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
None
|
||||
}
|
||||
|
||||
fn point_at_path_if_possible(
|
||||
@ -2240,23 +2260,3 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_param_in_ty<'tcx>(ty: Ty<'tcx>, param_to_point_at: ty::GenericArg<'tcx>) -> bool {
|
||||
let mut walk = ty.walk();
|
||||
while let Some(arg) = walk.next() {
|
||||
if arg == param_to_point_at {
|
||||
return true;
|
||||
} else if let ty::GenericArgKind::Type(ty) = arg.unpack()
|
||||
&& let ty::Alias(ty::Projection, ..) = ty.kind()
|
||||
{
|
||||
// This logic may seem a bit strange, but typically when
|
||||
// we have a projection type in a function signature, the
|
||||
// argument that's being passed into that signature is
|
||||
// not actually constraining that projection's substs in
|
||||
// a meaningful way. So we skip it, and see improvements
|
||||
// in some UI tests.
|
||||
walk.skip_current_subtree();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
mod _impl;
|
||||
mod adjust_fulfillment_errors;
|
||||
mod arg_matrix;
|
||||
mod checks;
|
||||
mod suggestions;
|
||||
|
@ -1563,6 +1563,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||
traits::ImplDerivedObligationCause {
|
||||
derived,
|
||||
impl_def_id,
|
||||
impl_def_predicate_index: None,
|
||||
span,
|
||||
},
|
||||
))
|
||||
|
@ -145,30 +145,32 @@ impl<'tcx> Elaborator<'tcx> {
|
||||
// Get predicates declared on the trait.
|
||||
let predicates = tcx.super_predicates_of(data.def_id());
|
||||
|
||||
let obligations = predicates.predicates.iter().map(|&(mut pred, span)| {
|
||||
// when parent predicate is non-const, elaborate it to non-const predicates.
|
||||
if data.constness == ty::BoundConstness::NotConst {
|
||||
pred = pred.without_const(tcx);
|
||||
}
|
||||
let obligations =
|
||||
predicates.predicates.iter().enumerate().map(|(index, &(mut pred, span))| {
|
||||
// when parent predicate is non-const, elaborate it to non-const predicates.
|
||||
if data.constness == ty::BoundConstness::NotConst {
|
||||
pred = pred.without_const(tcx);
|
||||
}
|
||||
|
||||
let cause = obligation.cause.clone().derived_cause(
|
||||
bound_predicate.rebind(data),
|
||||
|derived| {
|
||||
traits::ImplDerivedObligation(Box::new(
|
||||
traits::ImplDerivedObligationCause {
|
||||
derived,
|
||||
impl_def_id: data.def_id(),
|
||||
span,
|
||||
},
|
||||
))
|
||||
},
|
||||
);
|
||||
predicate_obligation(
|
||||
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
|
||||
obligation.param_env,
|
||||
cause,
|
||||
)
|
||||
});
|
||||
let cause = obligation.cause.clone().derived_cause(
|
||||
bound_predicate.rebind(data),
|
||||
|derived| {
|
||||
traits::ImplDerivedObligation(Box::new(
|
||||
traits::ImplDerivedObligationCause {
|
||||
derived,
|
||||
impl_def_id: data.def_id(),
|
||||
impl_def_predicate_index: Some(index),
|
||||
span,
|
||||
},
|
||||
))
|
||||
},
|
||||
);
|
||||
predicate_obligation(
|
||||
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
|
||||
obligation.param_env,
|
||||
cause,
|
||||
)
|
||||
});
|
||||
debug!(?data, ?obligations, "super_predicates");
|
||||
|
||||
// Only keep those bounds that we haven't already seen.
|
||||
|
@ -475,6 +475,8 @@ pub enum WellFormedLoc {
|
||||
pub struct ImplDerivedObligationCause<'tcx> {
|
||||
pub derived: DerivedObligationCause<'tcx>,
|
||||
pub impl_def_id: DefId,
|
||||
/// The index of the derived predicate in the parent impl's predicates.
|
||||
pub impl_def_predicate_index: Option<usize>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
|
@ -1190,6 +1190,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
|
||||
derived,
|
||||
impl_def_id,
|
||||
impl_def_predicate_index: None,
|
||||
span: obligation.cause.span,
|
||||
}))
|
||||
});
|
||||
|
@ -2608,11 +2608,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
assert_eq!(predicates.parent, None);
|
||||
let predicates = predicates.instantiate_own(tcx, substs);
|
||||
let mut obligations = Vec::with_capacity(predicates.len());
|
||||
for (predicate, span) in predicates {
|
||||
for (index, (predicate, span)) in predicates.into_iter().enumerate() {
|
||||
let cause = cause.clone().derived_cause(parent_trait_pred, |derived| {
|
||||
ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
|
||||
derived,
|
||||
impl_def_id: def_id,
|
||||
impl_def_predicate_index: Some(index),
|
||||
span,
|
||||
}))
|
||||
});
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0277]: the trait bound `B<C>: Copy` is not satisfied
|
||||
--> $DIR/deriving-copyclone.rs:31:13
|
||||
--> $DIR/deriving-copyclone.rs:31:26
|
||||
|
|
||||
LL | is_copy(B { a: 1, b: C });
|
||||
| ------- ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `B<C>`
|
||||
| ------- ^ the trait `Copy` is not implemented for `B<C>`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
@ -19,14 +19,14 @@ LL | fn is_copy<T: Copy>(_: T) {}
|
||||
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider borrowing here
|
||||
|
|
||||
LL | is_copy(&B { a: 1, b: C });
|
||||
| +
|
||||
LL | is_copy(B { a: 1, b: &C });
|
||||
| +
|
||||
|
||||
error[E0277]: the trait bound `B<C>: Clone` is not satisfied
|
||||
--> $DIR/deriving-copyclone.rs:32:14
|
||||
--> $DIR/deriving-copyclone.rs:32:27
|
||||
|
|
||||
LL | is_clone(B { a: 1, b: C });
|
||||
| -------- ^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `B<C>`
|
||||
| -------- ^ the trait `Clone` is not implemented for `B<C>`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
@ -43,14 +43,14 @@ LL | fn is_clone<T: Clone>(_: T) {}
|
||||
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider borrowing here
|
||||
|
|
||||
LL | is_clone(&B { a: 1, b: C });
|
||||
| +
|
||||
LL | is_clone(B { a: 1, b: &C });
|
||||
| +
|
||||
|
||||
error[E0277]: the trait bound `B<D>: Copy` is not satisfied
|
||||
--> $DIR/deriving-copyclone.rs:35:13
|
||||
--> $DIR/deriving-copyclone.rs:35:26
|
||||
|
|
||||
LL | is_copy(B { a: 1, b: D });
|
||||
| ------- ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `B<D>`
|
||||
| ------- ^ the trait `Copy` is not implemented for `B<D>`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
@ -67,8 +67,8 @@ LL | fn is_copy<T: Copy>(_: T) {}
|
||||
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider borrowing here
|
||||
|
|
||||
LL | is_copy(&B { a: 1, b: D });
|
||||
| +
|
||||
LL | is_copy(B { a: 1, b: &D });
|
||||
| +
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
28
tests/ui/errors/trait-bound-error-spans/blame-trait-error.rs
Normal file
28
tests/ui/errors/trait-bound-error-spans/blame-trait-error.rs
Normal file
@ -0,0 +1,28 @@
|
||||
trait T1 {}
|
||||
trait T2 {}
|
||||
trait T3 {}
|
||||
trait T4 {}
|
||||
|
||||
impl<B: T2> T1 for Wrapper<B> {}
|
||||
|
||||
impl T2 for i32 {}
|
||||
impl T3 for i32 {}
|
||||
|
||||
impl<A: T3> T2 for Burrito<A> {}
|
||||
|
||||
struct Wrapper<W> {
|
||||
value: W,
|
||||
}
|
||||
|
||||
struct Burrito<F> {
|
||||
filling: F,
|
||||
}
|
||||
|
||||
fn want<V: T1>(_x: V) {}
|
||||
|
||||
fn example<Q>(q: Q) {
|
||||
want(Wrapper { value: Burrito { filling: q } });
|
||||
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,35 @@
|
||||
error[E0277]: the trait bound `Q: T3` is not satisfied
|
||||
--> $DIR/blame-trait-error.rs:24:46
|
||||
|
|
||||
LL | want(Wrapper { value: Burrito { filling: q } });
|
||||
| ---- ^ the trait `T3` is not implemented for `Q`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required for `Burrito<Q>` to implement `T2`
|
||||
--> $DIR/blame-trait-error.rs:11:13
|
||||
|
|
||||
LL | impl<A: T3> T2 for Burrito<A> {}
|
||||
| -- ^^ ^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required for `Wrapper<Burrito<Q>>` to implement `T1`
|
||||
--> $DIR/blame-trait-error.rs:6:13
|
||||
|
|
||||
LL | impl<B: T2> T1 for Wrapper<B> {}
|
||||
| -- ^^ ^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error.rs:21:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T3>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
131
tests/ui/errors/traits/blame-trait-error-spans-on-exprs.rs
Normal file
131
tests/ui/errors/traits/blame-trait-error-spans-on-exprs.rs
Normal file
@ -0,0 +1,131 @@
|
||||
// This test examines the error spans reported when a generic `impl` fails.
|
||||
// For example, if a function wants an `Option<T>` where `T: Copy` but you pass `Some(vec![1, 2])`,
|
||||
// then we want to point at the `vec![1, 2]` and not the `Some( ... )` expression.
|
||||
|
||||
trait T1 {}
|
||||
trait T2 {}
|
||||
trait T3 {}
|
||||
trait T4 {}
|
||||
|
||||
impl T2 for i32 {}
|
||||
impl T3 for i32 {}
|
||||
|
||||
struct Wrapper<W> {
|
||||
value: W,
|
||||
}
|
||||
impl<B: T2> T1 for Wrapper<B> {}
|
||||
|
||||
struct Burrito<F> {
|
||||
spicy: bool,
|
||||
filling: F,
|
||||
}
|
||||
impl<A: T3> T2 for Burrito<A> {}
|
||||
|
||||
struct BurritoTuple<F>(F);
|
||||
impl<C: T3> T2 for BurritoTuple<C> {}
|
||||
|
||||
enum BurritoKinds<G> {
|
||||
SmallBurrito { spicy: bool, small_filling: G },
|
||||
LargeBurrito { spicy: bool, large_filling: G },
|
||||
MultiBurrito { first_filling: G, second_filling: G },
|
||||
}
|
||||
impl<D: T3> T2 for BurritoKinds<D> {}
|
||||
|
||||
struct Taco<H>(bool, H);
|
||||
impl<E: T3> T2 for Taco<E> {}
|
||||
|
||||
enum TacoKinds<H> {
|
||||
OneTaco(bool, H),
|
||||
TwoTacos(bool, H, H),
|
||||
}
|
||||
impl<F: T3> T2 for TacoKinds<F> {}
|
||||
|
||||
struct GenericBurrito<Spiciness, Filling> {
|
||||
spiciness: Spiciness,
|
||||
filling: Filling,
|
||||
}
|
||||
impl<X, Y: T3> T2 for GenericBurrito<X, Y> {}
|
||||
struct NotSpicy;
|
||||
|
||||
impl<A: T3, B: T3> T2 for (A, B) {}
|
||||
impl<A: T2, B: T2> T1 for (A, B) {}
|
||||
|
||||
fn want<V: T1>(_x: V) {}
|
||||
|
||||
// Some more-complex examples:
|
||||
type AliasBurrito<T> = GenericBurrito<T, T>;
|
||||
|
||||
// The following example is fairly confusing. The idea is that we want to "misdirect" the location
|
||||
// of the error.
|
||||
|
||||
struct Two<A, B> {
|
||||
a: A,
|
||||
b: B,
|
||||
}
|
||||
|
||||
impl<X, Y: T1, Z> T1 for Two<Two<X, Y>, Z> {}
|
||||
|
||||
struct DoubleWrapper<T> {
|
||||
item: Wrapper<T>,
|
||||
}
|
||||
|
||||
impl<T: T1> T1 for DoubleWrapper<T> {}
|
||||
|
||||
fn example<Q>(q: Q) {
|
||||
// In each of the following examples, we expect the error span to point at the 'q' variable,
|
||||
// since the missing constraint is `Q: T3`.
|
||||
|
||||
// Verifies for struct:
|
||||
want(Wrapper { value: Burrito { spicy: false, filling: q } });
|
||||
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
|
||||
|
||||
// Verifies for enum with named fields in variant:
|
||||
want(Wrapper { value: BurritoKinds::SmallBurrito { spicy: true, small_filling: q } });
|
||||
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
|
||||
|
||||
// Verifies for tuple struct:
|
||||
want(Wrapper { value: Taco(false, q) });
|
||||
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
|
||||
|
||||
// Verifies for tuple enum variant:
|
||||
want(Wrapper { value: TacoKinds::OneTaco(false, q) });
|
||||
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
|
||||
|
||||
// Verifies for generic type with multiple parameters:
|
||||
want(Wrapper { value: GenericBurrito { spiciness: NotSpicy, filling: q } });
|
||||
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
|
||||
|
||||
// Verifies for tuple:
|
||||
want((3, q));
|
||||
//~^ ERROR the trait bound `Q: T2` is not satisfied [E0277]
|
||||
|
||||
// Verifies for nested tuple:
|
||||
want(Wrapper { value: (3, q) });
|
||||
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
|
||||
|
||||
// Verifies for nested tuple:
|
||||
want(((3, q), 5));
|
||||
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
|
||||
|
||||
want(DoubleWrapper { item: Wrapper { value: q } });
|
||||
//~^ ERROR the trait bound `Q: T1` is not satisfied [E0277]
|
||||
|
||||
want(DoubleWrapper { item: Wrapper { value: DoubleWrapper { item: Wrapper { value: q } } } });
|
||||
//~^ ERROR the trait bound `Q: T1` is not satisfied [E0277]
|
||||
|
||||
// Verifies for type alias to struct:
|
||||
want(Wrapper { value: AliasBurrito { spiciness: q, filling: q } });
|
||||
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
|
||||
|
||||
want(Two { a: Two { a: (), b: q }, b: () });
|
||||
//~^ ERROR the trait bound `Q: T1` is not satisfied [E0277]
|
||||
|
||||
// We *should* blame the 'q'.
|
||||
// FIXME: Right now, the wrong field is blamed.
|
||||
want(
|
||||
Two { a: Two { a: (), b: Two { a: Two { a: (), b: q }, b: () } }, b: () },
|
||||
//~^ ERROR the trait bound `Q: T1` is not satisfied [E0277]
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {}
|
380
tests/ui/errors/traits/blame-trait-error-spans-on-exprs.stderr
Normal file
380
tests/ui/errors/traits/blame-trait-error-spans-on-exprs.stderr
Normal file
@ -0,0 +1,380 @@
|
||||
error[E0277]: the trait bound `Q: T3` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:79:60
|
||||
|
|
||||
LL | want(Wrapper { value: Burrito { spicy: false, filling: q } });
|
||||
| ---- required by a bound introduced by this call ^ the trait `T3` is not implemented for `Q`
|
||||
|
|
||||
note: required for `Burrito<Q>` to implement `T2`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:22:13
|
||||
|
|
||||
LL | impl<A: T3> T2 for Burrito<A> {}
|
||||
| -- ^^ ^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required for `Wrapper<Burrito<Q>>` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
|
||||
|
|
||||
LL | impl<B: T2> T1 for Wrapper<B> {}
|
||||
| -- ^^ ^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T3>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error[E0277]: the trait bound `Q: T3` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:83:84
|
||||
|
|
||||
LL | want(Wrapper { value: BurritoKinds::SmallBurrito { spicy: true, small_filling: q } });
|
||||
| ---- required by a bound introduced by this call ^ the trait `T3` is not implemented for `Q`
|
||||
|
|
||||
note: required for `BurritoKinds<Q>` to implement `T2`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:32:13
|
||||
|
|
||||
LL | impl<D: T3> T2 for BurritoKinds<D> {}
|
||||
| -- ^^ ^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required for `Wrapper<BurritoKinds<Q>>` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
|
||||
|
|
||||
LL | impl<B: T2> T1 for Wrapper<B> {}
|
||||
| -- ^^ ^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T3>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error[E0277]: the trait bound `Q: T3` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:87:39
|
||||
|
|
||||
LL | want(Wrapper { value: Taco(false, q) });
|
||||
| ---- ^ the trait `T3` is not implemented for `Q`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required for `Taco<Q>` to implement `T2`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:35:13
|
||||
|
|
||||
LL | impl<E: T3> T2 for Taco<E> {}
|
||||
| -- ^^ ^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required for `Wrapper<Taco<Q>>` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
|
||||
|
|
||||
LL | impl<B: T2> T1 for Wrapper<B> {}
|
||||
| -- ^^ ^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T3>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error[E0277]: the trait bound `Q: T3` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:91:27
|
||||
|
|
||||
LL | want(Wrapper { value: TacoKinds::OneTaco(false, q) });
|
||||
| ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `T3` is not implemented for `Q`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required for `TacoKinds<Q>` to implement `T2`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:41:13
|
||||
|
|
||||
LL | impl<F: T3> T2 for TacoKinds<F> {}
|
||||
| -- ^^ ^^^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required for `Wrapper<TacoKinds<Q>>` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
|
||||
|
|
||||
LL | impl<B: T2> T1 for Wrapper<B> {}
|
||||
| -- ^^ ^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T3>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error[E0277]: the trait bound `Q: T3` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:95:74
|
||||
|
|
||||
LL | want(Wrapper { value: GenericBurrito { spiciness: NotSpicy, filling: q } });
|
||||
| ---- required by a bound introduced by this call ^ the trait `T3` is not implemented for `Q`
|
||||
|
|
||||
note: required for `GenericBurrito<NotSpicy, Q>` to implement `T2`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:47:16
|
||||
|
|
||||
LL | impl<X, Y: T3> T2 for GenericBurrito<X, Y> {}
|
||||
| -- ^^ ^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required for `Wrapper<GenericBurrito<NotSpicy, Q>>` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
|
||||
|
|
||||
LL | impl<B: T2> T1 for Wrapper<B> {}
|
||||
| -- ^^ ^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T3>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error[E0277]: the trait bound `Q: T2` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:99:14
|
||||
|
|
||||
LL | want((3, q));
|
||||
| ---- ^ the trait `T2` is not implemented for `Q`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required for `(i32, Q)` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:51:20
|
||||
|
|
||||
LL | impl<A: T2, B: T2> T1 for (A, B) {}
|
||||
| -- ^^ ^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T2>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error[E0277]: the trait bound `Q: T3` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:103:31
|
||||
|
|
||||
LL | want(Wrapper { value: (3, q) });
|
||||
| ---- ^ the trait `T3` is not implemented for `Q`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required for `(i32, Q)` to implement `T2`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:50:20
|
||||
|
|
||||
LL | impl<A: T3, B: T3> T2 for (A, B) {}
|
||||
| -- ^^ ^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required for `Wrapper<(i32, Q)>` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
|
||||
|
|
||||
LL | impl<B: T2> T1 for Wrapper<B> {}
|
||||
| -- ^^ ^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T3>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error[E0277]: the trait bound `Q: T3` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:107:15
|
||||
|
|
||||
LL | want(((3, q), 5));
|
||||
| ---- ^ the trait `T3` is not implemented for `Q`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required for `(i32, Q)` to implement `T2`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:50:20
|
||||
|
|
||||
LL | impl<A: T3, B: T3> T2 for (A, B) {}
|
||||
| -- ^^ ^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required for `((i32, Q), i32)` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:51:20
|
||||
|
|
||||
LL | impl<A: T2, B: T2> T1 for (A, B) {}
|
||||
| -- ^^ ^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T3>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error[E0277]: the trait bound `Q: T1` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:110:49
|
||||
|
|
||||
LL | want(DoubleWrapper { item: Wrapper { value: q } });
|
||||
| ---- ^ the trait `T1` is not implemented for `Q`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required for `DoubleWrapper<Q>` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:72:13
|
||||
|
|
||||
LL | impl<T: T1> T1 for DoubleWrapper<T> {}
|
||||
| -- ^^ ^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T1>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error[E0277]: the trait bound `Q: T1` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:113:88
|
||||
|
|
||||
LL | want(DoubleWrapper { item: Wrapper { value: DoubleWrapper { item: Wrapper { value: q } } } });
|
||||
| ---- required by a bound introduced by this call ^ the trait `T1` is not implemented for `Q`
|
||||
|
|
||||
note: required for `DoubleWrapper<Q>` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:72:13
|
||||
|
|
||||
LL | impl<T: T1> T1 for DoubleWrapper<T> {}
|
||||
| -- ^^ ^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
= note: 1 redundant requirement hidden
|
||||
= note: required for `DoubleWrapper<DoubleWrapper<Q>>` to implement `T1`
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T1>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error[E0277]: the trait bound `Q: T3` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:117:27
|
||||
|
|
||||
LL | want(Wrapper { value: AliasBurrito { spiciness: q, filling: q } });
|
||||
| ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `T3` is not implemented for `Q`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required for `GenericBurrito<Q, Q>` to implement `T2`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:47:16
|
||||
|
|
||||
LL | impl<X, Y: T3> T2 for GenericBurrito<X, Y> {}
|
||||
| -- ^^ ^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required for `Wrapper<GenericBurrito<Q, Q>>` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
|
||||
|
|
||||
LL | impl<B: T2> T1 for Wrapper<B> {}
|
||||
| -- ^^ ^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T3>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error[E0277]: the trait bound `Q: T1` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:120:35
|
||||
|
|
||||
LL | want(Two { a: Two { a: (), b: q }, b: () });
|
||||
| ---- ^ the trait `T1` is not implemented for `Q`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required for `Two<Two<(), Q>, ()>` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:66:19
|
||||
|
|
||||
LL | impl<X, Y: T1, Z> T1 for Two<Two<X, Y>, Z> {}
|
||||
| -- ^^ ^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T1>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error[E0277]: the trait bound `Q: T1` is not satisfied
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:126:59
|
||||
|
|
||||
LL | want(
|
||||
| ---- required by a bound introduced by this call
|
||||
LL | Two { a: Two { a: (), b: Two { a: Two { a: (), b: q }, b: () } }, b: () },
|
||||
| ^ the trait `T1` is not implemented for `Q`
|
||||
|
|
||||
note: required for `Two<Two<(), Q>, ()>` to implement `T1`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:66:19
|
||||
|
|
||||
LL | impl<X, Y: T1, Z> T1 for Two<Two<X, Y>, Z> {}
|
||||
| -- ^^ ^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
= note: 1 redundant requirement hidden
|
||||
= note: required for `Two<Two<(), Two<Two<(), Q>, ()>>, ()>` to implement `T1`
|
||||
note: required by a bound in `want`
|
||||
--> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
|
||||
|
|
||||
LL | fn want<V: T1>(_x: V) {}
|
||||
| ^^ required by this bound in `want`
|
||||
help: consider restricting type parameter `Q`
|
||||
|
|
||||
LL | fn example<Q: T1>(q: Q) {
|
||||
| ++++
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
@ -101,10 +101,10 @@ LL | fn is_send<T: Send>(_: T) {}
|
||||
| ^^^^ required by this bound in `is_send`
|
||||
|
||||
error[E0277]: `main::TestType` cannot be sent between threads safely
|
||||
--> $DIR/negated-auto-traits-error.rs:66:13
|
||||
--> $DIR/negated-auto-traits-error.rs:66:20
|
||||
|
|
||||
LL | is_sync(Outer2(TestType));
|
||||
| ------- ^^^^^^^^^^^^^^^^ `main::TestType` cannot be sent between threads safely
|
||||
| ------- ^^^^^^^^ `main::TestType` cannot be sent between threads safely
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
|
Loading…
Reference in New Issue
Block a user