Skip to content

Incompletely allow overloaded call from opaque when self type bottoms out in infer #140496

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 127 additions & 42 deletions compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_middle::ty::adjustment::{
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::{bug, span_bug};
use rustc_span::def_id::LocalDefId;
use rustc_span::{Ident, Span, sym};
use rustc_span::{Ident, Span, Symbol, sym};
use rustc_trait_selection::error_reporting::traits::DefIdOrName;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
Expand Down Expand Up @@ -66,7 +66,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
arg_exprs: &'tcx [hir::Expr<'tcx>],
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
let original_callee_ty = match &callee_expr.kind {
let expr_ty = match &callee_expr.kind {
hir::ExprKind::Path(hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)) => self
.check_expr_with_expectation_and_args(
callee_expr,
Expand All @@ -76,8 +76,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => self.check_expr(callee_expr),
};

let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty);

let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
let mut result = None;
while result.is_none() && autoderef.next().is_some() {
Expand All @@ -87,14 +85,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let output = match result {
None => {
// First report an ambiguity error if possible.
let expr_ty = self.structurally_resolve_type(call_expr.span, expr_ty);

// this will report an error since original_callee_ty is not a fn
self.confirm_builtin_call(
call_expr,
callee_expr,
original_callee_ty,
arg_exprs,
expected,
)
self.confirm_builtin_call(call_expr, callee_expr, expr_ty, arg_exprs, expected)
}

Some(CallStep::Builtin(callee_ty)) => {
Expand Down Expand Up @@ -129,7 +124,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
autoderef: &Autoderef<'a, 'tcx>,
) -> Option<CallStep<'tcx>> {
let adjusted_ty =
self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
self.try_structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));

// If the callee is a bare function or a closure, then we're all set.
match *adjusted_ty.kind() {
Expand Down Expand Up @@ -226,6 +221,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
}

// We only want to confirm a call step here if the infer var
// originated from the defining use of an opaque.
ty::Infer(ty::TyVar(vid))
if let Some(alias_ty) = self.find_sup_as_registered_opaque(vid) =>
{
return self
.try_overloaded_call_traits_for_alias(call_expr, alias_ty, arg_exprs)
.map(|(autoref, method)| {
let mut adjustments = self.adjust_steps(autoderef);
adjustments.extend(autoref);
self.apply_adjustments(callee_expr, adjustments);
CallStep::Overloaded(method)
});
}

ty::Infer(_) => {
return None;
}

ty::Error(_) => {
return None;
}
Expand Down Expand Up @@ -290,40 +304,111 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// Try the options that are least restrictive on the caller first.
for (opt_trait_def_id, method_name, borrow) in call_trait_choices {
let Some(trait_def_id) = opt_trait_def_id else { continue };
if let Some(confirmed) = self.try_overloaded_call_trait(
call_expr,
adjusted_ty,
opt_arg_exprs,
opt_trait_def_id,
method_name,
borrow,
) {
return Some(confirmed);
}
}

None
}

let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span)))
fn try_overloaded_call_trait(
&self,
call_expr: &hir::Expr<'_>,
call_ty: Ty<'tcx>,
opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>,
opt_trait_def_id: Option<DefId>,
method_name: Symbol,
borrow: bool,
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
let Some(trait_def_id) = opt_trait_def_id else {
return None;
};

let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span)))
});

let Some(ok) = self.lookup_method_in_trait(
self.misc(call_expr.span),
Ident::with_dummy_span(method_name),
trait_def_id,
call_ty,
opt_input_type,
) else {
return None;
};
let method = self.register_infer_ok_obligations(ok);
let mut autoref = None;
if borrow {
// Check for &self vs &mut self in the method signature. Since this is either
// the Fn or FnMut trait, it should be one of those.
let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() else {
bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut")
};

// For initial two-phase borrow
// deployment, conservatively omit
// overloaded function call ops.
let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No);

autoref = Some(Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
target: method.sig.inputs()[0],
});
}

