Skip to content

Commit 9ccb5e9

Browse files
committed
rarw
1 parent ec0775c commit 9ccb5e9

File tree

6 files changed

+207
-26
lines changed

6 files changed

+207
-26
lines changed

compiler/rustc_hir_typeck/src/callee.rs

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_middle::ty::adjustment::{
1515
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
1616
use rustc_middle::{bug, span_bug};
1717
use rustc_span::def_id::LocalDefId;
18-
use rustc_span::{Span, sym};
18+
use rustc_span::{Span, Symbol, sym};
1919
use rustc_target::spec::{AbiMap, AbiMapping};
2020
use rustc_trait_selection::error_reporting::traits::DefIdOrName;
2121
use rustc_trait_selection::infer::InferCtxtExt as _;
@@ -78,7 +78,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7878
_ => self.check_expr(callee_expr),
7979
};
8080

81-
let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty);
81+
let expr_ty = self.try_structurally_resolve_type(call_expr.span, original_callee_ty);
8282

8383
let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
8484
let mut result = None;
@@ -200,7 +200,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
200200
arg_exprs: &'tcx [hir::Expr<'tcx>],
201201
autoderef: &Autoderef<'a, 'tcx>,
202202
) -> Option<CallStep<'tcx>> {
203-
let adjusted_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty());
203+
let adjusted_ty =
204+
self.try_structurally_resolve_type(autoderef.span(), autoderef.final_ty());
204205

205206
// If the callee is a function pointer or a closure, then we're all set.
206207
match *adjusted_ty.kind() {
@@ -297,6 +298,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
297298
return None;
298299
}
299300

301+
ty::Infer(ty::TyVar(vid)) => {
302+
// If we end up with an inference variable which is not the hidden type of
303+
// an opaque, emit an error.
304+
if let Some(alias_ty) = self.find_opaque_type_related_to_vid(vid) {
305+
return self
306+
.try_overloaded_call_traits_for_alias(call_expr, alias_ty, arg_exprs)
307+
.map(|(autoref, method)| {
308+
let mut adjustments = self.adjust_steps(autoderef);
309+
adjustments.extend(autoref);
310+
self.apply_adjustments(callee_expr, adjustments);
311+
CallStep::Overloaded(method)
312+
});
313+
} else {
314+
self.type_must_be_known_at_this_point(autoderef.span(), adjusted_ty);
315+
return None;
316+
}
317+
}
318+
300319
ty::Error(_) => {
301320
return None;
302321
}
@@ -401,6 +420,102 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
401420
None
402421
}
403422

423+
fn try_overloaded_call_trait(
424+
&self,
425+
call_expr: &hir::Expr<'_>,
426+
call_ty: Ty<'tcx>,
427+
opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>,
428+
opt_trait_def_id: Option<DefId>,
429+
method_name: Symbol,
430+
borrow: bool,
431+
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
432+
let Some(trait_def_id) = opt_trait_def_id else {
433+
return None;
434+
};
435+
436+
let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
437+
Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span)))
438+
});
439+
440+
let Some(ok) = self.lookup_method_for_operator(
441+
self.misc(call_expr.span),
442+
method_name,
443+
trait_def_id,
444+
call_ty,
445+
opt_input_type,
446+
) else {
447+
return None;
448+
};
449+
let method = self.register_infer_ok_obligations(ok);
450+
let mut autoref = None;
451+
if borrow {
452+
// Check for &self vs &mut self in the method signature. Since this is either
453+
// the Fn or FnMut trait, it should be one of those.
454+
let ty::Ref(_, _, mutbl) = *method.sig.inputs()[0].kind() else {
455+
bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut")
456+
};
457+
458+
// For initial two-phase borrow
459+
// deployment, conservatively omit
460+
// overloaded function call ops.
461+
let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::No);
462+
463+
autoref = Some(Adjustment {
464+
kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
465+
target: method.sig.inputs()[0],
466+
});
467+
}
468+
469+
Some((autoref, method))
470+
}
471+
472+
fn try_overloaded_call_traits_for_alias(
473+
&self,
474+
call_expr: &'tcx hir::Expr<'tcx>,
475+
alias_ty: ty::AliasTy<'tcx>,
476+
arg_exprs: &'tcx [rustc_hir::Expr<'tcx>],
477+
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
478+
let call_ty = alias_ty.to_ty(self.tcx);
479+
480+
let call_traits = [
481+
(self.tcx.lang_items().fn_trait(), sym::call, true),
482+
(self.tcx.lang_items().fn_mut_trait(), sym::call_mut, true),
483+
(self.tcx.lang_items().fn_once_trait(), sym::call_once, false),
484+
(self.tcx.lang_items().async_fn_trait(), sym::async_call, true),
485+
(self.tcx.lang_items().async_fn_mut_trait(), sym::async_call_mut, true),
486+
(self.tcx.lang_items().async_fn_once_trait(), sym::async_call_once, false),
487+
];
488+
// We only want to try a call trait if it shows up in the bounds
489+
// of the opaque. We confirm the first one that shows up in the
490+
// bounds list, which can lead to inference weirdness but doesn't
491+
// matter today.
492+
for clause in
493+
self.tcx.item_self_bounds(alias_ty.def_id).iter_instantiated(self.tcx, alias_ty.args)
494+
{
495+
let Some(poly_trait_ref) = clause.as_trait_clause() else {
496+
continue;
497+
};
498+
499+
if let Some(&(opt_trait_def_id, method_name, borrow)) =
500+
call_traits.iter().find(|(trait_def_id, _, _)| {
501+
trait_def_id.is_some_and(|trait_def_id| trait_def_id == poly_trait_ref.def_id())
502+
})
503+
&& let Some(confirmed) = self.try_overloaded_call_trait(
504+
call_expr,
505+
call_ty,
506+
Some(arg_exprs),
507+
opt_trait_def_id,
508+
method_name,
509+
borrow,
510+
)
511+
{
512+
return Some(confirmed);
513+
}
514+
}
515+
516+
None
517+
}
518+
404519
/// Give appropriate suggestion when encountering `||{/* not callable */}()`, where the
405520
/// likely intention is to call the closure, suggest `(||{})()`. (#55851)
406521
fn identify_bad_closure_def_and_call(

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,24 +1469,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14691469
pub(crate) fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
14701470
let ty = self.try_structurally_resolve_type(sp, ty);
14711471

1472-
if !ty.is_ty_var() {
1473-
ty
1474-
} else {
1475-
let e = self.tainted_by_errors().unwrap_or_else(|| {
1476-
self.err_ctxt()
1477-
.emit_inference_failure_err(
1478-
self.body_id,
1479-
sp,
1480-
ty.into(),
1481-
TypeAnnotationNeeded::E0282,
1482-
true,
1483-
)
1484-
.emit()
1485-
});
1486-
let err = Ty::new_error(self.tcx, e);
1487-
self.demand_suptype(sp, err, ty);
1488-
err
1489-
}
1472+
if !ty.is_ty_var() { ty } else { self.type_must_be_known_at_this_point(sp, ty) }
1473+
}
1474+
1475+
#[cold]
1476+
pub(crate) fn type_must_be_known_at_this_point(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
1477+
let guar = self.tainted_by_errors().unwrap_or_else(|| {
1478+
self.err_ctxt()
1479+
.emit_inference_failure_err(
1480+
self.body_id,
1481+
sp,
1482+
ty.into(),
1483+
TypeAnnotationNeeded::E0282,
1484+
true,
1485+
)
1486+
.emit()
1487+
});
1488+
let err = Ty::new_error(self.tcx, guar);
1489+
self.demand_suptype(sp, err, ty);
1490+
err
14901491
}
14911492

