about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-07-10 21:59:31 +0000
committerMichael Goulet <michael@errs.io>2022-07-14 23:29:03 +0000
commitddb7003b7950a6df815d1f03ea6eca9f4cc2a408 (patch)
tree07e74addfb8f65b008d8223c7c45f66422b5fd3b
parentc2f428d2f3340a0e7d995f4726223db91b93704c (diff)
downloadrust-ddb7003b7950a6df815d1f03ea6eca9f4cc2a408.tar.gz
rust-ddb7003b7950a6df815d1f03ea6eca9f4cc2a408.zip
Add support for APIT and RPIT callables in label_fn_like
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/checks.rs138
-rw-r--r--src/test/ui/argument-suggestions/exotic-calls.rs26
-rw-r--r--src/test/ui/argument-suggestions/exotic-calls.stderr67
-rw-r--r--src/test/ui/issues/issue-16939.stderr8
-rw-r--r--src/test/ui/mismatched_types/overloaded-calls-bad.stderr16
5 files changed, 195 insertions, 60 deletions
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index b1e391bf1c7..3dd63d74c3f 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -25,7 +25,7 @@ use rustc_infer::infer::InferOk;
 use rustc_infer::infer::TypeTrace;
 use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
+use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty};
 use rustc_session::Session;
 use rustc_span::symbol::Ident;
 use rustc_span::{self, Span};
@@ -89,7 +89,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 args_no_rcvr,
                 false,
                 tuple_arguments,
-                None,
+                method.ok().map(|method| method.def_id),
             );
             return self.tcx.ty_error();
         }
@@ -458,6 +458,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 c_variadic,
                 err_code,
                 fn_def_id,
+                call_expr,
             );
         }
     }
@@ -474,6 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         c_variadic: bool,
         err_code: &str,
         fn_def_id: Option<DefId>,
+        call_expr: &hir::Expr<'tcx>,
     ) {
         // Don't print if it has error types or is just plain `_`
         fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
@@ -495,6 +497,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 (self.resolve_vars_if_possible(ty), expr.span)
             })
             .collect();
+        let callee_expr = match &call_expr.peel_blocks().kind {
+            hir::ExprKind::Call(callee, _) => Some(*callee),
+            hir::ExprKind::MethodCall(_, callee, _) => {
+                if let Some((DefKind::AssocFn, def_id)) =
+                    self.typeck_results.borrow().type_dependent_def(call_expr.hir_id)
+                    && let Some(assoc) = tcx.opt_associated_item(def_id)
+                    && assoc.fn_has_self_parameter
+                {
+                    Some(&callee[0])
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        };
+        let callee_ty = callee_expr
+            .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr));
 
         // A "softer" version of the `demand_compatible`, which checks types without persisting them,
         // and treats error types differently
@@ -631,7 +650,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             Applicability::MachineApplicable,
                         );
                     };
-                    label_fn_like(tcx, &mut err, fn_def_id);
+                    self.label_fn_like(&mut err, fn_def_id, callee_ty);
                     err.emit();
                     return;
                 }
@@ -721,7 +740,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 format!("arguments to this {} are incorrect", call_name),
             );
             // Call out where the function is defined
-            label_fn_like(tcx, &mut err, fn_def_id);
+            self.label_fn_like(&mut err, fn_def_id, callee_ty);
             err.emit();
             return;
         }
@@ -1003,7 +1022,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         // Call out where the function is defined
-        label_fn_like(tcx, &mut err, fn_def_id);
+        self.label_fn_like(&mut err, fn_def_id, callee_ty);
 
         // And add a suggestion block for all of the parameters
         let suggestion_text = match suggestion_text {
@@ -1795,47 +1814,86 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
     }
-}
 
