diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index e52451b2fdc4..1d87484ef09b 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1014,6 +1014,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => "_".to_string(), }).collect::>().join(", "), ); + // When the obligation error has been ensured to have been caused by + // an argument, the `obligation.cause.span` points at the expression + // of the argument, so we can provide a suggestion. This is signaled + // by `points_at_arg`. Otherwise, we give a more general note. if points_at_arg { err.span_suggestion( obligation.cause.span, diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index b7db7c252066..c53f4e49971b 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -485,6 +485,9 @@ EnumTypeFoldableImpl! { pub struct FulfillmentError<'tcx> { pub obligation: PredicateObligation<'tcx>, pub code: FulfillmentErrorCode<'tcx>, + /// Diagnostics only: we opportunistically change the `code.span` when we encounter an + /// obligation error caused by a call argument. When this is the case, we also signal that in + /// this field to ensure accuracy of suggestions. pub points_at_arg_span: bool, } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index df9955d6ba01..430e57810d96 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -885,12 +885,12 @@ fn typeck_tables_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::TypeckTables<'_> { }; // All type checking constraints were added, try to fallback unsolved variables. - fcx.select_obligations_where_possible(false); + fcx.select_obligations_where_possible(false, |_| {}); let mut fallback_has_occurred = false; for ty in &fcx.unsolved_variables() { fallback_has_occurred |= fcx.fallback_if_possible(ty); } - fcx.select_obligations_where_possible(fallback_has_occurred); + fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); // Even though coercion casts provide type hints, we check casts after fallback for // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. @@ -2356,7 +2356,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // possible. This can help substantially when there are // indirect dependencies that don't seem worth tracking // precisely. - self.select_obligations_where_possible(false); + self.select_obligations_where_possible(false, |_| {}); ty = self.resolve_vars_if_possible(&ty); debug!("resolve_type_vars_with_obligations: ty={:?}", ty); @@ -2807,7 +2807,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn resolve_generator_interiors(&self, def_id: DefId) { let mut generators = self.deferred_generator_interiors.borrow_mut(); for (body_id, interior, kind) in generators.drain(..) { - self.select_obligations_where_possible(false); + self.select_obligations_where_possible(false, |_| {}); generator_interior::resolve_interior(self, def_id, body_id, interior, kind); } } @@ -2844,8 +2844,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Select as many obligations as we can at present. - fn select_obligations_where_possible(&self, fallback_has_occurred: bool) { - if let Err(errors) = self.fulfillment_cx.borrow_mut().select_where_possible(self) { + fn select_obligations_where_possible( + &self, + fallback_has_occurred: bool, + f: impl Fn(&mut Vec>), + ) { + if let Err(mut errors) = self.fulfillment_cx.borrow_mut().select_where_possible(self) { + f(&mut errors); self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred); } } @@ -3267,20 +3272,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // an "opportunistic" vtable resolution of any trait bounds on // the call. This helps coercions. if check_closures { - // We don't use `select_obligations_where_possible` to try to figure out if the - // obligation is comming from a single fn call argument, and if it is, we point - // at the expression corresponding to that argument, instead of the call. - if let Err( - mut errors, - ) = self.fulfillment_cx.borrow_mut().select_where_possible(self) { + self.select_obligations_where_possible(false, |errors| { self.point_at_arg_instead_of_call_if_possible( - &mut errors, + errors, &final_arg_types[..], sp, &args, ); - self.report_fulfillment_errors(&errors, self.inh.body_id, false); - } + }) } // For C-variadic functions, we don't have a declared type for all of @@ -3394,8 +3393,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - if referenced_in.len() == 1 { - error.obligation.cause.span = args[referenced_in[0]].span; + let mut referenced_in = final_arg_types.iter() + .flat_map(|(i, ty)| { + let ty = self.resolve_vars_if_possible(ty); + ty.walk() + .filter(|&ty| ty == predicate.skip_binder().self_ty()) + .map(move |_| *i) + }); + if let (Some(ref_in), None) = (referenced_in.next(), referenced_in.next()) { + // We make sure that only *one* argument matches the obligation failure + // and thet the obligation's span to its expression's. + error.obligation.cause.span = args[ref_in].span; error.points_at_arg_span = true; } } diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 18b555dc037c..956d04ff6229 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -724,7 +724,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match method { Some(ok) => { let method = self.register_infer_ok_obligations(ok); - self.select_obligations_where_possible(false); + self.select_obligations_where_possible(false, |_| {}); Ok(method) }