if let Some(ok) = self.lookup_method_in_trait(
self.misc(call_expr.span),
Ident::with_dummy_span(method_name),
trait_def_id,
adjusted_ty,
opt_input_type,
) {
let method = self.register_infer_ok_obligations(ok);
let mut autoref = None;
if borrow {
// Check for &self vs &mut self in the method signature. Since this is either
// the Fn or FnMut trait, it should be one of those.
let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() else {
bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut")
};

// For initial two-phase borrow
// deployment, conservatively omit
// overloaded function call ops.
let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No);

autoref = Some(Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
target: method.sig.inputs()[0],
});
}
Some((autoref, method))
}

return Some((autoref, method));
fn try_overloaded_call_traits_for_alias(
&self,
call_expr: &'tcx hir::Expr<'tcx>,
alias_ty: ty::AliasTy<'tcx>,
arg_exprs: &'tcx [rustc_hir::Expr<'tcx>],
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
let call_ty = alias_ty.to_ty(self.tcx);

let call_traits = [
(self.tcx.lang_items().fn_trait(), sym::call, true),
(self.tcx.lang_items().fn_mut_trait(), sym::call_mut, true),
(self.tcx.lang_items().fn_once_trait(), sym::call_once, false),
(self.tcx.lang_items().async_fn_trait(), sym::async_call, true),
(self.tcx.lang_items().async_fn_mut_trait(), sym::async_call_mut, true),
(self.tcx.lang_items().async_fn_once_trait(), sym::async_call_once, false),
];
// We only want to try a call trait if it shows up in the bounds
// of the opaque. We confirm the first one that shows up in the
// bounds list, which can lead to inference weirdness but doesn't
// matter today.
for clause in
self.tcx.item_self_bounds(alias_ty.def_id).iter_instantiated(self.tcx, alias_ty.args)
{
let Some(poly_trait_ref) = clause.as_trait_clause() else {
continue;
};

if let Some(&(opt_trait_def_id, method_name, borrow)) =
call_traits.iter().find(|(trait_def_id, _, _)| {
trait_def_id.is_some_and(|trait_def_id| trait_def_id == poly_trait_ref.def_id())
})
&& let Some(confirmed) = self.try_overloaded_call_trait(
call_expr,
call_ty,
Some(arg_exprs),
opt_trait_def_id,
method_name,
borrow,
)
{
return Some(confirmed);
}
}

Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
use rustc_session::Session;
use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym};
use rustc_trait_selection::error_reporting::TypeErrCtxt;
use rustc_trait_selection::error_reporting::infer::sub_relations::SubRelations;
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};