-fn label_fn_like<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>,
-    def_id: Option<DefId>,
-) {
-    let Some(def_id) = def_id else {
-        return;
-    };
-
-    if let Some(def_span) = tcx.def_ident_span(def_id) {
-        let mut spans: MultiSpan = def_span.into();
-
-        let params = tcx
-            .hir()
-            .get_if_local(def_id)
-            .and_then(|node| node.body_id())
-            .into_iter()
-            .flat_map(|id| tcx.hir().body(id).params);
-
-        for param in params {
-            spans.push_span_label(param.span, "");
+    fn label_fn_like(
+        &self,
+        err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>,
+        def_id: Option<DefId>,
+        callee_ty: Option<Ty<'tcx>>,
+    ) {
+        let Some(mut def_id) = def_id else {
+            return;
+        };
+
+        if let Some(assoc_item) = self.tcx.opt_associated_item(def_id)
+            && let trait_def_id = assoc_item.trait_item_def_id.unwrap_or_else(|| self.tcx.parent(def_id))
+            // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce"
+            && ty::ClosureKind::from_def_id(self.tcx, trait_def_id).is_some()
+            && let Some(callee_ty) = callee_ty
+        {
+            let callee_ty = callee_ty.peel_refs();
+            match *callee_ty.kind() {
+                ty::Param(param) => {
+                    let param =
+                        self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx);
+                    if param.kind.is_synthetic() {
+                        // if it's `impl Fn() -> ..` then just fall down to the def-id based logic
+                        def_id = param.def_id;
+                    } else {
+                        // Otherwise, find the predicate that makes this generic callable,
+                        // and point at that.
+                        let instantiated = self
+                            .tcx
+                            .explicit_predicates_of(self.body_id.owner)
+                            .instantiate_identity(self.tcx);
+                        // FIXME(compiler-errors): This could be problematic if something has two
+                        // fn-like predicates with different args, but callable types really never
+                        // do that, so it's OK.
+                        for (predicate, span) in
+                            std::iter::zip(instantiated.predicates, instantiated.spans)
+                        {
+                            if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder()
+                                && pred.self_ty() == callee_ty
+                                && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some()
+                            {
+                                err.span_note(span, "callable defined here");
+                                return;
+                            }
+                        }
+                    }
+                }
+                ty::Opaque(new_def_id, _) | ty::Closure(new_def_id, _) | ty::FnDef(new_def_id, _) => {
+                    def_id = new_def_id;
+                }
+                _ => {
+                    return;
+                }
+            }
         }
 
-        let def_kind = tcx.def_kind(def_id);
-        err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
-    } else {
-        match tcx.hir().get_if_local(def_id) {
-            Some(hir::Node::Expr(hir::Expr {
-                kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }),
-                ..
-            })) => {
-                let spans: MultiSpan = (*fn_decl_span).into();
+        if let Some(def_span) = self.tcx.def_ident_span(def_id) && !def_span.is_dummy() {
+            let mut spans: MultiSpan = def_span.into();
 
-                // Note: We don't point to param spans here because they overlap
-                // with the closure span itself
+            let params = self
+                .tcx
+                .hir()
+                .get_if_local(def_id)
+                .and_then(|node| node.body_id())
+                .into_iter()
+                .flat_map(|id| self.tcx.hir().body(id).params);
 
-                err.span_note(spans, "closure defined here");
+            for param in params {
+                spans.push_span_label(param.span, "");
             }
-            _ => {}
+
+            let def_kind = self.tcx.def_kind(def_id);
+            err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
+        } else if let def_kind @ (DefKind::Closure | DefKind::OpaqueTy) = self.tcx.def_kind(def_id)
+        {
+            err.span_note(
+                self.tcx.def_span(def_id),
+                &format!("{} defined here", def_kind.descr(def_id)),
+            );
         }
     }
 }
diff --git a/src/test/ui/argument-suggestions/exotic-calls.rs b/src/test/ui/argument-suggestions/exotic-calls.rs
new file mode 100644
index 00000000000..a18e967668d
--- /dev/null
+++ b/src/test/ui/argument-suggestions/exotic-calls.rs
@@ -0,0 +1,26 @@
+fn foo<T: Fn()>(t: T) {
+    t(1i32);
+    //~^ ERROR this function takes 0 arguments but 1 argument was supplied
+}
+
+fn bar(t: impl Fn()) {
+    t(1i32);
+    //~^ ERROR this function takes 0 arguments but 1 argument was supplied
+}
+
+fn baz() -> impl Fn() {
+    || {}
+}
+
+fn baz2() {
+    baz()(1i32)
+    //~^ ERROR this function takes 0 arguments but 1 argument was supplied
+}
+
+fn qux() {
+    let x = || {};
+    x(1i32);
+    //~^ ERROR this function takes 0 arguments but 1 argument was supplied
+}
+
+fn main() {}
diff --git a/src/test/ui/argument-suggestions/exotic-calls.stderr b/src/test/ui/argument-suggestions/exotic-calls.stderr
new file mode 100644
index 00000000000..ca93ecc4e38
--- /dev/null
+++ b/src/test/ui/argument-suggestions/exotic-calls.stderr
@@ -0,0 +1,67 @@
+error[E0057]: this function takes 0 arguments but 1 argument was supplied
+  --> $DIR/exotic-calls.rs:2:5
+   |
+LL |     t(1i32);
+   |     ^ ---- argument of type `i32` unexpected
+   |
+note: callable defined here
+  --> $DIR/exotic-calls.rs:1:11
+   |
+LL | fn foo<T: Fn()>(t: T) {
+   |           ^^^^
+help: remove the extra argument
+   |
+LL |     t();
+   |     ~~~
+
+error[E0057]: this function takes 0 arguments but 1 argument was supplied
+  --> $DIR/exotic-calls.rs:7:5
+   |
+LL |     t(1i32);
+   |     ^ ---- argument of type `i32` unexpected
+   |
+note: type parameter defined here
+  --> $DIR/exotic-calls.rs:6:11
+   |
+LL | fn bar(t: impl Fn()) {
+   |           ^^^^^^^^^
+help: remove the extra argument
+   |
+LL |     t();
+   |     ~~~
+
+error[E0057]: this function takes 0 arguments but 1 argument was supplied
+  --> $DIR/exotic-calls.rs:16:5
+   |
+LL |     baz()(1i32)
+   |     ^^^^^ ---- argument of type `i32` unexpected
+   |
+note: opaque type defined here
+  --> $DIR/exotic-calls.rs:11:13
+   |
+LL | fn baz() -> impl Fn() {
+   |             ^^^^^^^^^
+help: remove the extra argument
+   |
+LL |     baz()()
+   |
+
+error[E0057]: this function takes 0 arguments but 1 argument was supplied
+  --> $DIR/exotic-calls.rs:22:5
+   |
+LL |     x(1i32);
+   |     ^ ---- argument of type `i32` unexpected
+   |
+note: closure defined here
+  --> $DIR/exotic-calls.rs:21:13
+   |
+LL |     let x = || {};
+   |             ^^
+help: remove the extra argument
+   |
+LL |     x();
+   |     ~~~
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0057`.
diff --git a/src/test/ui/issues/issue-16939.stderr b/src/test/ui/issues/issue-16939.stderr
index 294524f0b61..aaa3c49b3d8 100644
--- a/src/test/ui/issues/issue-16939.stderr
+++ b/src/test/ui/issues/issue-16939.stderr
@@ -4,11 +4,11 @@ error[E0057]: this function takes 0 arguments but 1 argument was supplied
 LL |     |t| f(t);
    |         ^ - argument unexpected
    |
-note: associated function defined here
-  --> $SRC_DIR/core/src/ops/function.rs:LL:COL
+note: callable defined here
+  --> $DIR/issue-16939.rs:4:12
    |
-LL |     extern "rust-call" fn call(&self, args: Args) -> Self::Output;
-   |                           ^^^^
+LL | fn _foo<F: Fn()> (f: F) {
+   |            ^^^^
 help: remove the extra argument
    |
 LL |     |t| f();
diff --git a/src/test/ui/mismatched_types/overloaded-calls-bad.stderr b/src/test/ui/mismatched_types/overloaded-calls-bad.stderr
index cb93a7ad900..5ed15468fd6 100644
--- a/src/test/ui/mismatched_types/overloaded-calls-bad.stderr
+++ b/src/test/ui/mismatched_types/overloaded-calls-bad.stderr
@@ -5,12 +5,6 @@ LL |     let ans = s("what");
    |               - ^^^^^^ expected `isize`, found `&str`
    |               |
    |               arguments to this function are incorrect
-   |
-note: associated function defined here
-  --> $SRC_DIR/core/src/ops/function.rs:LL:COL
-   |
-LL |     extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
-   |                           ^^^^^^^^
 
 error[E0057]: this function takes 1 argument but 0 arguments were supplied
   --> $DIR/overloaded-calls-bad.rs:29:15
@@ -18,11 +12,6 @@ error[E0057]: this function takes 1 argument but 0 arguments were supplied
 LL |     let ans = s();
    |               ^-- an argument of type `isize` is missing
    |
-note: associated function defined here
-  --> $SRC_DIR/core/src/ops/function.rs:LL:COL
-   |
-LL |     extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
-   |                           ^^^^^^^^
 help: provide the argument
    |
 LL |     let ans = s(/* isize */);
@@ -36,11 +25,6 @@ LL |     let ans = s("burma", "shave");
    |                 |
    |                 expected `isize`, found `&str`
    |
-note: associated function defined here
-  --> $SRC_DIR/core/src/ops/function.rs:LL:COL
-   |
-LL |     extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
-   |                           ^^^^^^^^
 help: remove the extra argument
    |
 LL |     let ans = s(/* isize */);