Skip to content
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

Cleanup op lookup in HIR typeck #132274

Merged
merged 3 commits into from
Oct 29, 2024
Merged
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
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
base_ty: Ty<'tcx>,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref)
self.try_overloaded_place_op(span, base_ty, None, PlaceOp::Deref)
}

/// Returns the adjustment steps.
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{iter, slice};
use std::iter;

use rustc_ast::util::parser::PREC_UNAMBIGUOUS;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
Expand Down Expand Up @@ -300,7 +300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ident::with_dummy_span(method_name),
trait_def_id,
adjusted_ty,
opt_input_type.as_ref().map(slice::from_ref),
opt_input_type,
) {
let method = self.register_infer_ok_obligations(ok);
let mut autoref = None;
Expand Down
91 changes: 31 additions & 60 deletions compiler/rustc_hir_typeck/src/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,35 +324,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ok(pick)
}

pub(super) fn obligation_for_method(
&self,
cause: ObligationCause<'tcx>,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
opt_input_types: Option<&[Ty<'tcx>]>,
) -> (traits::PredicateObligation<'tcx>, ty::GenericArgsRef<'tcx>) {
// Construct a trait-reference `self_ty : Trait<input_tys>`
let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| {
match param.kind {
GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {}
GenericParamDefKind::Type { .. } => {
if param.index == 0 {
return self_ty.into();
} else if let Some(input_types) = opt_input_types {
return input_types[param.index as usize - 1].into();
}
}
}
self.var_for_def(cause.span, param)
});

let trait_ref = ty::TraitRef::new_from_args(self.tcx, trait_def_id, args);

// Construct an obligation
let poly_trait_ref = ty::Binder::dummy(trait_ref);
(traits::Obligation::new(self.tcx, cause, self.param_env, poly_trait_ref), args)
}

/// `lookup_method_in_trait` is used for overloaded operators.
/// It does a very narrow slice of what the normal probe/confirm path does.
/// In particular, it doesn't really do any probing: it simply constructs
Expand All @@ -365,24 +336,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
m_name: Ident,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
opt_input_types: Option<&[Ty<'tcx>]>,
opt_rhs_ty: Option<Ty<'tcx>>,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
let (obligation, args) =
self.obligation_for_method(cause, trait_def_id, self_ty, opt_input_types);
self.construct_obligation_for_trait(m_name, trait_def_id, obligation, args)
}
// Construct a trait-reference `self_ty : Trait<input_tys>`
let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| match param.kind {
GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {
unreachable!("did not expect operator trait to have lifetime/const")
}
GenericParamDefKind::Type { .. } => {
if param.index == 0 {
self_ty.into()
} else if let Some(rhs_ty) = opt_rhs_ty {
assert_eq!(param.index, 1, "did not expect >1 param on operator trait");
rhs_ty.into()
} else {
// FIXME: We should stop passing `None` for the failure case
// when probing for call exprs. I.e. `opt_rhs_ty` should always
// be set when it needs to be.
self.var_for_def(cause.span, param)
}
}
});

// FIXME(#18741): it seems likely that we can consolidate some of this
// code with the other method-lookup code. In particular, the second half
// of this method is basically the same as confirmation.
fn construct_obligation_for_trait(
&self,
m_name: Ident,
trait_def_id: DefId,
obligation: traits::PredicateObligation<'tcx>,
args: ty::GenericArgsRef<'tcx>,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
debug!(?obligation);
let obligation = traits::Obligation::new(
self.tcx,
cause,
self.param_env,
ty::TraitRef::new_from_args(self.tcx, trait_def_id, args),
);

// Now we want to know if this can be matched
if !self.predicate_may_hold(&obligation) {
Expand All @@ -406,8 +387,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
let mut obligations = PredicateObligations::new();

// FIXME(effects): revisit when binops get `#[const_trait]`

// Instantiate late-bound regions and instantiate the trait
// parameters into the method type to get the actual method type.
//
Expand All @@ -418,12 +397,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let fn_sig =
self.instantiate_binder_with_fresh_vars(obligation.cause.span, infer::FnCall, fn_sig);

let InferOk { value, obligations: o } =
let InferOk { value: fn_sig, obligations: o } =
self.at(&obligation.cause, self.param_env).normalize(fn_sig);
let fn_sig = {
obligations.extend(o);
value
};
obligations.extend(o);

// Register obligations for the parameters. This will include the
// `Self` parameter, which in turn has a bound of the main trait,
Expand All @@ -435,13 +411,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// any late-bound regions appearing in its bounds.
let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, args);

let InferOk { value, obligations: o } =
let InferOk { value: bounds, obligations: o } =
self.at(&obligation.cause, self.param_env).normalize(bounds);
let bounds = {
obligations.extend(o);
value
};

obligations.extend(o);
assert!(!bounds.has_escaping_bound_vars());

let predicates_cause = obligation.cause.clone();
Expand All @@ -454,7 +426,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Also add an obligation for the method type being well-formed.
let method_ty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(fn_sig));
debug!(
"lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}",
"lookup_method_in_trait: matched method method_ty={:?} obligation={:?}",
method_ty, obligation
);
obligations.push(traits::Obligation::new(
Expand All @@ -467,7 +439,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
));

