Skip to content

Commit 2e8de27

Browse files
Incompletely allow overloaded call from opaque when self type bottoms out in infer
1 parent 07ad2fa commit 2e8de27

File tree

2 files changed

+140
-35
lines changed

2 files changed

+140
-35
lines changed

compiler/rustc_hir_typeck/src/callee.rs

+123-35
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_middle::ty::adjustment::{
1414
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
1515
use rustc_middle::{bug, span_bug};
1616
use rustc_span::def_id::LocalDefId;
17-
use rustc_span::{Span, sym};
17+
use rustc_span::{Span, Symbol, sym};
1818
use rustc_trait_selection::error_reporting::traits::DefIdOrName;
1919
use rustc_trait_selection::infer::InferCtxtExt as _;
2020
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -66,7 +66,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6666
arg_exprs: &'tcx [hir::Expr<'tcx>],
6767
expected: Expectation<'tcx>,
6868
) -> Ty<'tcx> {
69-
let original_callee_ty = match &callee_expr.kind {
69+
let expr_ty = match &callee_expr.kind {
7070
hir::ExprKind::Path(hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)) => self
7171
.check_expr_with_expectation_and_args(
7272
callee_expr,
@@ -76,8 +76,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7676
_ => self.check_expr(callee_expr),
7777
};
7878

79-
let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty);
80-
8179
let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
8280
let mut result = None;
8381
while result.is_none() && autoderef.next().is_some() {
@@ -144,7 +142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
144142
autoderef: &Autoderef<'a, 'tcx>,
145143
) -> Option<CallStep<'tcx>> {
146144
let adjusted_ty =
147-
self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
145+
self.try_structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
148146

149147
// If the callee is a bare function or a closure, then we're all set.
150148
match *adjusted_ty.kind() {
@@ -241,6 +239,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
241239
return None;
242240
}
243241

242+
// We only want to confirm a call step here if the infer var
243+
// originated from the defining use of an opaque.
244+
ty::Infer(ty::TyVar(vid))
245+
if let Some(alias_ty) = self.find_sup_as_registered_opaque(vid) =>
246+
{
247+
return self
248+
.try_overloaded_call_traits_for_alias(call_expr, alias_ty, arg_exprs)
249+
.map(|(autoref, method)| {
250+
let mut adjustments = self.adjust_steps(autoderef);
251+
adjustments.extend(autoref);
252+
self.apply_adjustments(callee_expr, adjustments);
253+
CallStep::Overloaded(method)
254+
});
255+
}
256+
257+
ty::Infer(_) => {
258+
return None;
259+
}
260+
244261
ty::Error(_) => {
245262
return None;
246263
}
@@ -305,40 +322,111 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
305322

306323
// Try the options that are least restrictive on the caller first.
307324
for (opt_trait_def_id, method_name, borrow) in call_trait_choices {
308-
let Some(trait_def_id) = opt_trait_def_id else { continue };
325+
if let Some(confirmed) = self.try_overloaded_call_trait(
326+
call_expr,
327+
adjusted_ty,
328+
opt_arg_exprs,
329+
opt_trait_def_id,
330+
method_name,
331+
borrow,
332+
) {
333+
return Some(confirmed);
334+
}
335+
}
336+
337+
None
338+
}
339+
340+
fn try_overloaded_call_trait(
341+
&self,
342+
call_expr: &hir::Expr<'_>,
343+
call_ty: Ty<'tcx>,
344+
opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>,
345+
opt_trait_def_id: Option<DefId>,
346+
method_name: Symbol,
347+
borrow: bool,
348+
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
349+
let Some(trait_def_id) = opt_trait_def_id else {
350+
return None;
351+
};
352+
353+
let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
354+
Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span)))
355+
});
356+
357+
let Some(ok) = self.lookup_method_for_operator(
358+
self.misc(call_expr.span),
359+
method_name,
360+
trait_def_id,
361+
call_ty,
362+
opt_input_type,
363+
) else {
364+
return None;
365+
};
366+
let method = self.register_infer_ok_obligations(ok);
367+
let mut autoref = None;
368+
if borrow {
369+
// Check for &self vs &mut self in the method signature. Since this is either
370+
// the Fn or FnMut trait, it should be one of those.
371+
let ty::Ref(_, _, mutbl) = *method.sig.inputs()[0].kind() else {
372+
bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut")
373+
};
309374

310-
let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
311-
Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span)))
375+
// For initial two-phase borrow
376+
// deployment, conservatively omit
377+
// overloaded function call ops.
378+
let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::No);
379+
380+
autoref = Some(Adjustment {
381+
kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
382+
target: method.sig.inputs()[0],
312383
});
384+
}
313385

