about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorJanusz Marcinkiewicz <virrages@gmail.com>2019-11-24 14:31:19 +0100
committerJanusz Marcinkiewicz <virrages@gmail.com>2019-11-24 15:26:09 +0100
commit6f70803e7e3fa41a8ff80a5e9d066e0f2ddadbcd (patch)
treee5a14f0fba5cde77c0d25dfd894fd47286624d41 /src
parent7d761fe0462ba0f671a237d0bb35e3579b8ba0e8 (diff)
downloadrust-6f70803e7e3fa41a8ff80a5e9d066e0f2ddadbcd.tar.gz
rust-6f70803e7e3fa41a8ff80a5e9d066e0f2ddadbcd.zip
Fix pointing at arg for fulfillment errors in function calls
Diffstat (limited to 'src')
-rw-r--r--src/librustc/traits/error_reporting.rs2
-rw-r--r--src/librustc_typeck/check/mod.rs38
-rw-r--r--src/test/ui/unsized/unsized-fn-param.rs20
-rw-r--r--src/test/ui/unsized/unsized-fn-param.stderr43
4 files changed, 88 insertions, 15 deletions
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 0144d51a969..96d42dd73b1 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -165,7 +165,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         body_id: Option<hir::BodyId>,
         fallback_has_occurred: bool,
     ) {
-        debug!("report_fulfillment_errors({:?})", error);
+        debug!("report_fulfillment_error({:?})", error);
         match error.code {
             FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
                 self.report_selection_error(
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 512a49d13e7..9107e993311 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3641,7 +3641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             formal_tys.clone()
         };
 
-        let mut final_arg_types: Vec<(usize, Ty<'_>)> = vec![];
+        let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![];
 
         // Check the arguments.
         // We do this in a pretty awful way: first we type-check any arguments
@@ -3709,7 +3709,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // We're processing function arguments so we definitely want to use
                 // two-phase borrows.
                 self.demand_coerce(&arg, checked_ty, coerce_ty, AllowTwoPhase::Yes);
-                final_arg_types.push((i, coerce_ty));
+                final_arg_types.push((i, checked_ty, coerce_ty));
 
                 // 3. Relate the expected type and the formal one,
                 //    if the expected type was used for the coercion.
@@ -3756,14 +3756,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         vec![self.tcx.types.err; len]
     }
 
-    /// Given a vec of evaluated `FullfillmentError`s and an `fn` call argument expressions, we
-    /// walk the resolved types for each argument to see if any of the `FullfillmentError`s
-    /// reference a type argument. If they do, and there's only *one* argument that does, we point
-    /// at the corresponding argument's expression span instead of the `fn` call path span.
+    /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk
+    /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s
+    /// reference a type argument. The reason to walk also the checked type is that the coerced type
+    /// can be not easily comparable with predicate type (because of coercion). If the types match
+    /// for either checked or coerced type, and there's only *one* argument that does, we point at
+    /// the corresponding argument's expression span instead of the `fn` call path span.
     fn point_at_arg_instead_of_call_if_possible(
         &self,
         errors: &mut Vec<traits::FulfillmentError<'_>>,
-        final_arg_types: &[(usize, Ty<'tcx>)],
+        final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)],
         call_sp: Span,
         args: &'tcx [hir::Expr],
     ) {
@@ -3773,19 +3775,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             for error in errors {
                 if let ty::Predicate::Trait(predicate) = error.obligation.predicate {
                     // Collect the argument position for all arguments that could have caused this
-                    // `FullfillmentError`.
+                    // `FulfillmentError`.
                     let mut referenced_in = final_arg_types.iter()
+                        .map(|(i, checked_ty, _)| (i, checked_ty))
+                        .chain(final_arg_types.iter().map(|(i, _, coerced_ty)| (i, coerced_ty)))
                         .flat_map(|(i, ty)| {
                             let ty = self.resolve_vars_if_possible(ty);
                             // We walk the argument type because the argument's type could have
-                            // been `Option<T>`, but the `FullfillmentError` references `T`.
+                            // been `Option<T>`, but the `FulfillmentError` references `T`.
                             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()) {
+                        })
+                        .collect::<Vec<_>>();
+
+                    // Both checked and coerced types could have matched, thus we need to remove
+                    // duplicates.
+                    referenced_in.dedup();
+
+                    if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) {
                         // We make sure that only *one* argument matches the obligation failure
-                        // and thet the obligation's span to its expression's.
+                        // and we assign the obligation's span to its expression's.
                         error.obligation.cause.span = args[ref_in].span;
                         error.points_at_arg_span = true;
                     }
@@ -3794,8 +3804,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    /// Given a vec of evaluated `FullfillmentError`s and an `fn` call expression, we walk the
-    /// `PathSegment`s and resolve their type parameters to see if any of the `FullfillmentError`s
+    /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the
+    /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s
     /// were caused by them. If they were, we point at the corresponding type argument's span
     /// instead of the `fn` call path span.
     fn point_at_type_arg_instead_of_call_if_possible(
diff --git a/src/test/ui/unsized/unsized-fn-param.rs b/src/test/ui/unsized/unsized-fn-param.rs
new file mode 100644
index 00000000000..32efc7e17ad
--- /dev/null
+++ b/src/test/ui/unsized/unsized-fn-param.rs
@@ -0,0 +1,20 @@
+use std::convert::AsRef;
+use std::path::Path;
+
+fn foo11(_bar: &dyn AsRef<Path>, _baz: &str) {}
+fn foo12(_bar: &str, _baz: &dyn AsRef<Path>) {}
+
+fn foo21(_bar: &dyn AsRef<str>, _baz: &str) {}
+fn foo22(_bar: &str, _baz: &dyn AsRef<str>) {}
+
+fn main() {
+    foo11("bar", &"baz"); //~ ERROR the size for values of type
+    foo11(&"bar", &"baz");
+    foo12(&"bar", "baz"); //~ ERROR the size for values of type
+    foo12(&"bar", &"baz");
+
+    foo21("bar", &"baz"); //~ ERROR the size for values of type
+    foo21(&"bar", &"baz");
+    foo22(&"bar", "baz"); //~ ERROR the size for values of type
+    foo22(&"bar", &"baz");
+}
diff --git a/src/test/ui/unsized/unsized-fn-param.stderr b/src/test/ui/unsized/unsized-fn-param.stderr
new file mode 100644
index 00000000000..ed2c2e75cbd
--- /dev/null
+++ b/src/test/ui/unsized/unsized-fn-param.stderr
@@ -0,0 +1,43 @@
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/unsized-fn-param.rs:11:11
+   |
+LL |     foo11("bar", &"baz");
+   |           ^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `std::marker::Sized` is not implemented for `str`
+   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+   = note: required for the cast to the object type `dyn std::convert::AsRef<std::path::Path>`
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/unsized-fn-param.rs:13:19
+   |
+LL |     foo12(&"bar", "baz");
+   |                   ^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `std::marker::Sized` is not implemented for `str`
+   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+   = note: required for the cast to the object type `dyn std::convert::AsRef<std::path::Path>`
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/unsized-fn-param.rs:16:11
+   |
+LL |     foo21("bar", &"baz");
+   |           ^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `std::marker::Sized` is not implemented for `str`
+   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+   = note: required for the cast to the object type `dyn std::convert::AsRef<str>`
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/unsized-fn-param.rs:18:19
+   |
+LL |     foo22(&"bar", "baz");
+   |                   ^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `std::marker::Sized` is not implemented for `str`
+   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+   = note: required for the cast to the object type `dyn std::convert::AsRef<str>`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.