let callee = MethodCallee { def_id, args, sig: fn_sig };

debug!("callee = {:?}", callee);

Some(InferOk { obligations, value: callee })
Expand Down
36 changes: 24 additions & 12 deletions compiler/rustc_hir_typeck/src/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rustc_span::Span;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{Ident, sym};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{FulfillmentError, ObligationCtxt};
use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt};
use rustc_type_ir::TyKind::*;
use tracing::debug;
use {rustc_ast as ast, rustc_hir as hir};
Expand Down Expand Up @@ -895,7 +895,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let opname = Ident::with_dummy_span(opname);
let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip();
let input_types = opt_rhs_ty.as_slice();
let cause = self.cause(span, ObligationCauseCode::BinOp {
lhs_hir_id: lhs_expr.hir_id,
rhs_hir_id: opt_rhs_expr.map(|expr| expr.hir_id),
Expand All @@ -904,13 +903,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
output_ty: expected.only_has_type(self),
});

let method = self.lookup_method_in_trait(
cause.clone(),
opname,
trait_did,
lhs_ty,
Some(input_types),
);
let method =
self.lookup_method_in_trait(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty);
match method {
Some(ok) => {
let method = self.register_infer_ok_obligations(ok);
Expand All @@ -931,9 +925,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_expr_coercible_to_type(rhs_expr, rhs_ty, None);
}

let (obligation, _) =
self.obligation_for_method(cause, trait_did, lhs_ty, Some(input_types));
// FIXME: This should potentially just add the obligation to the `FnCtxt`
// Construct an obligation `self_ty : Trait<input_tys>`
let args =
ty::GenericArgs::for_item(self.tcx, trait_did, |param, _| match param.kind {
ty::GenericParamDefKind::Lifetime
| ty::GenericParamDefKind::Const { .. } => {
unreachable!("did not expect operand trait to have lifetime/const args")
}
ty::GenericParamDefKind::Type { .. } => {
if param.index == 0 {
lhs_ty.into()
} else {
opt_rhs_ty.expect("expected RHS for binop").into()
}
}
});
let obligation = Obligation::new(
self.tcx,
cause,
self.param_env,
ty::TraitRef::new_from_args(self.tcx, trait_did, args),
);
let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx);
ocx.register_obligation(obligation);
Err(ocx.select_all_or_error())
Expand Down
15 changes: 7 additions & 8 deletions compiler/rustc_hir_typeck/src/place_op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If some lookup succeeded, install method in table
let input_ty = self.next_ty_var(base_expr.span);
let method =
self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index);
self.try_overloaded_place_op(expr.span, self_ty, Some(input_ty), PlaceOp::Index);

if let Some(result) = method {
debug!("try_index_step: success, using overloaded indexing");
Expand Down Expand Up @@ -189,7 +189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
span: Span,
base_ty: Ty<'tcx>,
arg_tys: &[Ty<'tcx>],
opt_rhs_ty: Option<Ty<'tcx>>,
op: PlaceOp,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
Expand All @@ -207,15 +207,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ident::with_dummy_span(imm_op),
imm_tr,
base_ty,
Some(arg_tys),
opt_rhs_ty,
)
}

fn try_mutable_overloaded_place_op(
&self,
span: Span,
base_ty: Ty<'tcx>,
arg_tys: &[Ty<'tcx>],
opt_rhs_ty: Option<Ty<'tcx>>,
op: PlaceOp,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
Expand All @@ -233,7 +233,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ident::with_dummy_span(mut_op),
mut_tr,
base_ty,
Some(arg_tys),
opt_rhs_ty,
)
}

Expand Down Expand Up @@ -284,7 +284,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& let Some(ok) = self.try_mutable_overloaded_place_op(
expr.span,
source,
&[],
None,
PlaceOp::Deref,
)
{
Expand Down Expand Up @@ -359,8 +359,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some(self.typeck_results.borrow().node_args(expr.hir_id).type_at(1))
}
};
let arg_tys = arg_ty.as_slice();
let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op);
let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_ty, op);
let method = match method {
Some(ok) => self.register_infer_ok_obligations(ok),
// Couldn't find the mutable variant of the place op, keep the
Expand Down
Loading