Skip to content

Deduplicate op methods #106173

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

Merged
merged 2 commits into from
Dec 28, 2022
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/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});

if let Some(ok) = self.lookup_method_in_trait(
call_expr.span,
self.misc(call_expr.span),
method_name,
trait_def_id,
adjusted_ty,
Expand Down
140 changes: 18 additions & 122 deletions compiler/rustc_hir_typeck/src/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub use self::suggest::SelfSource;
pub use self::MethodError::*;

use crate::errors::OpMethodGenericParams;
use crate::{Expectation, FnCtxt};
use crate::FnCtxt;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir;
Expand Down Expand Up @@ -264,7 +264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

pub(super) fn obligation_for_method(
&self,
span: Span,
cause: ObligationCause<'tcx>,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
opt_input_types: Option<&[Ty<'tcx>]>,
Expand All @@ -282,71 +282,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
self.var_for_def(span, param)
});

let trait_ref = self.tcx.mk_trait_ref(trait_def_id, substs);

// Construct an obligation
let poly_trait_ref = ty::Binder::dummy(trait_ref);
(
traits::Obligation::misc(
self.tcx,
span,
self.body_id,
self.param_env,
poly_trait_ref.without_const(),
),
substs,
)
}

pub(super) fn obligation_for_op_method(
&self,
span: Span,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
opt_input_type: Option<Ty<'tcx>>,
opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
expected: Expectation<'tcx>,
) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
{
// Construct a trait-reference `self_ty : Trait<input_tys>`
let substs = InternalSubsts::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_type) = opt_input_type {
return input_type.into();
}
}
}
self.var_for_def(span, param)
self.var_for_def(cause.span, param)
});

let trait_ref = self.tcx.mk_trait_ref(trait_def_id, substs);

// Construct an obligation
let poly_trait_ref = ty::Binder::dummy(trait_ref);
let output_ty = expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty));

(
traits::Obligation::new(
self.tcx,
traits::ObligationCause::new(
span,
self.body_id,
traits::BinOp {
rhs_span: opt_input_expr.map(|expr| expr.span),
is_lit: opt_input_expr
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
output_ty,
},
),
cause,
self.param_env,
poly_trait_ref,
poly_trait_ref.without_const(),
),
substs,
)
Expand All @@ -357,69 +305,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// In particular, it doesn't really do any probing: it simply constructs
/// an obligation for a particular trait with the given self type and checks
/// whether that trait is implemented.
#[instrument(level = "debug", skip(self, span))]
#[instrument(level = "debug", skip(self))]
pub(super) fn lookup_method_in_trait(
&self,
span: Span,
cause: ObligationCause<'tcx>,
m_name: Ident,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
opt_input_types: Option<&[Ty<'tcx>]>,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
let (obligation, substs) =
self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
self.construct_obligation_for_trait(
span,
m_name,
trait_def_id,
obligation,
substs,
None,
false,
)
}

pub(super) fn lookup_op_method_in_trait(
&self,
span: Span,
m_name: Ident,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
opt_input_type: Option<Ty<'tcx>>,
opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
expected: Expectation<'tcx>,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
let (obligation, substs) = self.obligation_for_op_method(
span,
trait_def_id,
self_ty,
opt_input_type,
opt_input_expr,
expected,
);
self.construct_obligation_for_trait(
span,
m_name,
trait_def_id,
obligation,
substs,
opt_input_expr,
true,
)
self.obligation_for_method(cause, trait_def_id, self_ty, opt_input_types);
self.construct_obligation_for_trait(m_name, trait_def_id, obligation, substs)
}

// 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,
span: Span,
m_name: Ident,
trait_def_id: DefId,
obligation: traits::PredicateObligation<'tcx>,
substs: &'tcx ty::List<ty::subst::GenericArg<'tcx>>,
opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
is_op: bool,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
debug!(?obligation);

Expand All @@ -435,7 +343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let tcx = self.tcx;
let Some(method_item) = self.associated_value(trait_def_id, m_name) else {
tcx.sess.delay_span_bug(
span,
obligation.cause.span,
"operator trait does not have corresponding operator method",
);
return None;
Expand All @@ -461,24 +369,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// with bound regions.
let fn_sig = tcx.bound_fn_sig(def_id);
let fn_sig = fn_sig.subst(self.tcx, substs);
let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig);

let cause = if is_op {
ObligationCause::new(
span,
self.body_id,
traits::BinOp {
rhs_span: opt_input_expr.map(|expr| expr.span),
is_lit: opt_input_expr
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
output_ty: None,
},
)
} else {
traits::ObligationCause::misc(span, self.body_id)
};
let fn_sig =
self.replace_bound_vars_with_fresh_vars(obligation.cause.span, infer::FnCall, fn_sig);

let InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(fn_sig);
let InferOk { value, obligations: o } =
self.at(&obligation.cause, self.param_env).normalize(fn_sig);
let fn_sig = {
obligations.extend(o);
value
Expand All @@ -494,15 +389,16 @@ 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, substs);

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

assert!(!bounds.has_escaping_bound_vars());

let predicates_cause = cause.clone();
let predicates_cause = obligation.cause.clone();
obligations.extend(traits::predicates_for_generics(
move |_, _| predicates_cause.clone(),
self.param_env,
Expand All @@ -517,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
obligations.push(traits::Obligation::new(
tcx,
cause,
obligation.cause,
self.param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())),
));
Expand Down
Loading