314-
if let Some(ok) = self.lookup_method_for_operator(
315-
self.misc(call_expr.span),
316-
method_name,
317-
trait_def_id,
318-
adjusted_ty,
319-
opt_input_type,
320-
) {
321-
let method = self.register_infer_ok_obligations(ok);
322-
let mut autoref = None;
323-
if borrow {
324-
// Check for &self vs &mut self in the method signature. Since this is either
325-
// the Fn or FnMut trait, it should be one of those.
326-
let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() else {
327-
bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut")
328-
};
329-
330-
// For initial two-phase borrow
331-
// deployment, conservatively omit
332-
// overloaded function call ops.
333-
let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No);
334-
335-
autoref = Some(Adjustment {
336-
kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
337-
target: method.sig.inputs()[0],
338-
});
339-
}
386+
Some((autoref, method))
387+
}
340388

341-
return Some((autoref, method));
389+
fn try_overloaded_call_traits_for_alias(
390+
&self,
391+
call_expr: &'tcx hir::Expr<'tcx>,
392+
alias_ty: ty::AliasTy<'tcx>,
393+
arg_exprs: &'tcx [rustc_hir::Expr<'tcx>],
394+
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
395+
let call_ty = alias_ty.to_ty(self.tcx);
396+
397+
let call_traits = [
398+
(self.tcx.lang_items().fn_trait(), sym::call, true),
399+
(self.tcx.lang_items().fn_mut_trait(), sym::call_mut, true),
400+
(self.tcx.lang_items().fn_once_trait(), sym::call_once, false),
401+
(self.tcx.lang_items().async_fn_trait(), sym::async_call, true),
402+
(self.tcx.lang_items().async_fn_mut_trait(), sym::async_call_mut, true),
403+
(self.tcx.lang_items().async_fn_once_trait(), sym::async_call_once, false),
404+
];
405+
// We only want to try a call trait if it shows up in the bounds
406+
// of the opaque. We confirm the first one that shows up in the
407+
// bounds list, which can lead to inference weirdness but doesn't
408+
// matter today.
409+
for clause in
410+
self.tcx.item_self_bounds(alias_ty.def_id).iter_instantiated(self.tcx, alias_ty.args)
411+
{
412+
let Some(poly_trait_ref) = clause.as_trait_clause() else {
413+
continue;
414+
};
415+
416+
if let Some(&(opt_trait_def_id, method_name, borrow)) =
417+
call_traits.iter().find(|(trait_def_id, _, _)| {
418+
trait_def_id.is_some_and(|trait_def_id| trait_def_id == poly_trait_ref.def_id())
419+
})
420+
&& let Some(confirmed) = self.try_overloaded_call_trait(
421+
call_expr,
422+
call_ty,
423+
Some(arg_exprs),
424+
opt_trait_def_id,
425+
method_name,
426+
borrow,
427+
)
428+
{
429+
return Some(confirmed);
342430
}
343431
}
344432

compiler/rustc_infer/src/infer/mod.rs

+17
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,23 @@ impl<'tcx> InferCtxt<'tcx> {
955955
self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect()
956956
}
957957

958+
pub fn find_sup_as_registered_opaque(&self, ty_vid: TyVid) -> Option<ty::AliasTy<'tcx>> {
959+
let ty_sub_vid = self.sub_root_var(ty_vid);
960+
let opaques: Vec<_> = self.inner.borrow_mut().opaque_types().iter_opaque_types().collect();
961+
opaques
962+
.into_iter()
963+
.find(|(_, hidden_ty)| {
964+
if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind()
965+
&& self.sub_root_var(hidden_vid) == ty_sub_vid
966+
{
967+
true
968+
} else {
969+
false
970+
}
971+
})
972+
.map(|(key, _)| ty::AliasTy::new_from_args(self.tcx, key.def_id.to_def_id(), key.args))
973+
}
974+
958975
#[inline(always)]
959976
pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool {
960977
debug_assert!(!self.next_trait_solver());

0 commit comments

Comments
 (0)