Skip to content

Commit 0734200

Browse files
Use autoderef
1 parent dca5f5b commit 0734200

File tree

3 files changed

+78
-43
lines changed

3 files changed

+78
-43
lines changed

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

Lines changed: 51 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -61,55 +61,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6161
pointing_at_return_type
6262
}
6363

64-
/// When encountering an fn-like ctor that needs to unify with a value, check whether calling
65-
/// the ctor would successfully solve the type mismatch and if so, suggest it:
64+
/// When encountering an fn-like type, try accessing the output of the type
65+
/// // and suggesting calling it if it satisfies a predicate (i.e. if the
66+
/// output has a method or a field):
6667
/// ```compile_fail,E0308
6768
/// fn foo(x: usize) -> usize { x }
6869
/// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
6970
/// ```
70-
fn suggest_fn_call(
71+
pub(crate) fn suggest_fn_call(
7172
&self,
7273
err: &mut Diagnostic,
7374
expr: &hir::Expr<'_>,
74-
expected: Ty<'tcx>,
7575
found: Ty<'tcx>,
76+
can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
7677
) -> bool {
77-
let (def_id, output, inputs) = match *found.kind() {
78-
ty::FnDef(def_id, _) => {
79-
let fn_sig = found.fn_sig(self.tcx);
80-
(def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len())
81-
}
82-
ty::Closure(def_id, substs) => {
83-
let fn_sig = substs.as_closure().sig();
84-
(def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)
85-
}
86-
ty::Opaque(def_id, substs) => {
87-
let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
88-
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
89-
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
90-
// args tuple will always be substs[1]
91-
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
92-
{
93-
Some((
94-
pred.kind().rebind(proj.term.ty().unwrap()),
95-
args.len(),
96-
))
78+
// Autoderef is useful here because sometimes we box callables, etc.
79+
let Some((def_id, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
80+
match *found.kind() {
81+
ty::FnPtr(fn_sig) => Some((None, fn_sig.output(), fn_sig.inputs().skip_binder().len())),
82+
ty::FnDef(def_id, _) => {
83+
let fn_sig = found.fn_sig(self.tcx);
84+
Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len()))
85+
}
86+
ty::Closure(def_id, substs) => {
87+
let fn_sig = substs.as_closure().sig();
88+
Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1))
89+
}
90+
ty::Opaque(def_id, substs) => {
91+
let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
92+
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
93+
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
94+
// args tuple will always be substs[1]
95+
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
96+
{
97+
Some((
98+
pred.kind().rebind(proj.term.ty().unwrap()),
99+
args.len(),
100+
))
101+
} else {
102+
None
103+
}
104+
});
105+
if let Some((output, inputs)) = sig {
106+
Some((Some(def_id), output, inputs))
97107
} else {
98108
None
99109
}
100-
});
101-
if let Some((output, inputs)) = sig {
102-
(def_id, output, inputs)
103-
} else {
104-
return false;
105110
}
111+
_ => None,
106112
}
107-
_ => return false,
108-
};
113+
}) else { return false; };
109114

110115
let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
111-
let output = self.normalize_associated_types_in(expr.span, output);
112-
if !output.is_ty_var() && self.can_coerce(output, expected) {
116+
// We don't want to register any extra obligations, which should be
117+
// implied by wf, but also because that would possibly result in
118+
// erroneous errors later on.
119+
let infer::InferOk { value: output, obligations: _ } =
120+
self.normalize_associated_types_in_as_infer_ok(expr.span, output);
121+
if !output.is_ty_var() && can_satisfy(output) {
113122
let (sugg_call, mut applicability) = match inputs {
114123
0 => ("".to_string(), Applicability::MachineApplicable),
115124
1..=4 => (
@@ -119,11 +128,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
119128
_ => ("...".to_string(), Applicability::HasPlaceholders),
120129
};
121130

122-
let msg = match self.tcx.def_kind(def_id) {
123-
DefKind::Fn => "call this function",
124-
DefKind::Closure | DefKind::OpaqueTy => "call this closure",
125-
DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct",
126-
DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant",
131+
let msg = match def_id.map(|def_id| self.tcx.def_kind(def_id)) {
132+
Some(DefKind::Fn) => "call this function",
133+
Some(DefKind::Closure | DefKind::OpaqueTy) => "call this closure",
134+
Some(DefKind::Ctor(CtorOf::Struct, _)) => "instantiate this tuple struct",
135+
Some(DefKind::Ctor(CtorOf::Variant, _)) => "instantiate this tuple variant",
127136
_ => "call this function",
128137
};
129138

@@ -178,12 +187,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
178187
} else {
179188
err.span_suggestion(sp, &msg, suggestion, applicability);
180189
}
181-
} else if let (ty::FnDef(def_id, ..), true) =
182-
(&found.kind(), self.suggest_fn_call(err, expr, expected, found))
190+
} else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
191+
&& let ty::FnDef(def_id, ..) = &found.kind()
192+
&& let Some(sp) = self.tcx.hir().span_if_local(*def_id)
183193
{
184-
if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
185-
err.span_label(sp, format!("{found} defined here"));
186-
}
194+
err.span_label(sp, format!("{found} defined here"));
187195
} else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
188196
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
189197
if !methods.is_empty() {

src/test/ui/suggestions/call-boxed.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn main() {
2+
let mut x = 1i32;
3+
let y = Box::new(|| 1);
4+
x = y;
5+
//~^ ERROR mismatched types
6+
//~| HELP use parentheses to call this closure
7+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/call-boxed.rs:4:9
3+
|
4+
LL | let mut x = 1i32;
5+
| ---- expected due to this value
6+
LL | let y = Box::new(|| 1);
7+
| -- the found closure
8+
LL | x = y;
9+
| ^ expected `i32`, found struct `Box`
10+
|
11+
= note: expected type `i32`
12+
found struct `Box<[closure@$DIR/call-boxed.rs:3:22: 3:24]>`
13+
help: use parentheses to call this closure
14+
|
15+
LL | x = y();
16+
| ++
17+
18+
error: aborting due to previous error
19+
20+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)