about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2022-08-16 18:16:11 +0530
committerGitHub <noreply@github.com>2022-08-16 18:16:11 +0530
commit070de4bc4866fe8f36bf1538e01e10eab25f3c79 (patch)
treee18648a3d3821cb169d2cc855a56e0b84b0e71cc /compiler
parent14a459bf37bc19476d43e0045d078121c12d3fef (diff)
parent1c084f15f308f1d977fd763c3d8bed831b44abf2 (diff)
downloadrust-070de4bc4866fe8f36bf1538e01e10eab25f3c79.tar.gz
rust-070de4bc4866fe8f36bf1538e01e10eab25f3c79.zip
Rollup merge of #99942 - compiler-errors:nonsense-un-tupled-fn-trait-error, r=cjgillot
Fix nonsense non-tupled `Fn` trait error

Given this code:

```rust
#![feature(unboxed_closures)]

fn a<F: Fn<usize>>(f: F) {}

fn main() {
    a(|_: usize| {});
}
```

We currently emit this error:
```
error[E0631]: type mismatch in closure arguments
 --> src/main.rs:6:5
  |
6 |     a(|_: usize| {});
  |     ^ ---------- found signature of `fn(usize) -> _`
  |     |
  |     expected signature of `fn(usize) -> _`
  |
note: required by a bound in `a`
 --> src/main.rs:3:9
  |
3 | fn a<F: Fn<usize>>(f: F) {}
  |         ^^^^^^^^^ required by this bound in `a`

For more information about this error, try `rustc --explain E0631`.
error: could not compile `playground` due to previous error
```
Notably, it says the same thing for "expected" and "found"!

Fix the output so that we instead emit:
```
error[E0308]: mismatched types
 --> /home/gh-compiler-errors/test.rs:6:5
  |
6 |     a(|_: usize| {});
  |     ^ types differ
  |
  = note: expected trait `Fn<usize>`
             found trait `Fn<(usize,)>`
note: required by a bound in `a`
 --> /home/gh-compiler-errors/test.rs:3:9
  |
3 | fn a<F: Fn<usize>>(f: F) {}
  |         ^^^^^^^^^ required by this bound in `a`

error: aborting due to previous error
```

The error could still use some work, namely the "mismatched types" part, but I'm leaving it a bit rough since the only way you'd ever get this error is when you're messing with `#![feature(unboxed_closures)]`.

Simply making sure we actually print out the difference in trait-refs is good enough for me. I could probably factor in some additional improvements if those are desired.
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs1
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs12
-rw-r--r--compiler/rustc_middle/src/ty/error.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs30
4 files changed, 52 insertions, 3 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index fcb87a9f32f..3c7dac2bfd8 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1750,6 +1750,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values
             && let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind()
             && let Some(def_id) = def_id.as_local()
+            && terr.involves_regions()
         {
             let span = self.tcx.def_span(def_id);
             diag.span_note(span, "this closure does not fulfill the lifetime requirements");
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 1e26e7bb86e..444817f396e 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1935,6 +1935,18 @@ impl<'tcx> TypeTrace<'tcx> {
         }
     }
 
+    pub fn poly_trait_refs(
+        cause: &ObligationCause<'tcx>,
+        a_is_expected: bool,
+        a: ty::PolyTraitRef<'tcx>,
+        b: ty::PolyTraitRef<'tcx>,
+    ) -> TypeTrace<'tcx> {
+        TypeTrace {
+            cause: cause.clone(),
+            values: PolyTraitRefs(ExpectedFound::new(a_is_expected, a.into(), b.into())),
+        }
+    }
+
     pub fn consts(
         cause: &ObligationCause<'tcx>,
         a_is_expected: bool,
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 8e6af936ded..ac89bec702e 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -74,6 +74,18 @@ pub enum TypeError<'tcx> {
     TargetFeatureCast(DefId),
 }
 
+impl TypeError<'_> {
+    pub fn involves_regions(self) -> bool {
+        match self {
+            TypeError::RegionsDoesNotOutlive(_, _)
+            | TypeError::RegionsInsufficientlyPolymorphic(_, _)
+            | TypeError::RegionsOverlyPolymorphic(_, _)
+            | TypeError::RegionsPlaceholderMismatch => true,
+            _ => false,
+        }
+    }
+}
+
 /// Explains the source of a type err in a short, human readable way. This is meant to be placed
 /// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()`
 /// afterwards to present additional details, particularly when it comes to lifetime-related
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 4260cb53adc..1a39a168038 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -22,6 +22,7 @@ use rustc_hir::intravisit::Visitor;
 use rustc_hir::GenericParam;
 use rustc_hir::Item;
 use rustc_hir::Node;
+use rustc_infer::infer::TypeTrace;
 use rustc_infer::traits::TraitEngine;
 use rustc_middle::traits::select::OverflowError;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
@@ -941,9 +942,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
 
                 self.reported_closure_mismatch.borrow_mut().insert((span, found_span));
 
+                let mut not_tupled = false;
+
                 let found = match found_trait_ref.skip_binder().substs.type_at(1).kind() {
                     ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()],
-                    _ => vec![ArgKind::empty()],
+                    _ => {
+                        not_tupled = true;
+                        vec![ArgKind::empty()]
+                    }
                 };
 
                 let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1);
@@ -951,10 +957,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     ty::Tuple(ref tys) => {
                         tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect()
                     }
-                    _ => vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())],
+                    _ => {
+                        not_tupled = true;
+                        vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())]
+                    }
                 };
 
-                if found.len() == expected.len() {
+                // If this is a `Fn` family trait and either the expected or found
+                // is not tupled, then fall back to just a regular mismatch error.
+                // This shouldn't be common unless manually implementing one of the
+                // traits manually, but don't make it more confusing when it does
+                // happen.
+                if Some(expected_trait_ref.def_id()) != tcx.lang_items().gen_trait() && not_tupled {
+                    self.report_and_explain_type_error(
+                        TypeTrace::poly_trait_refs(
+                            &obligation.cause,
+                            true,
+                            expected_trait_ref,
+                            found_trait_ref,
+                        ),
+                        ty::error::TypeError::Mismatch,
+                    )
+                } else if found.len() == expected.len() {
                     self.report_closure_arg_mismatch(
                         span,
                         found_span,