Point at specific field in struct literal when trait fulfillment fails
This commit is contained in:
parent
c8e6a9e8b6
commit
2a67e99d7d
21 changed files with 1150 additions and 111 deletions
|
@ -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
|
||||
|
@ -1837,7 +1838,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,
|
||||
|
@ -1867,7 +1868,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,
|
||||
|
@ -1892,16 +1893,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1934,7 +1943,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,
|
||||
|
@ -1953,13 +1971,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,
|
||||
|
@ -1977,14 +2002,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);
|
||||
|
@ -1994,7 +2019,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();
|
||||
|
||||
|
@ -2004,17 +2029,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(
|
||||
|
@ -2234,23 +2254,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;
|
||||
|
|
|
@ -1568,6 +1568,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.
|
||||
|
|
|
@ -467,6 +467,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,
|
||||
}
|
||||
|
||||
|
|
|
@ -1224,6 +1224,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
|
||||
derived,
|
||||
impl_def_id,
|
||||
impl_def_predicate_index: None,
|
||||
span: obligation.cause.span,
|
||||
}))
|
||||
});
|
||||
|
|
|
@ -2562,11 +2562,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 +1 @@
|
|||
Subproject commit 2cd1b5593d26dc6a03c20f8619187ad4b2485552
|
||||
Subproject commit 2bd5d42c9956369132228da6409f0e68da56c51a
|
|
@ -1 +1 @@
|
|||
Subproject commit 960d610e7f33889a2577f5f17c26f0d5c82b30df
|
||||
Subproject commit 8ca261268068d80c0969260fff15199bad87b587
|
|
@ -1 +1 @@
|
|||
Subproject commit 2cb0ed9ba56360949f492f9866afe8c293f9f9da
|
||||
Subproject commit 3ae62681ff236d5528ef7c8c28ba7c6b2ecc6731
|
|
@ -1 +1 @@
|
|||
Subproject commit a9fb7d13eadfcc5f457962731f105b97f9a7474a
|
||||
Subproject commit 8888f9428fe9a48f31de6bd2cef9b9bf80791edc
|
|
@ -1 +1 @@
|
|||
Subproject commit 985d561f0bb9b76ec043a2b12511790ec7a2b954
|
||||
Subproject commit 8c460b2237a6359a7e3335890db8da049bdd62fc
|
|
@ -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`.
|
|
@ -35,16 +35,14 @@ trait Ty<'a> {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let v = Unit2.m(
|
||||
L {
|
||||
//~^ ERROR to be a closure that returns `Unit3`, but it returns `Unit4`
|
||||
//~| ERROR type mismatch
|
||||
f: |x| {
|
||||
drop(x);
|
||||
Unit4
|
||||
},
|
||||
let v = Unit2.m(L {
|
||||
//~^ ERROR type mismatch
|
||||
//~| ERROR to be a closure that returns `Unit3`, but it returns `Unit4`
|
||||
f: |x| {
|
||||
drop(x);
|
||||
Unit4
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
impl<'a> Ty<'a> for Unit2 {
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
error[E0271]: type mismatch resolving `for<'r> <L<[closure@issue-62203-hrtb-ice.rs:42:16]> as T0<'r, (&'r u8,)>>::O == <_ as Ty<'r>>::V`
|
||||
--> $DIR/issue-62203-hrtb-ice.rs:39:9
|
||||
error[E0271]: type mismatch resolving `for<'r> <L<[closure@issue-62203-hrtb-ice.rs:41:12]> as T0<'r, (&'r u8,)>>::O == <_ as Ty<'r>>::V`
|
||||
--> $DIR/issue-62203-hrtb-ice.rs:38:21
|
||||
|
|
||||
LL | let v = Unit2.m(
|
||||
| - required by a bound introduced by this call
|
||||
LL | / L {
|
||||
LL | let v = Unit2.m(L {
|
||||
| ___________________-_^
|
||||
| | |
|
||||
| | required by a bound introduced by this call
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | f: |x| {
|
||||
LL | | f: |x| {
|
||||
... |
|
||||
LL | | },
|
||||
LL | | },
|
||||
| |_________^ type mismatch resolving `for<'r> <L<[closure@issue-62203-hrtb-ice.rs:42:16]> as T0<'r, (&'r u8,)>>::O == <_ as Ty<'r>>::V`
|
||||
LL | | });
|
||||
| |_____^ type mismatch resolving `for<'r> <L<[closure@issue-62203-hrtb-ice.rs:41:12]> as T0<'r, (&'r u8,)>>::O == <_ as Ty<'r>>::V`
|
||||
|
|
||||
note: expected this to be `<_ as Ty<'_>>::V`
|
||||
--> $DIR/issue-62203-hrtb-ice.rs:21:14
|
||||
|
@ -30,21 +31,22 @@ LL | where
|
|||
LL | F: for<'r> T0<'r, (<Self as Ty<'r>>::V,), O = <B as Ty<'r>>::V>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^ required by this bound in `T1::m`
|
||||
|
||||
error[E0271]: expected `[closure@issue-62203-hrtb-ice.rs:42:16]` to be a closure that returns `Unit3`, but it returns `Unit4`
|
||||
--> $DIR/issue-62203-hrtb-ice.rs:39:9
|
||||
error[E0271]: expected `[closure@issue-62203-hrtb-ice.rs:41:12]` to be a closure that returns `Unit3`, but it returns `Unit4`
|
||||
--> $DIR/issue-62203-hrtb-ice.rs:38:21
|
||||
|
|
||||
LL | let v = Unit2.m(
|
||||
| - required by a bound introduced by this call
|
||||
LL | / L {
|
||||
LL | let v = Unit2.m(L {
|
||||
| ___________________-_^
|
||||
| | |
|
||||
| | required by a bound introduced by this call
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | f: |x| {
|
||||
LL | | f: |x| {
|
||||
... |
|
||||
LL | | },
|
||||
LL | | },
|
||||
| |_________^ expected struct `Unit3`, found struct `Unit4`
|
||||
LL | | });
|
||||
| |_____^ expected struct `Unit3`, found struct `Unit4`
|
||||
|
|
||||
note: required for `L<[closure@$DIR/issue-62203-hrtb-ice.rs:42:16: 42:19]>` to implement `for<'r> T0<'r, (&'r u8,)>`
|
||||
note: required for `L<[closure@$DIR/issue-62203-hrtb-ice.rs:41:12: 41:15]>` to implement `for<'r> T0<'r, (&'r u8,)>`
|
||||
--> $DIR/issue-62203-hrtb-ice.rs:17:16
|
||||
|
|
||||
LL | impl<'a, A, T> T0<'a, A> for L<T>
|
||||
|
|
|
@ -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…
Add table
Reference in a new issue