Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 3c7c8d3

Browse files
compiler-errorslcnr
authored andcommitted
Incompletely allow overloaded call from opaque when self type bottoms out in infer
1 parent 09e02a1 commit 3c7c8d3

File tree

2 files changed

+146
-58
lines changed

2 files changed

+146
-58
lines changed

compiler/rustc_hir_typeck/src/callee.rs

Lines changed: 129 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::iter;
22

33
use rustc_ast::util::parser::ExprPrecedence;
4-
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
4+
use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
55
use rustc_hir::def::{self, CtorKind, Namespace, Res};
66
use rustc_hir::def_id::DefId;
77
use rustc_hir::{self as hir, HirId, LangItem};
@@ -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 _;
@@ -58,6 +58,7 @@ enum CallStep<'tcx> {
5858
Overloaded(MethodCallee<'tcx>),
5959
}
6060

61+
#[allow(unused)] // TODO
6162
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6263
pub(crate) fn check_expr_call(
6364
&self,
@@ -66,7 +67,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6667
arg_exprs: &'tcx [hir::Expr<'tcx>],
6768
expected: Expectation<'tcx>,
6869
) -> Ty<'tcx> {
69-
let original_callee_ty = match &callee_expr.kind {
70+
let expr_ty = match &callee_expr.kind {
7071
hir::ExprKind::Path(hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)) => self
7172
.check_expr_with_expectation_and_args(
7273
callee_expr,
@@ -76,8 +77,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7677
_ => self.check_expr(callee_expr),
7778
};
7879

79-
let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty);
80-
8180
let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
8281
let mut result = None;
8382
while result.is_none() && autoderef.next().is_some() {
@@ -87,29 +86,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8786

8887
let output = match result {
8988
None => {
90-
// Check all of the arg expressions, but with no expectations
91-
// since we don't have a signature to compare them to.
92-
for arg in arg_exprs {
93-
self.check_expr(arg);
94-
}
89+
// First report an ambiguity error if possible.
90+
let expr_ty = self.structurally_resolve_type(call_expr.span, expr_ty);
9591

96-
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind
97-
&& let [segment] = path.segments
98-
{
99-
self.dcx().try_steal_modify_and_emit_err(
100-
segment.ident.span,
101-
StashKey::CallIntoMethod,
102-
|err| {
103-
// Try suggesting `foo(a)` -> `a.foo()` if possible.
104-
self.suggest_call_as_method(
105-
err, segment, arg_exprs, call_expr, expected,
106-
);
107-
},
108-
);
109-
}
110-
111-
let guar = self.report_invalid_callee(call_expr, callee_expr, expr_ty, arg_exprs);
112-
Ty::new_error(self.tcx, guar)
92+
// this will report an error since original_callee_ty is not a fn
93+
self.confirm_builtin_call(call_expr, callee_expr, expr_ty, arg_exprs, expected)
11394
}
11495

11596
Some(CallStep::Builtin(callee_ty)) => {
@@ -144,7 +125,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
144125
autoderef: &Autoderef<'a, 'tcx>,
145126
) -> Option<CallStep<'tcx>> {
146127
let adjusted_ty =
147-
self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
128+
self.try_structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
148129

149130
// If the callee is a bare function or a closure, then we're all set.
150131
match *adjusted_ty.kind() {
@@ -241,6 +222,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
241222
return None;
242223
}
243224

225+
// We only want to confirm a call step here if the infer var
226+
// originated from the defining use of an opaque.
227+
ty::Infer(ty::TyVar(vid))
228+
if let Some(alias_ty) = self.find_sup_as_registered_opaque(vid) =>
229+
{
230+
return self
231+
.try_overloaded_call_traits_for_alias(call_expr, alias_ty, arg_exprs)
232+
.map(|(autoref, method)| {
233+
let mut adjustments = self.adjust_steps(autoderef);
234+
adjustments.extend(autoref);
235+
self.apply_adjustments(callee_expr, adjustments);
236+
CallStep::Overloaded(method)
237+
});
238+
}
239+
240+
ty::Infer(_) => {
241+
return None;
242+
}
243+
244244
ty::Error(_) => {
245245
return None;
246246
}
@@ -305,40 +305,111 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
305305

306306
// Try the options that are least restrictive on the caller first.
307307
for (opt_trait_def_id, method_name, borrow) in call_trait_choices {
308-
let Some(trait_def_id) = opt_trait_def_id else { continue };
308+
if let Some(confirmed) = self.try_overloaded_call_trait(
309+
call_expr,
310+
adjusted_ty,
311+
opt_arg_exprs,
312+
opt_trait_def_id,
313+
method_name,
314+
borrow,
315+
) {
316+
return Some(confirmed);
317+
}
318+
}
309319

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)))
320+
None
321+
}
322+
323+
fn try_overloaded_call_trait(
324+
&self,
325+
call_expr: &hir::Expr<'_>,
326+
call_ty: Ty<'tcx>,
327+
opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>,
328+
opt_trait_def_id: Option<DefId>,
329+
method_name: Symbol,
330+
borrow: bool,
331+
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
332+
let Some(trait_def_id) = opt_trait_def_id else {
333+
return None;
334+
};
335+
336+
let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
337+
Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span)))
338+
});
339+
340+
let Some(ok) = self.lookup_method_for_operator(
341+
self.misc(call_expr.span),
342+
method_name,
343+
trait_def_id,
344+
call_ty,
345+
opt_input_type,
346+
) else {
347+
return None;
348+
};
349+
let method = self.register_infer_ok_obligations(ok);
350+
let mut autoref = None;
351+
if borrow {
352+
// Check for &self vs &mut self in the method signature. Since this is either
353+
// the Fn or FnMut trait, it should be one of those.
354+
let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() else {
355+
bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut")
356+
};
357+
358+
// For initial two-phase borrow
359+
// deployment, conservatively omit
360+
// overloaded function call ops.
361+
let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No);
362+
363+
autoref = Some(Adjustment {
364+
kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
365+
target: method.sig.inputs()[0],
312366
});
367+
}
313368

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-
}
369+
Some((autoref, method))
370+
}
371+
372+
fn try_overloaded_call_traits_for_alias(
373+
&self,
374+
call_expr: &'tcx hir::Expr<'tcx>,
375+
alias_ty: ty::AliasTy<'tcx>,
376+
arg_exprs: &'tcx [rustc_hir::Expr<'tcx>],
377+
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
378+
let call_ty = alias_ty.to_ty(self.tcx);
379+
380+
let call_traits = [
381+
(self.tcx.lang_items().fn_trait(), sym::call, true),
382+
(self.tcx.lang_items().fn_mut_trait(), sym::call_mut, true),
383+
(self.tcx.lang_items().fn_once_trait(), sym::call_once, false),
384+
(self.tcx.lang_items().async_fn_trait(), sym::async_call, true),
385+
(self.tcx.lang_items().async_fn_mut_trait(), sym::async_call_mut, true),
386+
(self.tcx.lang_items().async_fn_once_trait(), sym::async_call_once, false),
387+
];
388+
// We only want to try a call trait if it shows up in the bounds
389+
// of the opaque. We confirm the first one that shows up in the
390+
// bounds list, which can lead to inference weirdness but doesn't
391+
// matter today.
392+
for clause in
393+
self.tcx.item_self_bounds(alias_ty.def_id).iter_instantiated(self.tcx, alias_ty.args)
394+
{
395+
let Some(poly_trait_ref) = clause.as_trait_clause() else {
396+
continue;
397+
};
340398

341-
return Some((autoref, method));
399+
if let Some(&(opt_trait_def_id, method_name, borrow)) =
400+
call_traits.iter().find(|(trait_def_id, _, _)| {
401+
trait_def_id.is_some_and(|trait_def_id| trait_def_id == poly_trait_ref.def_id())
402+
})
403+
&& let Some(confirmed) = self.try_overloaded_call_trait(
404+
call_expr,
405+
call_ty,
406+
Some(arg_exprs),
407+
opt_trait_def_id,
408+
method_name,
409+
borrow,
410+
)
411+
{
412+
return Some(confirmed);
342413
}
343414
}
344415

compiler/rustc_infer/src/infer/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,23 @@ impl<'tcx> InferCtxt<'tcx> {
959959
self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect()
960960
}
961961

962+
pub fn find_sup_as_registered_opaque(&self, ty_vid: TyVid) -> Option<ty::AliasTy<'tcx>> {
963+
let ty_sub_vid = self.sub_root_var(ty_vid);
964+
let opaques = self.inner.borrow().opaque_type_storage.opaque_types.clone();
965+
opaques
966+
.into_iter()
967+
.find(|(_, hidden_ty)| {
968+
if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind()
969+
&& self.sub_root_var(hidden_vid) == ty_sub_vid
970+
{
971+
true
972+
} else {
973+
false
974+
}
975+
})
976+
.map(|(key, _)| ty::AliasTy::new_from_args(self.tcx, key.def_id.to_def_id(), key.args))
977+
}
978+
962979
#[inline(always)]
963980
pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool {
964981
debug_assert!(!self.next_trait_solver());

0 commit comments

Comments
 (0)