Skip to content

Commit ddb7003

Browse files
Add support for APIT and RPIT callables in label_fn_like
1 parent c2f428d commit ddb7003

File tree

5 files changed

+195
-60
lines changed

5 files changed

+195
-60
lines changed

compiler/rustc_typeck/src/check/fn_ctxt/checks.rs

+98-40
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use rustc_infer::infer::InferOk;
2525
use rustc_infer::infer::TypeTrace;
2626
use rustc_middle::ty::adjustment::AllowTwoPhase;
2727
use rustc_middle::ty::visit::TypeVisitable;
28-
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
28+
use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty};
2929
use rustc_session::Session;
3030
use rustc_span::symbol::Ident;
3131
use rustc_span::{self, Span};
@@ -89,7 +89,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8989
args_no_rcvr,
9090
false,
9191
tuple_arguments,
92-
None,
92+
method.ok().map(|method| method.def_id),
9393
);
9494
return self.tcx.ty_error();
9595
}
@@ -458,6 +458,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
458458
c_variadic,
459459
err_code,
460460
fn_def_id,
461+
call_expr,
461462
);
462463
}
463464
}
@@ -474,6 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
474475
c_variadic: bool,
475476
err_code: &str,
476477
fn_def_id: Option<DefId>,
478+
call_expr: &hir::Expr<'tcx>,
477479
) {
478480
// Don't print if it has error types or is just plain `_`
479481
fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
@@ -495,6 +497,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
495497
(self.resolve_vars_if_possible(ty), expr.span)
496498
})
497499
.collect();
500+
let callee_expr = match &call_expr.peel_blocks().kind {
501+
hir::ExprKind::Call(callee, _) => Some(*callee),
502+
hir::ExprKind::MethodCall(_, callee, _) => {
503+
if let Some((DefKind::AssocFn, def_id)) =
504+
self.typeck_results.borrow().type_dependent_def(call_expr.hir_id)
505+
&& let Some(assoc) = tcx.opt_associated_item(def_id)
506+
&& assoc.fn_has_self_parameter
507+
{
508+
Some(&callee[0])
509+
} else {
510+
None
511+
}
512+
}
513+
_ => None,
514+
};
515+
let callee_ty = callee_expr
516+
.and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr));
498517

499518
// A "softer" version of the `demand_compatible`, which checks types without persisting them,
500519
// and treats error types differently
@@ -631,7 +650,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
631650
Applicability::MachineApplicable,
632651
);
633652
};
634-
label_fn_like(tcx, &mut err, fn_def_id);
653+
self.label_fn_like(&mut err, fn_def_id, callee_ty);
635654
err.emit();
636655
return;
637656
}
@@ -721,7 +740,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
721740
format!("arguments to this {} are incorrect", call_name),
722741
);
723742
// Call out where the function is defined
724-
label_fn_like(tcx, &mut err, fn_def_id);
743+
self.label_fn_like(&mut err, fn_def_id, callee_ty);
725744
err.emit();
726745
return;
727746
}
@@ -1003,7 +1022,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10031022
}
10041023

10051024
// Call out where the function is defined
1006-
label_fn_like(tcx, &mut err, fn_def_id);
1025+
self.label_fn_like(&mut err, fn_def_id, callee_ty);
10071026

10081027
// And add a suggestion block for all of the parameters
10091028
let suggestion_text = match suggestion_text {
@@ -1795,47 +1814,86 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17951814
}
17961815
}
17971816
}
1798-
}
17991817

