use super::method::MethodCallee;
-use super::{FnCtxt, Needs, PlaceOp};
+use super::{FnCtxt, PlaceOp};
use rustc_errors::struct_span_err;
use rustc_hir as hir;
}
/// Returns the adjustment steps.
- pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'tcx>, needs: Needs) -> Vec<Adjustment<'tcx>> {
- fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx, needs))
+ pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'tcx>) -> Vec<Adjustment<'tcx>> {
+ fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx))
}
pub fn adjust_steps_as_infer_ok(
&self,
fcx: &FnCtxt<'a, 'tcx>,
- needs: Needs,
) -> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
let mut obligations = vec![];
let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(self.cur_ty));
.iter()
.map(|&(source, kind)| {
if let AutoderefKind::Overloaded = kind {
- fcx.try_overloaded_deref(self.span, source, needs).and_then(
+ fcx.try_overloaded_deref(self.span, source).and_then(
|InferOk { value: method, obligations: o }| {
obligations.extend(o);
if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
&self,
span: Span,
base_ty: Ty<'tcx>,
- needs: Needs,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
- self.try_overloaded_place_op(span, base_ty, &[], needs, PlaceOp::Deref)
+ self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref)
}
}
use super::autoderef::Autoderef;
use super::method::MethodCallee;
-use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag};
+use super::{Expectation, FnCtxt, TupleArgumentsFlag};
use crate::type_error_struct;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
// If the callee is a bare function or a closure, then we're all set.
match adjusted_ty.kind {
ty::FnDef(..) | ty::FnPtr(_) => {
- let adjustments = autoderef.adjust_steps(self, Needs::None);
+ let adjustments = autoderef.adjust_steps(self);
self.apply_adjustments(callee_expr, adjustments);
return Some(CallStep::Builtin(adjusted_ty));
}
&closure_sig,
)
.0;
- let adjustments = autoderef.adjust_steps(self, Needs::None);
+ let adjustments = autoderef.adjust_steps(self);
self.record_deferred_call_resolution(
def_id,
DeferredCallResolution {
self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs))
.or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None))
.map(|(autoref, method)| {
- let mut adjustments = autoderef.adjust_steps(self, Needs::None);
+ let mut adjustments = autoderef.adjust_steps(self);
adjustments.extend(autoref);
self.apply_adjustments(callee_expr, adjustments);
CallStep::Overloaded(method)
//! we may want to adjust precisely when coercions occur.
use crate::astconv::AstConv;
-use crate::check::{FnCtxt, Needs};
+use crate::check::FnCtxt;
use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
return success(vec![], ty, obligations);
}
- let needs = Needs::maybe_mut_place(mutbl_b);
let InferOk { value: mut adjustments, obligations: o } =
- autoderef.adjust_steps_as_infer_ok(self, needs);
+ autoderef.adjust_steps_as_infer_ok(self);
obligations.extend(o);
obligations.extend(autoderef.into_obligations());
use rustc_infer::infer;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::ty;
-use rustc_middle::ty::adjustment::{
- Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
-};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
use rustc_middle::ty::Ty;
use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::{AdtKind, Visibility};
self.check_expr_with_expectation(expr, ExpectHasType(expected))
}
- pub(super) fn check_expr_with_expectation(
+ fn check_expr_with_expectation_and_needs(
&self,
expr: &'tcx hir::Expr<'tcx>,
expected: Expectation<'tcx>,
+ needs: Needs,
) -> Ty<'tcx> {
- self.check_expr_with_expectation_and_needs(expr, expected, Needs::None)
+ let ty = self.check_expr_with_expectation(expr, expected);
+
+ // If the expression is used in a place whether mutable place is required
+ // e.g. LHS of assignment, perform the conversion.
+ if let Needs::MutPlace = needs {
+ self.convert_place_derefs_to_mutable(expr);
+ }
+
+ ty
}
pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> {
/// Note that inspecting a type's structure *directly* may expose the fact
/// that there are actually multiple representations for `Error`, so avoid
/// that when err needs to be handled differently.
- fn check_expr_with_expectation_and_needs(
+ pub(super) fn check_expr_with_expectation(
&self,
expr: &'tcx hir::Expr<'tcx>,
expected: Expectation<'tcx>,
- needs: Needs,
) -> Ty<'tcx> {
debug!(">> type-checking: expr={:?} expected={:?}", expr, expected);
let old_diverges = self.diverges.replace(Diverges::Maybe);
let old_has_errors = self.has_errors.replace(false);
- let ty = self.check_expr_kind(expr, expected, needs);
+ let ty = self.check_expr_kind(expr, expected);
// Warn for non-block expressions with diverging children.
match expr.kind {
&self,
expr: &'tcx hir::Expr<'tcx>,
expected: Expectation<'tcx>,
- needs: Needs,
) -> Ty<'tcx> {
- debug!("check_expr_kind(expr={:?}, expected={:?}, needs={:?})", expr, expected, needs,);
+ debug!("check_expr_kind(expr={:?}, expected={:?})", expr, expected);
let tcx = self.tcx;
match expr.kind {
self.check_expr_assign(expr, expected, lhs, rhs, span)
}
ExprKind::AssignOp(op, ref lhs, ref rhs) => self.check_binop_assign(expr, op, lhs, rhs),
- ExprKind::Unary(unop, ref oprnd) => {
- self.check_expr_unary(unop, oprnd, expected, needs, expr)
- }
+ ExprKind::Unary(unop, ref oprnd) => self.check_expr_unary(unop, oprnd, expected, expr),
ExprKind::AddrOf(kind, mutbl, ref oprnd) => {
self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr)
}
ExprKind::Block(ref body, _) => self.check_block_with_expected(&body, expected),
ExprKind::Call(ref callee, ref args) => self.check_call(expr, &callee, args, expected),
ExprKind::MethodCall(ref segment, span, ref args, _) => {
- self.check_method_call(expr, segment, span, args, expected, needs)
+ self.check_method_call(expr, segment, span, args, expected)
}
ExprKind::Cast(ref e, ref t) => self.check_expr_cast(e, t, expr),
ExprKind::Type(ref e, ref t) => {
ExprKind::Struct(ref qpath, fields, ref base_expr) => {
self.check_expr_struct(expr, expected, qpath, fields, base_expr)
}
- ExprKind::Field(ref base, field) => self.check_field(expr, needs, &base, field),
- ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, needs, expr),
+ ExprKind::Field(ref base, field) => self.check_field(expr, &base, field),
+ ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, expr),
ExprKind::Yield(ref value, ref src) => self.check_expr_yield(value, expr, src),
hir::ExprKind::Err => tcx.types.err,
}
unop: hir::UnOp,
oprnd: &'tcx hir::Expr<'tcx>,
expected: Expectation<'tcx>,
- needs: Needs,
expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx;
hir::UnOp::UnNot | hir::UnOp::UnNeg => expected,
hir::UnOp::UnDeref => NoExpectation,
};
- let needs = match unop {
- hir::UnOp::UnDeref => needs,
- _ => Needs::None,
- };
- let mut oprnd_t = self.check_expr_with_expectation_and_needs(&oprnd, expected_inner, needs);
+ let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner);
if !oprnd_t.references_error() {
oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
match unop {
hir::UnOp::UnDeref => {
- if let Some(mt) = oprnd_t.builtin_deref(true) {
- oprnd_t = mt.ty;
- } else if let Some(ok) = self.try_overloaded_deref(expr.span, oprnd_t, needs) {
- let method = self.register_infer_ok_obligations(ok);
- if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind {
- let mutbl = match mutbl {
- hir::Mutability::Not => AutoBorrowMutability::Not,
- hir::Mutability::Mut => AutoBorrowMutability::Mut {
- // (It shouldn't actually matter for unary ops whether
- // we enable two-phase borrows or not, since a unary
- // op has no additional operands.)
- allow_two_phase_borrow: AllowTwoPhase::No,
- },
- };
- self.apply_adjustments(
- oprnd,
- vec![Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
- target: method.sig.inputs()[0],
- }],
- );
- }
- oprnd_t = self.make_overloaded_place_return_type(method).ty;
- self.write_method_call(expr.hir_id, method);
+ if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) {
+ oprnd_t = ty;
} else {
let mut err = type_error_struct!(
tcx.sess,
_ => NoExpectation,
}
});
- let needs = Needs::maybe_mut_place(mutbl);
- let ty = self.check_expr_with_expectation_and_needs(&oprnd, hint, needs);
+ let ty =
+ self.check_expr_with_expectation_and_needs(&oprnd, hint, Needs::maybe_mut_place(mutbl));
let tm = ty::TypeAndMut { ty, mutbl };
match kind {
span: Span,
args: &'tcx [hir::Expr<'tcx>],
expected: Expectation<'tcx>,
- needs: Needs,
) -> Ty<'tcx> {
let rcvr = &args[0];
- let rcvr_t = self.check_expr_with_needs(&rcvr, needs);
+ let rcvr_t = self.check_expr(&rcvr);
// no need to check for bot/err -- callee does that
let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t);
fn check_field(
&self,
expr: &'tcx hir::Expr<'tcx>,
- needs: Needs,
base: &'tcx hir::Expr<'tcx>,
field: Ident,
) -> Ty<'tcx> {
- let expr_t = self.check_expr_with_needs(base, needs);
+ let expr_t = self.check_expr(base);
let expr_t = self.structurally_resolved_type(base.span, expr_t);
let mut private_candidate = None;
let mut autoderef = self.autoderef(expr.span, expr_t);
// of error recovery.
self.write_field_index(expr.hir_id, index);
if field.vis.is_accessible_from(def_scope, self.tcx) {
- let adjustments = autoderef.adjust_steps(self, needs);
+ let adjustments = autoderef.adjust_steps(self);
self.apply_adjustments(base, adjustments);
autoderef.finalize(self);
if let Ok(index) = fstr.parse::<usize>() {
if fstr == index.to_string() {
if let Some(field_ty) = tys.get(index) {
- let adjustments = autoderef.adjust_steps(self, needs);
+ let adjustments = autoderef.adjust_steps(self);
self.apply_adjustments(base, adjustments);
autoderef.finalize(self);
&self,
base: &'tcx hir::Expr<'tcx>,
idx: &'tcx hir::Expr<'tcx>,
- needs: Needs,
expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
- let base_t = self.check_expr_with_needs(&base, needs);
+ let base_t = self.check_expr(&base);
let idx_t = self.check_expr(&idx);
if base_t.references_error() {
idx_t
} else {
let base_t = self.structurally_resolved_type(base.span, base_t);
- match self.lookup_indexing(expr, base, base_t, idx_t, needs) {
+ match self.lookup_indexing(expr, base, base_t, idx_t) {
Some((index_ty, element_ty)) => {
// two-phase not needed because index_ty is never mutable
self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
use super::{probe, MethodCallee};
use crate::astconv::AstConv;
-use crate::check::{callee, FnCtxt, Needs};
+use crate::check::{callee, FnCtxt};
use crate::hir::def_id::DefId;
use crate::hir::GenericArg;
use rustc_hir as hir;
};
assert_eq!(n, pick.autoderefs);
- let mut adjustments = autoderef.adjust_steps(self, Needs::None);
+ let mut adjustments = autoderef.adjust_steps(self);
let mut target = autoderef.unambiguous_final_ty(self);
pub mod method;
mod op;
mod pat;
-mod reconciliation;
+mod place_op;
mod regionck;
mod upvar;
mod wfcheck;
use rustc_middle::hir::map::blocks::FnLikeNode;
use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::ty::adjustment::{
- Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
+ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
};
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
use rustc_middle::ty::query::Providers;
use crate::require_c_abi_if_c_variadic;
use crate::util::common::indenter;
-use self::autoderef::Autoderef;
use self::callee::DeferredCallResolution;
use self::coercion::{CoerceMany, DynamicCoerceMany};
use self::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl};
ret_ty.builtin_deref(true).unwrap()
}
- fn lookup_indexing(
- &self,
- expr: &hir::Expr<'_>,
- base_expr: &'tcx hir::Expr<'tcx>,
- base_ty: Ty<'tcx>,
- idx_ty: Ty<'tcx>,
- needs: Needs,
- ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
- // FIXME(#18741) -- this is almost but not quite the same as the
- // autoderef that normal method probing does. They could likely be
- // consolidated.
-
- let mut autoderef = self.autoderef(base_expr.span, base_ty);
- let mut result = None;
- while result.is_none() && autoderef.next().is_some() {
- result = self.try_index_step(expr, base_expr, &autoderef, needs, idx_ty);
- }
- autoderef.finalize(self);
- result
- }
-
- /// To type-check `base_expr[index_expr]`, we progressively autoderef
- /// (and otherwise adjust) `base_expr`, looking for a type which either
- /// supports builtin indexing or overloaded indexing.
- /// This loop implements one step in that search; the autoderef loop
- /// is implemented by `lookup_indexing`.
- fn try_index_step(
- &self,
- expr: &hir::Expr<'_>,
- base_expr: &hir::Expr<'_>,
- autoderef: &Autoderef<'a, 'tcx>,
- needs: Needs,
- index_ty: Ty<'tcx>,
- ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
- let adjusted_ty = autoderef.unambiguous_final_ty(self);
- debug!(
- "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
- index_ty={:?})",
- expr, base_expr, adjusted_ty, index_ty
- );
-
- for &unsize in &[false, true] {
- let mut self_ty = adjusted_ty;
- if unsize {
- // We only unsize arrays here.
- if let ty::Array(element_ty, _) = adjusted_ty.kind {
- self_ty = self.tcx.mk_slice(element_ty);
- } else {
- continue;
- }
- }
-
- // If some lookup succeeds, write callee into table and extract index/element
- // type from the method signature.
- // If some lookup succeeded, install method in table
- let input_ty = self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::AutoDeref,
- span: base_expr.span,
- });
- let method = self.try_overloaded_place_op(
- expr.span,
- self_ty,
- &[input_ty],
- needs,
- PlaceOp::Index,
- );
-
- let result = method.map(|ok| {
- debug!("try_index_step: success, using overloaded indexing");
- let method = self.register_infer_ok_obligations(ok);
-
- let mut adjustments = autoderef.adjust_steps(self, needs);
- if let ty::Ref(region, _, r_mutbl) = method.sig.inputs()[0].kind {
- let mutbl = match r_mutbl {
- hir::Mutability::Not => AutoBorrowMutability::Not,
- hir::Mutability::Mut => AutoBorrowMutability::Mut {
- // Indexing can be desugared to a method call,
- // so maybe we could use two-phase here.
- // See the documentation of AllowTwoPhase for why that's
- // not the case today.
- allow_two_phase_borrow: AllowTwoPhase::No,
- },
- };
- adjustments.push(Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
- target: self
- .tcx
- .mk_ref(region, ty::TypeAndMut { mutbl: r_mutbl, ty: adjusted_ty }),
- });
- }
- if unsize {
- adjustments.push(Adjustment {
- kind: Adjust::Pointer(PointerCast::Unsize),
- target: method.sig.inputs()[0],
- });
- }
- self.apply_adjustments(base_expr, adjustments);
-
- self.write_method_call(expr.hir_id, method);
- (input_ty, self.make_overloaded_place_return_type(method).ty)
- });
- if result.is_some() {
- return result;
- }
- }
-
- None
- }
-
- fn resolve_place_op(&self, op: PlaceOp, is_mut: bool) -> (Option<DefId>, Ident) {
- let (tr, name) = match (op, is_mut) {
- (PlaceOp::Deref, false) => (self.tcx.lang_items().deref_trait(), sym::deref),
- (PlaceOp::Deref, true) => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
- (PlaceOp::Index, false) => (self.tcx.lang_items().index_trait(), sym::index),
- (PlaceOp::Index, true) => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
- };
- (tr, Ident::with_dummy_span(name))
- }
-
- fn try_overloaded_place_op(
- &self,
- span: Span,
- base_ty: Ty<'tcx>,
- arg_tys: &[Ty<'tcx>],
- needs: Needs,
- op: PlaceOp,
- ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
- debug!("try_overloaded_place_op({:?},{:?},{:?},{:?})", span, base_ty, needs, op);
-
- // Try Mut first, if needed.
- let (mut_tr, mut_op) = self.resolve_place_op(op, true);
- let method = match (needs, mut_tr) {
- (Needs::MutPlace, Some(trait_did)) => {
- self.lookup_method_in_trait(span, mut_op, trait_did, base_ty, Some(arg_tys))
- }
- _ => None,
- };
-
- // Otherwise, fall back to the immutable version.
- let (imm_tr, imm_op) = self.resolve_place_op(op, false);
- match (method, imm_tr) {
- (None, Some(trait_did)) => {
- self.lookup_method_in_trait(span, imm_op, trait_did, base_ty, Some(arg_tys))
- }
- (method, _) => method,
- }
- }
-
fn check_method_argument_types(
&self,
sp: Span,
//! Code related to processing overloaded binary and unary operators.
use super::method::MethodCallee;
-use super::{FnCtxt, Needs};
+use super::FnCtxt;
use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
// trait matching creating lifetime constraints that are too strict.
// e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
// in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
- let lhs_ty = self.check_expr_with_needs(lhs_expr, Needs::None);
+ let lhs_ty = self.check_expr(lhs_expr);
let fresh_var = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
span: lhs_expr.span,
// equivalence on the LHS of an assign-op like `+=`;
// overwritten or mutably-borrowed places cannot be
// coerced to a supertype.
- self.check_expr_with_needs(lhs_expr, Needs::MutPlace)
+ self.check_expr(lhs_expr)
}
};
let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
--- /dev/null
+use crate::check::autoderef::Autoderef;
+use crate::check::method::MethodCallee;
+use crate::check::{FnCtxt, PlaceOp};
+use rustc_hir as hir;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::InferOk;
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
+use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
+use rustc_middle::ty::{self, Ty};
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::Span;
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ pub(super) fn lookup_derefing(
+ &self,
+ expr: &hir::Expr<'_>,
+ oprnd: &'tcx hir::Expr<'tcx>,
+ oprnd_ty: Ty<'tcx>,
+ ) -> Option<Ty<'tcx>> {
+ if let Some(mt) = oprnd_ty.builtin_deref(true) {
+ return Some(mt.ty);
+ }
+
+ let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?;
+ let method = self.register_infer_ok_obligations(ok);
+ if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
+ self.apply_adjustments(
+ oprnd,
+ vec![Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
+ target: method.sig.inputs()[0],
+ }],
+ );
+ } else {
+ span_bug!(expr.span, "input to deref is not a ref?");
+ }
+ let ty = self.make_overloaded_place_return_type(method).ty;
+ self.write_method_call(expr.hir_id, method);
+ Some(ty)
+ }
+
+ pub(super) fn lookup_indexing(
+ &self,
+ expr: &hir::Expr<'_>,
+ base_expr: &'tcx hir::Expr<'tcx>,
+ base_ty: Ty<'tcx>,
+ idx_ty: Ty<'tcx>,
+ ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
+ // FIXME(#18741) -- this is almost but not quite the same as the
+ // autoderef that normal method probing does. They could likely be
+ // consolidated.
+
+ let mut autoderef = self.autoderef(base_expr.span, base_ty);
+ let mut result = None;
+ while result.is_none() && autoderef.next().is_some() {
+ result = self.try_index_step(expr, base_expr, &autoderef, idx_ty);
+ }
+ autoderef.finalize(self);
+ result
+ }
+
+ /// To type-check `base_expr[index_expr]`, we progressively autoderef
+ /// (and otherwise adjust) `base_expr`, looking for a type which either
+ /// supports builtin indexing or overloaded indexing.
+ /// This loop implements one step in that search; the autoderef loop
+ /// is implemented by `lookup_indexing`.
+ fn try_index_step(
+ &self,
+ expr: &hir::Expr<'_>,
+ base_expr: &hir::Expr<'_>,
+ autoderef: &Autoderef<'a, 'tcx>,
+ index_ty: Ty<'tcx>,
+ ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
+ let adjusted_ty = autoderef.unambiguous_final_ty(self);
+ debug!(
+ "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
+ index_ty={:?})",
+ expr, base_expr, adjusted_ty, index_ty
+ );
+
+ for &unsize in &[false, true] {
+ let mut self_ty = adjusted_ty;
+ if unsize {
+ // We only unsize arrays here.
+ if let ty::Array(element_ty, _) = adjusted_ty.kind {
+ self_ty = self.tcx.mk_slice(element_ty);
+ } else {
+ continue;
+ }
+ }
+
+ // If some lookup succeeds, write callee into table and extract index/element
+ // type from the method signature.
+ // If some lookup succeeded, install method in table
+ let input_ty = self.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::AutoDeref,
+ span: base_expr.span,
+ });
+ let method =
+ self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index);
+
+ let result = method.map(|ok| {
+ debug!("try_index_step: success, using overloaded indexing");
+ let method = self.register_infer_ok_obligations(ok);
+
+ let mut adjustments = autoderef.adjust_steps(self);
+ if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
+ adjustments.push(Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
+ target: self.tcx.mk_ref(
+ region,
+ ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty },
+ ),
+ });
+ } else {
+ span_bug!(expr.span, "input to index is not a ref?");
+ }
+ if unsize {
+ adjustments.push(Adjustment {
+ kind: Adjust::Pointer(PointerCast::Unsize),
+ target: method.sig.inputs()[0],
+ });
+ }
+ self.apply_adjustments(base_expr, adjustments);
+
+ self.write_method_call(expr.hir_id, method);
+ (input_ty, self.make_overloaded_place_return_type(method).ty)
+ });
+ if result.is_some() {
+ return result;
+ }
+ }
+
+ None
+ }
+
+ /// Try to resolve an overloaded place op. We only deal with the immutable
+ /// variant here (Deref/Index). In some contexts we would need the mutable
+ /// variant (DerefMut/IndexMut); those would be later converted by
+ /// `convert_place_derefs_to_mutable`.
+ pub(super) fn try_overloaded_place_op(
+ &self,
+ span: Span,
+ base_ty: Ty<'tcx>,
+ arg_tys: &[Ty<'tcx>],
+ op: PlaceOp,
+ ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
+ debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
+
+ let (imm_tr, imm_op) = match op {
+ PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref),
+ PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index),
+ };
+ imm_tr.and_then(|trait_did| {
+ self.lookup_method_in_trait(
+ span,
+ Ident::with_dummy_span(imm_op),
+ trait_did,
+ base_ty,
+ Some(arg_tys),
+ )
+ })
+ }
+
+ fn try_mutable_overloaded_place_op(
+ &self,
+ span: Span,
+ base_ty: Ty<'tcx>,
+ arg_tys: &[Ty<'tcx>],
+ op: PlaceOp,
+ ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
+ debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
+
+ let (mut_tr, mut_op) = match op {
+ PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
+ PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
+ };
+ mut_tr.and_then(|trait_did| {
+ self.lookup_method_in_trait(
+ span,
+ Ident::with_dummy_span(mut_op),
+ trait_did,
+ base_ty,
+ Some(arg_tys),
+ )
+ })
+ }
+
+ /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
+ /// into `DerefMut` and `IndexMut` respectively.
+ ///
+ /// This is a second pass of typechecking derefs/indices. We need this we do not
+ /// always know whether a place needs to be mutable or not in the first pass.
+ /// This happens whether there is an implicit mutable reborrow, e.g. when the type
+ /// is used as the receiver of a method call.
+ pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) {
+ // Gather up expressions we want to munge.
+ let mut exprs = vec![expr];
+
+ loop {
+ match exprs.last().unwrap().kind {
+ hir::ExprKind::Field(ref expr, _)
+ | hir::ExprKind::Index(ref expr, _)
+ | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
+ _ => break,
+ }
+ }
+
+ debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
+
+ // Fix up autoderefs and derefs.
+ for (i, &expr) in exprs.iter().rev().enumerate() {
+ debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
+
+ // Fix up the autoderefs. Autorefs can only occur immediately preceding
+ // overloaded place ops, and will be fixed by them in order to get
+ // the correct region.
+ let mut source = self.node_ty(expr.hir_id);
+ // Do not mutate adjustments in place, but rather take them,
+ // and replace them after mutating them, to avoid having the
+ // tables borrowed during (`deref_mut`) method resolution.
+ let previous_adjustments =
+ self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id);
+ if let Some(mut adjustments) = previous_adjustments {
+ for adjustment in &mut adjustments {
+ if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
+ if let Some(ok) = self.try_mutable_overloaded_place_op(
+ expr.span,
+ source,
+ &[],
+ PlaceOp::Deref,
+ ) {
+ let method = self.register_infer_ok_obligations(ok);
+ if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
+ *deref = OverloadedDeref { region, mutbl };
+ }
+ }
+ }
+ source = adjustment.target;
+ }
+ self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
+ }
+
+ match expr.kind {
+ hir::ExprKind::Index(ref base_expr, ref index_expr) => {
+ // We need to get the final type in case dereferences were needed for the trait
+ // to apply (#72002).
+ let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr);
+ self.convert_place_op_to_mutable(
+ PlaceOp::Index,
+ expr,
+ base_expr,
+ &[index_expr_ty],
+ );
+ }
+ hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
+ self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
+ }
+ _ => {}
+ }
+ }
+ }
+
+ fn convert_place_op_to_mutable(
+ &self,
+ op: PlaceOp,
+ expr: &hir::Expr<'_>,
+ base_expr: &hir::Expr<'_>,
+ arg_tys: &[Ty<'tcx>],
+ ) {
+ debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
+ if !self.tables.borrow().is_method_call(expr) {
+ debug!("convert_place_op_to_mutable - builtin, nothing to do");
+ return;
+ }
+
+ // Need to deref because overloaded place ops take self by-reference.
+ let base_ty = self
+ .tables
+ .borrow()
+ .expr_ty_adjusted(base_expr)
+ .builtin_deref(false)
+ .expect("place op takes something that is not a ref")
+ .ty;
+
+ let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, 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
+ // current, immutable version.
+ None => return,
+ };
+ debug!("convert_place_op_to_mutable: method={:?}", method);
+ self.write_method_call(expr.hir_id, method);
+
+ let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind {
+ r
+ } else {
+ span_bug!(expr.span, "input to mutable place op is not a mut ref?");
+ };
+
+ // Convert the autoref in the base expr to mutable with the correct
+ // region and mutability.
+ let base_expr_ty = self.node_ty(base_expr.hir_id);
+ if let Some(adjustments) =
+ self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
+ {
+ let mut source = base_expr_ty;
+ for adjustment in &mut adjustments[..] {
+ if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
+ debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
+ let mutbl = AutoBorrowMutability::Mut {
+ // Deref/indexing can be desugared to a method call,
+ // so maybe we could use two-phase here.
+ // See the documentation of AllowTwoPhase for why that's
+ // not the case today.
+ allow_two_phase_borrow: AllowTwoPhase::No,
+ };
+ adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
+ adjustment.target =
+ self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
+ }
+ source = adjustment.target;
+ }
+
+ // If we have an autoref followed by unsizing at the end, fix the unsize target.
+ if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
+ adjustments[..]
+ {
+ *target = method.sig.inputs()[0];
+ }
+ }
+ }
+}
+++ /dev/null
-use crate::check::{FnCtxt, Needs, PlaceOp};
-use rustc_hir as hir;
-use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
-use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
-use rustc_middle::ty::{self, Ty};
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
- /// into `DerefMut` and `IndexMut` respectively.
- ///
- /// This is a second pass of typechecking derefs/indices. We need this we do not
- /// always know whether a place needs to be mutable or not in the first pass.
- /// This happens whether there is an implicit mutable reborrow, e.g. when the type
- /// is used as the receiver of a method call.
- pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) {
- // Gather up expressions we want to munge.
- let mut exprs = vec![expr];
-
- loop {
- match exprs.last().unwrap().kind {
- hir::ExprKind::Field(ref expr, _)
- | hir::ExprKind::Index(ref expr, _)
- | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
- _ => break,
- }
- }
-
- debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
-
- // Fix up autoderefs and derefs.
- for (i, &expr) in exprs.iter().rev().enumerate() {
- debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
-
- // Fix up the autoderefs. Autorefs can only occur immediately preceding
- // overloaded place ops, and will be fixed by them in order to get
- // the correct region.
- let mut source = self.node_ty(expr.hir_id);
- // Do not mutate adjustments in place, but rather take them,
- // and replace them after mutating them, to avoid having the
- // tables borrowed during (`deref_mut`) method resolution.
- let previous_adjustments =
- self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id);
- if let Some(mut adjustments) = previous_adjustments {
- let needs = Needs::MutPlace;
- for adjustment in &mut adjustments {
- if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
- if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) {
- let method = self.register_infer_ok_obligations(ok);
- if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
- *deref = OverloadedDeref { region, mutbl };
- }
- }
- }
- source = adjustment.target;
- }
- self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
- }
-
- match expr.kind {
- hir::ExprKind::Index(ref base_expr, ref index_expr) => {
- // We need to get the final type in case dereferences were needed for the trait
- // to apply (#72002).
- let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr);
- self.convert_place_op_to_mutable(
- PlaceOp::Index,
- expr,
- base_expr,
- &[index_expr_ty],
- );
- }
- hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
- self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
- }
- _ => {}
- }
- }
- }
-
- fn convert_place_op_to_mutable(
- &self,
- op: PlaceOp,
- expr: &hir::Expr<'_>,
- base_expr: &hir::Expr<'_>,
- arg_tys: &[Ty<'tcx>],
- ) {
- debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
- if !self.tables.borrow().is_method_call(expr) {
- debug!("convert_place_op_to_mutable - builtin, nothing to do");
- return;
- }
-
- // Need to deref because overloaded place ops take self by-reference.
- let base_ty = self
- .tables
- .borrow()
- .expr_ty_adjusted(base_expr)
- .builtin_deref(false)
- .expect("place op takes something that is not a ref")
- .ty;
-
- let method = self.try_overloaded_place_op(expr.span, base_ty, arg_tys, Needs::MutPlace, op);
- let method = match method {
- Some(ok) => self.register_infer_ok_obligations(ok),
- None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed"),
- };
- debug!("convert_place_op_to_mutable: method={:?}", method);
- self.write_method_call(expr.hir_id, method);
-
- let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind {
- (r, mutbl)
- } else {
- span_bug!(expr.span, "input to place op is not a ref?");
- };
-
- // Convert the autoref in the base expr to mutable with the correct
- // region and mutability.
- let base_expr_ty = self.node_ty(base_expr.hir_id);
- if let Some(adjustments) =
- self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
- {
- let mut source = base_expr_ty;
- for adjustment in &mut adjustments[..] {
- if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
- debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
- let mutbl = match mutbl {
- hir::Mutability::Not => AutoBorrowMutability::Not,
- hir::Mutability::Mut => AutoBorrowMutability::Mut {
- // For initial two-phase borrow
- // deployment, conservatively omit
- // overloaded operators.
- allow_two_phase_borrow: AllowTwoPhase::No,
- },
- };
- adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
- adjustment.target =
- self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
- }
- source = adjustment.target;
- }
-
- // If we have an autoref followed by unsizing at the end, fix the unsize target.
-
- if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
- adjustments[..]
- {
- *target = method.sig.inputs()[0];
- }
- }
- }
-}