14921493
pub(crate) fn structurally_resolve_const(

compiler/rustc_infer/src/infer/mod.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,35 @@ impl<'tcx> InferCtxt<'tcx> {
10001000
self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect()
10011001
}
10021002

1003+
/// Searches for an opaque type key whose hidden type is related to `ty_vid`.
1004+
///
1005+
/// This only checks for a subtype relation, it does not require equality.
1006+
pub fn find_opaque_type_related_to_vid(&self, ty_vid: TyVid) -> Option<ty::AliasTy<'tcx>> {
1007+
// Avoid accidentally allowing more code to compile with the old solver.
1008+
if !self.next_trait_solver() {
1009+
return None;
1010+
}
1011+
1012+
let ty_sub_vid = self.sub_root_var(ty_vid);
1013+
let inner = &mut *self.inner.borrow_mut();
1014+
// This is iffy, can't call `type_variables()` as we're already
1015+
// borrowing the `opaque_type_storage` here.
1016+
let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log);
1017+
let opaque = inner
1018+
.opaque_type_storage
1019+
.iter_opaque_types()
1020+
.find(|(_, hidden_ty)| {
1021+
if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() {
1022+
let opaque_sub_vid = type_variables.sub_root_var(hidden_vid);
1023+
opaque_sub_vid == ty_sub_vid
1024+
} else {
1025+
false
1026+
}
1027+
})
1028+
.map(|(key, _)| ty::AliasTy::new_from_args(self.tcx, key.def_id.to_def_id(), key.args));
1029+
opaque
1030+
}
1031+
10031032
#[inline(always)]
10041033
pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool {
10051034
debug_assert!(!self.next_trait_solver());
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//@ revisions: current next
2+
//@[next] compile-flags: -Znext-solver
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@ check-pass
5+
6+
fn fn_trait() -> impl Fn() {
7+
if false {
8+
let f = fn_trait();
9+
f();
10+
}
11+
12+
|| ()
13+
}
14+
15+
fn fn_mut() -> impl FnMut() -> usize {
16+
if false {
17+
let mut f = fn_mut();
18+
f();
19+
}
20+
21+
let mut state = 0;
22+
move || {
23+
state += 1;
24+
state
25+
}
26+
}
27+
28+
fn fn_once() -> impl FnOnce() {
29+
if false {
30+
let mut f = fn_once();
31+
f();
32+
}
33+
34+
let string = String::new();
35+
move || drop(string)
36+
}
37+
38+
fn main() {}

tests/ui/inference/return-block-type-inference-15965.stderr

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
error[E0282]: type annotations needed
22
--> $DIR/return-block-type-inference-15965.rs:5:9
33
|
4-
LL | / { return () }
5-
LL | |
6-
LL | | ()
7-
| |______^ cannot infer type
4+
LL | { return () }
5+
| ^^^^^^^^^^^^^ cannot infer type
86

97
error: aborting due to 1 previous error
108

tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | let mut closure0 = None;
55
| ^^^^^^^^^^^^
66
...
77
LL | return c();
8-
| --- type must be known at this point
8+
| - type must be known at this point
99
|
1010
help: consider giving `closure0` an explicit type, where the placeholders `_` are specified
1111
|

0 commit comments

Comments
 (0)