1800-
fn label_fn_like<'tcx>(
1801-
tcx: TyCtxt<'tcx>,
1802-
err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>,
1803-
def_id: Option<DefId>,
1804-
) {
1805-
let Some(def_id) = def_id else {
1806-
return;
1807-
};
1808-
1809-
if let Some(def_span) = tcx.def_ident_span(def_id) {
1810-
let mut spans: MultiSpan = def_span.into();
1811-
1812-
let params = tcx
1813-
.hir()
1814-
.get_if_local(def_id)
1815-
.and_then(|node| node.body_id())
1816-
.into_iter()
1817-
.flat_map(|id| tcx.hir().body(id).params);
1818-
1819-
for param in params {
1820-
spans.push_span_label(param.span, "");
1818+
fn label_fn_like(
1819+
&self,
1820+
err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>,
1821+
def_id: Option<DefId>,
1822+
callee_ty: Option<Ty<'tcx>>,
1823+
) {
1824+
let Some(mut def_id) = def_id else {
1825+
return;
1826+
};
1827+
1828+
if let Some(assoc_item) = self.tcx.opt_associated_item(def_id)
1829+
&& let trait_def_id = assoc_item.trait_item_def_id.unwrap_or_else(|| self.tcx.parent(def_id))
1830+
// Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce"
1831+
&& ty::ClosureKind::from_def_id(self.tcx, trait_def_id).is_some()
1832+
&& let Some(callee_ty) = callee_ty
1833+
{
1834+
let callee_ty = callee_ty.peel_refs();
1835+
match *callee_ty.kind() {
1836+
ty::Param(param) => {
1837+
let param =
1838+
self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx);
1839+
if param.kind.is_synthetic() {
1840+
// if it's `impl Fn() -> ..` then just fall down to the def-id based logic
1841+
def_id = param.def_id;
1842+
} else {
1843+
// Otherwise, find the predicate that makes this generic callable,
1844+
// and point at that.
1845+
let instantiated = self
1846+
.tcx
1847+
.explicit_predicates_of(self.body_id.owner)
1848+
.instantiate_identity(self.tcx);
1849+
// FIXME(compiler-errors): This could be problematic if something has two
1850+
// fn-like predicates with different args, but callable types really never
1851+
// do that, so it's OK.
1852+
for (predicate, span) in
1853+
std::iter::zip(instantiated.predicates, instantiated.spans)
1854+
{
1855+
if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder()
1856+
&& pred.self_ty() == callee_ty
1857+
&& ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some()
1858+
{
1859+
err.span_note(span, "callable defined here");
1860+
return;
1861+
}
1862+
}
1863+
}
1864+
}
1865+
ty::Opaque(new_def_id, _) | ty::Closure(new_def_id, _) | ty::FnDef(new_def_id, _) => {
1866+
def_id = new_def_id;
1867+
}
1868+
_ => {
1869+
return;
1870+
}
1871+
}
18211872
}
18221873

1823-
let def_kind = tcx.def_kind(def_id);
1824-
err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
1825-
} else {
1826-
match tcx.hir().get_if_local(def_id) {
1827-
Some(hir::Node::Expr(hir::Expr {
1828-
kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }),
1829-
..
1830-
})) => {
1831-
let spans: MultiSpan = (*fn_decl_span).into();
1874+
if let Some(def_span) = self.tcx.def_ident_span(def_id) && !def_span.is_dummy() {
1875+
let mut spans: MultiSpan = def_span.into();
18321876

1833-
// Note: We don't point to param spans here because they overlap
1834-
// with the closure span itself
1877+
let params = self
1878+
.tcx
1879+
.hir()
1880+
.get_if_local(def_id)
1881+
.and_then(|node| node.body_id())
1882+
.into_iter()
1883+
.flat_map(|id| self.tcx.hir().body(id).params);
18351884

1836-
err.span_note(spans, "closure defined here");
1885+
for param in params {
1886+
spans.push_span_label(param.span, "");
18371887
}
1838-
_ => {}
1888+
1889+
let def_kind = self.tcx.def_kind(def_id);
1890+
err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
1891+
} else if let def_kind @ (DefKind::Closure | DefKind::OpaqueTy) = self.tcx.def_kind(def_id)
1892+
{
1893+
err.span_note(
1894+
self.tcx.def_span(def_id),
1895+
&format!("{} defined here", def_kind.descr(def_id)),
1896+
);
18391897
}
18401898
}
18411899
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
fn foo<T: Fn()>(t: T) {
2+
t(1i32);
3+
//~^ ERROR this function takes 0 arguments but 1 argument was supplied
4+
}
5+
6+
fn bar(t: impl Fn()) {
7+
t(1i32);
8+
//~^ ERROR this function takes 0 arguments but 1 argument was supplied
9+
}
10+
11+
fn baz() -> impl Fn() {
12+
|| {}
13+
}
14+
15+
fn baz2() {
16+
baz()(1i32)
17+
//~^ ERROR this function takes 0 arguments but 1 argument was supplied
18+
}
19+
20+
fn qux() {
21+
let x = || {};
22+
x(1i32);
23+
//~^ ERROR this function takes 0 arguments but 1 argument was supplied
24+
}
25+
26+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
error[E0057]: this function takes 0 arguments but 1 argument was supplied
2+
--> $DIR/exotic-calls.rs:2:5
3+
|
4+
LL | t(1i32);
5+
| ^ ---- argument of type `i32` unexpected
6+
|
7+
note: callable defined here
8+
--> $DIR/exotic-calls.rs:1:11
9+
|
10+
LL | fn foo<T: Fn()>(t: T) {
11+
| ^^^^
12+
help: remove the extra argument
13+
|
14+
LL | t();
15+
| ~~~
16+
17+
error[E0057]: this function takes 0 arguments but 1 argument was supplied
18+
--> $DIR/exotic-calls.rs:7:5
19+
|
20+
LL | t(1i32);
21+
| ^ ---- argument of type `i32` unexpected
22+
|
23+
note: type parameter defined here
24+
--> $DIR/exotic-calls.rs:6:11
25+
|
26+
LL | fn bar(t: impl Fn()) {
27+
| ^^^^^^^^^
28+
help: remove the extra argument
29+
|
30+
LL | t();
31+
| ~~~
32+
33+
error[E0057]: this function takes 0 arguments but 1 argument was supplied
34+
--> $DIR/exotic-calls.rs:16:5
35+
|
36+
LL | baz()(1i32)
37+
| ^^^^^ ---- argument of type `i32` unexpected
38+
|
39+
note: opaque type defined here
40+
--> $DIR/exotic-calls.rs:11:13
41+
|
42+
LL | fn baz() -> impl Fn() {
43+
| ^^^^^^^^^
44+
help: remove the extra argument
45+
|
46+
LL | baz()()
47+
|
48+
49+
error[E0057]: this function takes 0 arguments but 1 argument was supplied
50+
--> $DIR/exotic-calls.rs:22:5
51+
|
52+
LL | x(1i32);
53+
| ^ ---- argument of type `i32` unexpected
54+
|
55+
note: closure defined here
56+
--> $DIR/exotic-calls.rs:21:13
57+
|
58+
LL | let x = || {};
59+
| ^^
60+
help: remove the extra argument
61+
|
62+
LL | x();
63+
| ~~~
64+
65+
error: aborting due to 4 previous errors
66+
67+
For more information about this error, try `rustc --explain E0057`.

src/test/ui/issues/issue-16939.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ error[E0057]: this function takes 0 arguments but 1 argument was supplied
44
LL | |t| f(t);
55
| ^ - argument unexpected
66
|
7-
note: associated function defined here
8-
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
7+
note: callable defined here
8+
--> $DIR/issue-16939.rs:4:12
99
|
10-
LL | extern "rust-call" fn call(&self, args: Args) -> Self::Output;
11-
| ^^^^
10+
LL | fn _foo<F: Fn()> (f: F) {
11+
| ^^^^
1212
help: remove the extra argument
1313
|
1414
LL | |t| f();

src/test/ui/mismatched_types/overloaded-calls-bad.stderr

-16
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,13 @@ LL | let ans = s("what");
55
| - ^^^^^^ expected `isize`, found `&str`
66
| |
77
| arguments to this function are incorrect
8-
|
9-
note: associated function defined here
10-
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
11-
|
12-
LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
13-
| ^^^^^^^^
148

159
error[E0057]: this function takes 1 argument but 0 arguments were supplied
1610
--> $DIR/overloaded-calls-bad.rs:29:15
1711
|
1812
LL | let ans = s();
1913
| ^-- an argument of type `isize` is missing
2014
|
21-
note: associated function defined here
22-
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
23-
|
24-
LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
25-
| ^^^^^^^^
2615
help: provide the argument
2716
|
2817
LL | let ans = s(/* isize */);
@@ -36,11 +25,6 @@ LL | let ans = s("burma", "shave");
3625
| |
3726
| expected `isize`, found `&str`
3827
|
39-
note: associated function defined here
40-
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
41-
|
42-
LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
43-
| ^^^^^^^^
4428
help: remove the extra argument
4529
|
4630
LL | let ans = s(/* isize */);

0 commit comments

Comments
 (0)