use crate::coercion::DynamicCoerceMany;
Expand Down Expand Up @@ -177,14 +176,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
///
/// [`InferCtxtErrorExt::err_ctxt`]: rustc_trait_selection::error_reporting::InferCtxtErrorExt::err_ctxt
pub(crate) fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> {
let mut sub_relations = SubRelations::default();
sub_relations.add_constraints(
self,
self.fulfillment_cx.borrow_mut().pending_obligations().iter().map(|o| o.predicate),
);
TypeErrCtxt {
infcx: &self.infcx,
sub_relations: RefCell::new(sub_relations),
typeck_results: Some(self.typeck_results.borrow()),
fallback_has_occurred: self.fallback_has_occurred.get(),
normalize_fn_sig: Box::new(|fn_sig| {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1213,7 +1213,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
debug!("pick_all_method: step={:?}", step);
// skip types that are from a type error or that would require dereferencing
// a raw pointer
!step.self_ty.references_error() && !step.from_unsafe_deref
!step.self_ty.value.references_error() && !step.from_unsafe_deref
})
.find_map(|step| {
let InferOk { value: self_ty, obligations: _ } = self
Expand Down
22 changes: 1 addition & 21 deletions compiler/rustc_hir_typeck/src/writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use rustc_errors::ErrorGuaranteed;
use rustc_hir::intravisit::{self, InferKind, Visitor};
use rustc_hir::{self as hir, AmbigArg, HirId};
use rustc_infer::traits::solve::Goal;
use rustc_middle::span_bug;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion};
use rustc_middle::ty::{
Expand Down Expand Up @@ -513,15 +512,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
self.typeck_results.user_provided_types_mut().extend(
fcx_typeck_results.user_provided_types().items().map(|(local_id, c_ty)| {
let hir_id = HirId { owner: common_hir_owner, local_id };

if cfg!(debug_assertions) && c_ty.has_infer() {
span_bug!(
hir_id.to_span(self.fcx.tcx),
"writeback: `{:?}` has inference variables",
c_ty
);
};

(hir_id, *c_ty)
}),
);
Expand All @@ -532,17 +522,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);

self.typeck_results.user_provided_sigs.extend_unord(
fcx_typeck_results.user_provided_sigs.items().map(|(&def_id, c_sig)| {
if cfg!(debug_assertions) && c_sig.has_infer() {
span_bug!(
self.fcx.tcx.def_span(def_id),
"writeback: `{:?}` has inference variables",
c_sig
);
};

(def_id, *c_sig)
}),
fcx_typeck_results.user_provided_sigs.items().map(|(def_id, c_sig)| (*def_id, *c_sig)),
);
}

Expand Down
31 changes: 17 additions & 14 deletions compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ use tracing::debug;

use crate::infer::InferCtxt;
use crate::infer::canonical::{
Canonical, CanonicalQueryInput, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind,
OriginalQueryValues,
Canonical, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarKind, OriginalQueryValues,
};

impl<'tcx> InferCtxt<'tcx> {
Expand Down Expand Up @@ -299,6 +298,7 @@ struct Canonicalizer<'cx, 'tcx> {
// Note that indices is only used once `var_values` is big enough to be
// heap-allocated.
indices: FxHashMap<GenericArg<'tcx>, BoundVar>,
sub_root_lookup_table: FxHashMap<ty::TyVid, usize>,
canonicalize_mode: &'cx dyn CanonicalizeMode,
needs_canonical_flags: TypeFlags,

Expand Down Expand Up @@ -367,9 +367,10 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
// FIXME: perf problem described in #55921.
ui = ty::UniverseIndex::ROOT;
}
let sub_root = self.get_or_insert_sub_root(vid);
self.canonicalize_ty_var(
CanonicalVarInfo {
kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
kind: CanonicalVarKind::Ty { universe: ui, sub_root },
},
t,
)
Expand All @@ -382,21 +383,15 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
if nt != t {
return self.fold_ty(nt);
} else {
self.canonicalize_ty_var(
CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) },
t,
)
self.canonicalize_ty_var(CanonicalVarInfo { kind: CanonicalVarKind::Int }, t)
}
}
ty::Infer(ty::FloatVar(vid)) => {
let nt = self.infcx.unwrap().opportunistic_resolve_float_var(vid);
if nt != t {
return self.fold_ty(nt);
} else {
self.canonicalize_ty_var(
CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) },
t,
)
self.canonicalize_ty_var(CanonicalVarInfo { kind: CanonicalVarKind::Float }, t)
}
}

Expand Down Expand Up @@ -576,6 +571,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
variables: SmallVec::from_slice(base.variables),
query_state,
indices: FxHashMap::default(),
sub_root_lookup_table: Default::default(),
binder_index: ty::INNERMOST,
};
if canonicalizer.query_state.var_values.spilled() {
Expand Down Expand Up @@ -670,6 +666,13 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
}
}

fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar {
let root_vid = self.infcx.unwrap().sub_root_var(vid);
let idx =
*self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len());
ty::BoundVar::from(idx)
}

/// Replaces the universe indexes used in `var_values` with their index in
/// `query_state.universe_map`. This minimizes the maximum universe used in
/// the canonicalized value.
Expand All @@ -690,11 +693,11 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
.iter()
.map(|v| CanonicalVarInfo {
kind: match v.kind {
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
CanonicalVarKind::Int | CanonicalVarKind::Float => {
return *v;
}
CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u]))
CanonicalVarKind::Ty { universe, sub_root } => {
CanonicalVarKind::Ty { universe: reverse_universe_map[&universe], sub_root }
}
CanonicalVarKind::Region(u) => {
CanonicalVarKind::Region(reverse_universe_map[&u])
Expand Down
Loading
Loading