mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-07 12:48:30 +00:00
Auto merge of #41651 - arielb1:missing-adjustment-2, r=eddyb
refactor the handling of lvalue ops I think I got the code into a "mostly sane" situation. Fixes #41604. beta-nominating because fixes regression in #41578. I think I can do a smaller fix, but the previous code is too fragile. r? @eddyb
This commit is contained in:
commit
06fb4d2564
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
use astconv::AstConv;
|
use astconv::AstConv;
|
||||||
|
|
||||||
use super::FnCtxt;
|
use super::{FnCtxt, LvalueOp};
|
||||||
|
|
||||||
use check::coercion::AsCoercionSite;
|
use check::coercion::AsCoercionSite;
|
||||||
use rustc::infer::InferOk;
|
use rustc::infer::InferOk;
|
||||||
@ -18,7 +18,7 @@ use rustc::traits;
|
|||||||
use rustc::ty::{self, Ty, TraitRef};
|
use rustc::ty::{self, Ty, TraitRef};
|
||||||
use rustc::ty::{ToPredicate, TypeFoldable};
|
use rustc::ty::{ToPredicate, TypeFoldable};
|
||||||
use rustc::ty::{MethodCall, MethodCallee};
|
use rustc::ty::{MethodCall, MethodCallee};
|
||||||
use rustc::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
|
use rustc::ty::{LvaluePreference, NoPreference};
|
||||||
use rustc::hir;
|
use rustc::hir;
|
||||||
|
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
@ -213,39 +213,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
span: Span,
|
span: Span,
|
||||||
base_expr: Option<&hir::Expr>,
|
base_expr: Option<&hir::Expr>,
|
||||||
base_ty: Ty<'tcx>,
|
base_ty: Ty<'tcx>,
|
||||||
lvalue_pref: LvaluePreference)
|
pref: LvaluePreference)
|
||||||
-> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
-> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||||
debug!("try_overloaded_deref({:?},{:?},{:?},{:?})",
|
let rcvr = base_expr.map(|base_expr| super::AdjustedRcvr {
|
||||||
span,
|
rcvr_expr: base_expr, autoderefs: 0, unsize: false
|
||||||
base_expr,
|
});
|
||||||
base_ty,
|
|
||||||
lvalue_pref);
|
|
||||||
// Try DerefMut first, if preferred.
|
|
||||||
let method = match (lvalue_pref, self.tcx.lang_items.deref_mut_trait()) {
|
|
||||||
(PreferMutLvalue, Some(trait_did)) => {
|
|
||||||
self.lookup_method_in_trait(span,
|
|
||||||
base_expr,
|
|
||||||
Symbol::intern("deref_mut"),
|
|
||||||
trait_did,
|
|
||||||
base_ty,
|
|
||||||
None)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Otherwise, fall back to Deref.
|
self.try_overloaded_lvalue_op(span, rcvr, base_ty, &[], pref, LvalueOp::Deref)
|
||||||
let method = match (method, self.tcx.lang_items.deref_trait()) {
|
|
||||||
(None, Some(trait_did)) => {
|
|
||||||
self.lookup_method_in_trait(span,
|
|
||||||
base_expr,
|
|
||||||
Symbol::intern("deref"),
|
|
||||||
trait_did,
|
|
||||||
base_ty,
|
|
||||||
None)
|
|
||||||
}
|
|
||||||
(method, _) => method,
|
|
||||||
};
|
|
||||||
|
|
||||||
method
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,11 +165,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match self.lookup_method_in_trait_adjusted(call_expr.span,
|
match self.lookup_method_in_trait_adjusted(call_expr.span,
|
||||||
Some(&callee_expr),
|
Some(super::AdjustedRcvr {
|
||||||
|
rcvr_expr: callee_expr,
|
||||||
|
autoderefs,
|
||||||
|
unsize: false
|
||||||
|
}),
|
||||||
method_name,
|
method_name,
|
||||||
trait_def_id,
|
trait_def_id,
|
||||||
autoderefs,
|
|
||||||
false,
|
|
||||||
adjusted_ty,
|
adjusted_ty,
|
||||||
None) {
|
None) {
|
||||||
None => continue,
|
None => continue,
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
use super::probe;
|
use super::probe;
|
||||||
|
|
||||||
use check::{FnCtxt, callee};
|
use check::{FnCtxt, LvalueOp, callee};
|
||||||
use hir::def_id::DefId;
|
use hir::def_id::DefId;
|
||||||
use rustc::ty::subst::Substs;
|
use rustc::ty::subst::Substs;
|
||||||
use rustc::traits;
|
use rustc::traits;
|
||||||
@ -433,11 +433,25 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
for (i, &expr) in exprs.iter().rev().enumerate() {
|
for (i, &expr) in exprs.iter().rev().enumerate() {
|
||||||
debug!("convert_lvalue_derefs_to_mutable: i={} expr={:?}", i, expr);
|
debug!("convert_lvalue_derefs_to_mutable: i={} expr={:?}", i, expr);
|
||||||
|
|
||||||
// Count autoderefs. We don't need to fix up the autoref - the parent
|
// Fix up the adjustment.
|
||||||
// expression will fix them up for us.
|
let autoderefs = match self.tables.borrow_mut().adjustments.get_mut(&expr.id) {
|
||||||
let adjustment = self.tables.borrow().adjustments.get(&expr.id).cloned();
|
Some(&mut Adjustment {
|
||||||
match adjustment {
|
kind: Adjust::DerefRef { autoderefs, ref mut autoref, .. }, ref mut target
|
||||||
Some(Adjustment { kind: Adjust::DerefRef { autoderefs, .. }, .. }) => {
|
}) => {
|
||||||
|
if let &mut Some(AutoBorrow::Ref(_, ref mut mutbl)) = autoref {
|
||||||
|
*mutbl = hir::Mutability::MutMutable;
|
||||||
|
*target = match target.sty {
|
||||||
|
ty::TyRef(r, ty::TypeAndMut { ty, .. }) =>
|
||||||
|
self.tcx.mk_ref(r, ty::TypeAndMut { ty, mutbl: *mutbl }),
|
||||||
|
_ => span_bug!(expr.span, "AutoBorrow::Ref resulted in non-ref {:?}",
|
||||||
|
target)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
autoderefs
|
||||||
|
}
|
||||||
|
Some(_) | None => 0
|
||||||
|
};
|
||||||
|
|
||||||
if autoderefs > 0 {
|
if autoderefs > 0 {
|
||||||
let mut autoderef = self.autoderef(expr.span, self.node_ty(expr.id));
|
let mut autoderef = self.autoderef(expr.span, self.node_ty(expr.id));
|
||||||
autoderef.nth(autoderefs).unwrap_or_else(|| {
|
autoderef.nth(autoderefs).unwrap_or_else(|| {
|
||||||
@ -447,123 +461,53 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
});
|
});
|
||||||
autoderef.finalize(PreferMutLvalue, expr);
|
autoderef.finalize(PreferMutLvalue, expr);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Some(_) | None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't retry the first one or we might infinite loop!
|
|
||||||
if i == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
match expr.node {
|
match expr.node {
|
||||||
hir::ExprIndex(ref base_expr, ref index_expr) => {
|
hir::ExprIndex(ref base_expr, ref index_expr) => {
|
||||||
// If this is an overloaded index, the
|
|
||||||
// adjustment will include an extra layer of
|
|
||||||
// autoref because the method is an &self/&mut
|
|
||||||
// self method. We have to peel it off to get
|
|
||||||
// the raw adjustment that `try_index_step`
|
|
||||||
// expects. This is annoying and horrible. We
|
|
||||||
// ought to recode this routine so it doesn't
|
|
||||||
// (ab)use the normal type checking paths.
|
|
||||||
let adj = self.tables.borrow_mut().adjustments.remove(&base_expr.id);
|
|
||||||
let (autoderefs, unsize, adjusted_base_ty) = match adj {
|
|
||||||
Some(Adjustment {
|
|
||||||
kind: Adjust::DerefRef { autoderefs, autoref, unsize },
|
|
||||||
target
|
|
||||||
}) => {
|
|
||||||
match autoref {
|
|
||||||
None => {
|
|
||||||
assert!(!unsize);
|
|
||||||
}
|
|
||||||
Some(AutoBorrow::Ref(..)) => {}
|
|
||||||
Some(_) => {
|
|
||||||
span_bug!(base_expr.span,
|
|
||||||
"unexpected adjustment autoref {:?}",
|
|
||||||
adj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(autoderefs, unsize, if unsize {
|
|
||||||
target.builtin_deref(false, NoPreference)
|
|
||||||
.expect("fixup: AutoBorrow::Ref is not &T")
|
|
||||||
.ty
|
|
||||||
} else {
|
|
||||||
let ty = self.node_ty(base_expr.id);
|
|
||||||
let mut ty = self.shallow_resolve(ty);
|
|
||||||
let mut method_type = |method_call: ty::MethodCall| {
|
|
||||||
self.tables.borrow().method_map.get(&method_call).map(|m| {
|
|
||||||
self.resolve_type_vars_if_possible(&m.ty)
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
if !ty.references_error() {
|
|
||||||
for i in 0..autoderefs {
|
|
||||||
ty = ty.adjust_for_autoderef(self.tcx,
|
|
||||||
base_expr.id,
|
|
||||||
base_expr.span,
|
|
||||||
i as u32,
|
|
||||||
&mut method_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ty
|
|
||||||
})
|
|
||||||
}
|
|
||||||
None => (0, false, self.node_ty(base_expr.id)),
|
|
||||||
Some(_) => {
|
|
||||||
span_bug!(base_expr.span, "unexpected adjustment type");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let index_expr_ty = self.node_ty(index_expr.id);
|
let index_expr_ty = self.node_ty(index_expr.id);
|
||||||
let adjusted_base_ty = self.resolve_type_vars_if_possible(&adjusted_base_ty);
|
self.convert_lvalue_op_to_mutable(
|
||||||
let index_expr_ty = self.resolve_type_vars_if_possible(&index_expr_ty);
|
LvalueOp::Index, expr, base_expr, &[index_expr_ty]);
|
||||||
|
|
||||||
let result = self.try_index_step(ty::MethodCall::expr(expr.id),
|
|
||||||
expr,
|
|
||||||
&base_expr,
|
|
||||||
adjusted_base_ty,
|
|
||||||
autoderefs,
|
|
||||||
unsize,
|
|
||||||
PreferMutLvalue,
|
|
||||||
index_expr_ty);
|
|
||||||
|
|
||||||
if let Some((input_ty, return_ty)) = result {
|
|
||||||
self.demand_suptype(index_expr.span, input_ty, index_expr_ty);
|
|
||||||
|
|
||||||
let expr_ty = self.node_ty(expr.id);
|
|
||||||
self.demand_suptype(expr.span, expr_ty, return_ty);
|
|
||||||
} else {
|
|
||||||
// We could not perform a mutable index. Re-apply the
|
|
||||||
// immutable index adjustments - borrowck will detect
|
|
||||||
// this as an error.
|
|
||||||
if let Some(adjustment) = adjustment {
|
|
||||||
self.apply_adjustment(expr.id, adjustment);
|
|
||||||
}
|
|
||||||
self.tcx.sess.delay_span_bug(
|
|
||||||
expr.span, "convert_lvalue_derefs_to_mutable failed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
hir::ExprUnary(hir::UnDeref, ref base_expr) => {
|
hir::ExprUnary(hir::UnDeref, ref base_expr) => {
|
||||||
// if this is an overloaded deref, then re-evaluate with
|
self.convert_lvalue_op_to_mutable(
|
||||||
// a preference for mut
|
LvalueOp::Deref, expr, base_expr, &[]);
|
||||||
let method_call = ty::MethodCall::expr(expr.id);
|
|
||||||
if self.tables.borrow().method_map.contains_key(&method_call) {
|
|
||||||
self.tables.borrow_mut().adjustments.remove(&base_expr.id);
|
|
||||||
let method = self.try_overloaded_deref(expr.span,
|
|
||||||
Some(&base_expr),
|
|
||||||
self.node_ty(base_expr.id),
|
|
||||||
PreferMutLvalue);
|
|
||||||
let ok = method.expect("re-trying deref failed");
|
|
||||||
let method = self.register_infer_ok_obligations(ok);
|
|
||||||
self.tables.borrow_mut().method_map.insert(method_call, method);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn convert_lvalue_op_to_mutable(&self,
|
||||||
|
op: LvalueOp,
|
||||||
|
expr: &hir::Expr,
|
||||||
|
base_expr: &hir::Expr,
|
||||||
|
arg_tys: &[Ty<'tcx>])
|
||||||
|
{
|
||||||
|
debug!("convert_lvalue_op_to_mutable({:?}, {:?}, {:?}, {:?})",
|
||||||
|
op, expr, base_expr, arg_tys);
|
||||||
|
let method_call = ty::MethodCall::expr(expr.id);
|
||||||
|
if !self.tables.borrow().method_map.contains_key(&method_call) {
|
||||||
|
debug!("convert_lvalue_op_to_mutable - builtin, nothing to do");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let base_ty = self.tables.borrow().adjustments.get(&base_expr.id)
|
||||||
|
.map_or_else(|| self.node_ty(expr.id), |adj| adj.target);
|
||||||
|
let base_ty = self.resolve_type_vars_if_possible(&base_ty);
|
||||||
|
|
||||||
|
// Need to deref because overloaded lvalue ops take self by-reference.
|
||||||
|
let base_ty = base_ty.builtin_deref(false, NoPreference)
|
||||||
|
.expect("lvalue op takes something that is not a ref")
|
||||||
|
.ty;
|
||||||
|
|
||||||
|
let method = self.try_overloaded_lvalue_op(
|
||||||
|
expr.span, None, base_ty, arg_tys, PreferMutLvalue, op);
|
||||||
|
let ok = method.expect("re-trying op failed");
|
||||||
|
let method = self.register_infer_ok_obligations(ok);
|
||||||
|
debug!("convert_lvalue_op_to_mutable: method={:?}", method);
|
||||||
|
self.tables.borrow_mut().method_map.insert(method_call, method);
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// MISCELLANY
|
// MISCELLANY
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
//! Method lookup: the secret sauce of Rust. See `README.md`.
|
//! Method lookup: the secret sauce of Rust. See `README.md`.
|
||||||
|
|
||||||
use check::FnCtxt;
|
use check::{FnCtxt, AdjustedRcvr};
|
||||||
use hir::def::Def;
|
use hir::def::Def;
|
||||||
use hir::def_id::DefId;
|
use hir::def_id::DefId;
|
||||||
use rustc::ty::subst::Substs;
|
use rustc::ty::subst::Substs;
|
||||||
@ -153,24 +153,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
supplied_method_types))
|
supplied_method_types))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup_method_in_trait(&self,
|
|
||||||
span: Span,
|
|
||||||
self_expr: Option<&hir::Expr>,
|
|
||||||
m_name: ast::Name,
|
|
||||||
trait_def_id: DefId,
|
|
||||||
self_ty: ty::Ty<'tcx>,
|
|
||||||
opt_input_types: Option<Vec<ty::Ty<'tcx>>>)
|
|
||||||
-> Option<InferOk<'tcx, ty::MethodCallee<'tcx>>> {
|
|
||||||
self.lookup_method_in_trait_adjusted(span,
|
|
||||||
self_expr,
|
|
||||||
m_name,
|
|
||||||
trait_def_id,
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
self_ty,
|
|
||||||
opt_input_types)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `lookup_in_trait_adjusted` is used for overloaded operators.
|
/// `lookup_in_trait_adjusted` is used for overloaded operators.
|
||||||
/// It does a very narrow slice of what the normal probe/confirm path does.
|
/// 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
|
/// In particular, it doesn't really do any probing: it simply constructs
|
||||||
@ -184,18 +166,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
/// this method is basically the same as confirmation.
|
/// this method is basically the same as confirmation.
|
||||||
pub fn lookup_method_in_trait_adjusted(&self,
|
pub fn lookup_method_in_trait_adjusted(&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
self_expr: Option<&hir::Expr>,
|
self_info: Option<AdjustedRcvr>,
|
||||||
m_name: ast::Name,
|
m_name: ast::Name,
|
||||||
trait_def_id: DefId,
|
trait_def_id: DefId,
|
||||||
autoderefs: usize,
|
|
||||||
unsize: bool,
|
|
||||||
self_ty: ty::Ty<'tcx>,
|
self_ty: ty::Ty<'tcx>,
|
||||||
opt_input_types: Option<Vec<ty::Ty<'tcx>>>)
|
opt_input_types: Option<Vec<ty::Ty<'tcx>>>)
|
||||||
-> Option<InferOk<'tcx, ty::MethodCallee<'tcx>>> {
|
-> Option<InferOk<'tcx, ty::MethodCallee<'tcx>>> {
|
||||||
debug!("lookup_in_trait_adjusted(self_ty={:?}, self_expr={:?}, \
|
debug!("lookup_in_trait_adjusted(self_ty={:?}, self_info={:?}, \
|
||||||
m_name={}, trait_def_id={:?})",
|
m_name={}, trait_def_id={:?})",
|
||||||
self_ty,
|
self_ty,
|
||||||
self_expr,
|
self_info,
|
||||||
m_name,
|
m_name,
|
||||||
trait_def_id);
|
trait_def_id);
|
||||||
|
|
||||||
@ -288,10 +268,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
obligations.push(traits::Obligation::new(cause, ty::Predicate::WellFormed(method_ty)));
|
obligations.push(traits::Obligation::new(cause, ty::Predicate::WellFormed(method_ty)));
|
||||||
|
|
||||||
// Insert any adjustments needed (always an autoref of some mutability).
|
// Insert any adjustments needed (always an autoref of some mutability).
|
||||||
if let Some(self_expr) = self_expr {
|
if let Some(AdjustedRcvr { rcvr_expr, autoderefs, unsize }) = self_info {
|
||||||
debug!("lookup_in_trait_adjusted: inserting adjustment if needed \
|
debug!("lookup_in_trait_adjusted: inserting adjustment if needed \
|
||||||
(self-id={}, autoderefs={}, unsize={}, fty={:?})",
|
(self-id={}, autoderefs={}, unsize={}, fty={:?})",
|
||||||
self_expr.id, autoderefs, unsize, original_method_ty);
|
rcvr_expr.id, autoderefs, unsize, original_method_ty);
|
||||||
|
|
||||||
let original_sig = original_method_ty.fn_sig();
|
let original_sig = original_method_ty.fn_sig();
|
||||||
let autoref = match (&original_sig.input(0).skip_binder().sty,
|
let autoref = match (&original_sig.input(0).skip_binder().sty,
|
||||||
@ -308,7 +288,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.apply_adjustment(self_expr.id, Adjustment {
|
self.apply_adjustment(rcvr_expr.id, Adjustment {
|
||||||
kind: Adjust::DerefRef {
|
kind: Adjust::DerefRef {
|
||||||
autoderefs: autoderefs,
|
autoderefs: autoderefs,
|
||||||
autoref: autoref,
|
autoref: autoref,
|
||||||
|
@ -361,6 +361,19 @@ impl UnsafetyState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum LvalueOp {
|
||||||
|
Deref,
|
||||||
|
Index
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct AdjustedRcvr<'a> {
|
||||||
|
pub rcvr_expr: &'a hir::Expr,
|
||||||
|
pub autoderefs: usize,
|
||||||
|
pub unsize: bool
|
||||||
|
}
|
||||||
|
|
||||||
/// Tracks whether executing a node may exit normally (versus
|
/// Tracks whether executing a node may exit normally (versus
|
||||||
/// return/break/panic, which "diverge", leaving dead code in their
|
/// return/break/panic, which "diverge", leaving dead code in their
|
||||||
/// wake). Tracked semi-automatically (through type variables marked
|
/// wake). Tracked semi-automatically (through type variables marked
|
||||||
@ -2156,9 +2169,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
|
|
||||||
while let Some((adj_ty, autoderefs)) = autoderef.next() {
|
while let Some((adj_ty, autoderefs)) = autoderef.next() {
|
||||||
if let Some(final_mt) = self.try_index_step(
|
if let Some(final_mt) = self.try_index_step(
|
||||||
MethodCall::expr(expr.id),
|
MethodCall::expr(expr.id), expr, Some(AdjustedRcvr {
|
||||||
expr, base_expr, adj_ty, autoderefs,
|
rcvr_expr: base_expr,
|
||||||
false, lvalue_pref, idx_ty)
|
autoderefs,
|
||||||
|
unsize: false
|
||||||
|
}), base_expr.span, adj_ty, lvalue_pref, idx_ty)
|
||||||
{
|
{
|
||||||
autoderef.finalize(lvalue_pref, base_expr);
|
autoderef.finalize(lvalue_pref, base_expr);
|
||||||
return Some(final_mt);
|
return Some(final_mt);
|
||||||
@ -2166,10 +2181,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
|
|
||||||
if let ty::TyArray(element_ty, _) = adj_ty.sty {
|
if let ty::TyArray(element_ty, _) = adj_ty.sty {
|
||||||
autoderef.finalize(lvalue_pref, base_expr);
|
autoderef.finalize(lvalue_pref, base_expr);
|
||||||
let adjusted_ty = self.tcx.mk_slice(element_ty);
|
let adj_ty = self.tcx.mk_slice(element_ty);
|
||||||
return self.try_index_step(
|
return self.try_index_step(
|
||||||
MethodCall::expr(expr.id), expr, base_expr,
|
MethodCall::expr(expr.id), expr, Some(AdjustedRcvr {
|
||||||
adjusted_ty, autoderefs, true, lvalue_pref, idx_ty);
|
rcvr_expr: base_expr,
|
||||||
|
autoderefs,
|
||||||
|
unsize: true
|
||||||
|
}), base_expr.span, adj_ty, lvalue_pref, idx_ty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
autoderef.unambiguous_final_ty();
|
autoderef.unambiguous_final_ty();
|
||||||
@ -2184,71 +2202,44 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
fn try_index_step(&self,
|
fn try_index_step(&self,
|
||||||
method_call: MethodCall,
|
method_call: MethodCall,
|
||||||
expr: &hir::Expr,
|
expr: &hir::Expr,
|
||||||
base_expr: &'gcx hir::Expr,
|
base_expr: Option<AdjustedRcvr>,
|
||||||
|
base_span: Span,
|
||||||
adjusted_ty: Ty<'tcx>,
|
adjusted_ty: Ty<'tcx>,
|
||||||
autoderefs: usize,
|
|
||||||
unsize: bool,
|
|
||||||
lvalue_pref: LvaluePreference,
|
lvalue_pref: LvaluePreference,
|
||||||
index_ty: Ty<'tcx>)
|
index_ty: Ty<'tcx>)
|
||||||
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
|
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
|
||||||
{
|
{
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
debug!("try_index_step(expr={:?}, base_expr.id={:?}, adjusted_ty={:?}, \
|
debug!("try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
|
||||||
autoderefs={}, unsize={}, index_ty={:?})",
|
index_ty={:?})",
|
||||||
expr,
|
expr,
|
||||||
base_expr,
|
base_expr,
|
||||||
adjusted_ty,
|
adjusted_ty,
|
||||||
autoderefs,
|
|
||||||
unsize,
|
|
||||||
index_ty);
|
index_ty);
|
||||||
|
|
||||||
let input_ty = self.next_ty_var(TypeVariableOrigin::AutoDeref(base_expr.span));
|
let input_ty = self.next_ty_var(TypeVariableOrigin::AutoDeref(base_span));
|
||||||
|
|
||||||
// First, try built-in indexing.
|
// First, try built-in indexing.
|
||||||
match (adjusted_ty.builtin_index(), &index_ty.sty) {
|
match (adjusted_ty.builtin_index(), &index_ty.sty) {
|
||||||
(Some(ty), &ty::TyUint(ast::UintTy::Us)) | (Some(ty), &ty::TyInfer(ty::IntVar(_))) => {
|
(Some(ty), &ty::TyUint(ast::UintTy::Us)) | (Some(ty), &ty::TyInfer(ty::IntVar(_))) => {
|
||||||
debug!("try_index_step: success, using built-in indexing");
|
debug!("try_index_step: success, using built-in indexing");
|
||||||
// If we had `[T; N]`, we should've caught it before unsizing to `[T]`.
|
// If we had `[T; N]`, we should've caught it before unsizing to `[T]`.
|
||||||
assert!(!unsize);
|
if let Some(base_expr) = base_expr {
|
||||||
self.apply_autoderef_adjustment(base_expr.id, autoderefs, adjusted_ty);
|
assert!(!base_expr.unsize);
|
||||||
|
self.apply_autoderef_adjustment(
|
||||||
|
base_expr.rcvr_expr.id, base_expr.autoderefs, adjusted_ty);
|
||||||
|
}
|
||||||
return Some((tcx.types.usize, ty));
|
return Some((tcx.types.usize, ty));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try `IndexMut` first, if preferred.
|
|
||||||
let method = match (lvalue_pref, tcx.lang_items.index_mut_trait()) {
|
|
||||||
(PreferMutLvalue, Some(trait_did)) => {
|
|
||||||
self.lookup_method_in_trait_adjusted(expr.span,
|
|
||||||
Some(&base_expr),
|
|
||||||
Symbol::intern("index_mut"),
|
|
||||||
trait_did,
|
|
||||||
autoderefs,
|
|
||||||
unsize,
|
|
||||||
adjusted_ty,
|
|
||||||
Some(vec![input_ty]))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Otherwise, fall back to `Index`.
|
|
||||||
let method = match (method, tcx.lang_items.index_trait()) {
|
|
||||||
(None, Some(trait_did)) => {
|
|
||||||
self.lookup_method_in_trait_adjusted(expr.span,
|
|
||||||
Some(&base_expr),
|
|
||||||
Symbol::intern("index"),
|
|
||||||
trait_did,
|
|
||||||
autoderefs,
|
|
||||||
unsize,
|
|
||||||
adjusted_ty,
|
|
||||||
Some(vec![input_ty]))
|
|
||||||
}
|
|
||||||
(method, _) => method,
|
|
||||||
};
|
|
||||||
|
|
||||||
// If some lookup succeeds, write callee into table and extract index/element
|
// If some lookup succeeds, write callee into table and extract index/element
|
||||||
// type from the method signature.
|
// type from the method signature.
|
||||||
// If some lookup succeeded, install method in table
|
// If some lookup succeeded, install method in table
|
||||||
|
let method = self.try_overloaded_lvalue_op(
|
||||||
|
expr.span, base_expr, adjusted_ty, &[input_ty], lvalue_pref, LvalueOp::Index);
|
||||||
|
|
||||||
method.map(|ok| {
|
method.map(|ok| {
|
||||||
debug!("try_index_step: success, using overloaded indexing");
|
debug!("try_index_step: success, using overloaded indexing");
|
||||||
let method = self.register_infer_ok_obligations(ok);
|
let method = self.register_infer_ok_obligations(ok);
|
||||||
@ -2257,6 +2248,67 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_lvalue_op(&self, op: LvalueOp, is_mut: bool) -> (Option<DefId>, Symbol) {
|
||||||
|
let (tr, name) = match (op, is_mut) {
|
||||||
|
(LvalueOp::Deref, false) =>
|
||||||
|
(self.tcx.lang_items.deref_trait(), "deref"),
|
||||||
|
(LvalueOp::Deref, true) =>
|
||||||
|
(self.tcx.lang_items.deref_mut_trait(), "deref_mut"),
|
||||||
|
(LvalueOp::Index, false) =>
|
||||||
|
(self.tcx.lang_items.index_trait(), "index"),
|
||||||
|
(LvalueOp::Index, true) =>
|
||||||
|
(self.tcx.lang_items.index_mut_trait(), "index_mut"),
|
||||||
|
};
|
||||||
|
(tr, Symbol::intern(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_overloaded_lvalue_op(&self,
|
||||||
|
span: Span,
|
||||||
|
base_expr: Option<AdjustedRcvr>,
|
||||||
|
base_ty: Ty<'tcx>,
|
||||||
|
arg_tys: &[Ty<'tcx>],
|
||||||
|
lvalue_pref: LvaluePreference,
|
||||||
|
op: LvalueOp)
|
||||||
|
-> Option<InferOk<'tcx, MethodCallee<'tcx>>>
|
||||||
|
{
|
||||||
|
debug!("try_overloaded_lvalue_op({:?},{:?},{:?},{:?},{:?})",
|
||||||
|
span,
|
||||||
|
base_expr,
|
||||||
|
base_ty,
|
||||||
|
lvalue_pref,
|
||||||
|
op);
|
||||||
|
|
||||||
|
// Try Mut first, if preferred.
|
||||||
|
let (mut_tr, mut_op) = self.resolve_lvalue_op(op, true);
|
||||||
|
let method = match (lvalue_pref, mut_tr) {
|
||||||
|
(PreferMutLvalue, Some(trait_did)) => {
|
||||||
|
self.lookup_method_in_trait_adjusted(span,
|
||||||
|
base_expr,
|
||||||
|
mut_op,
|
||||||
|
trait_did,
|
||||||
|
base_ty,
|
||||||
|
Some(arg_tys.to_owned()))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Otherwise, fall back to the immutable version.
|
||||||
|
let (imm_tr, imm_op) = self.resolve_lvalue_op(op, false);
|
||||||
|
let method = match (method, imm_tr) {
|
||||||
|
(None, Some(trait_did)) => {
|
||||||
|
self.lookup_method_in_trait_adjusted(span,
|
||||||
|
base_expr,
|
||||||
|
imm_op,
|
||||||
|
trait_did,
|
||||||
|
base_ty,
|
||||||
|
Some(arg_tys.to_owned()))
|
||||||
|
}
|
||||||
|
(method, _) => method,
|
||||||
|
};
|
||||||
|
|
||||||
|
method
|
||||||
|
}
|
||||||
|
|
||||||
fn check_method_argument_types(&self,
|
fn check_method_argument_types(&self,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
method_fn_ty: Ty<'tcx>,
|
method_fn_ty: Ty<'tcx>,
|
||||||
|
@ -398,8 +398,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
|
|
||||||
let method = match trait_did {
|
let method = match trait_did {
|
||||||
Some(trait_did) => {
|
Some(trait_did) => {
|
||||||
self.lookup_method_in_trait(expr.span,
|
let lhs_expr = Some(super::AdjustedRcvr {
|
||||||
Some(lhs_expr),
|
rcvr_expr: lhs_expr, autoderefs: 0, unsize: false
|
||||||
|
});
|
||||||
|
self.lookup_method_in_trait_adjusted(expr.span,
|
||||||
|
lhs_expr,
|
||||||
opname,
|
opname,
|
||||||
trait_did,
|
trait_did,
|
||||||
lhs_ty,
|
lhs_ty,
|
||||||
|
20
src/test/run-pass/issue-41604.rs
Normal file
20
src/test/run-pass/issue-41604.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
struct B;
|
||||||
|
|
||||||
|
impl B {
|
||||||
|
fn init(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut b = [B];
|
||||||
|
b[1-1].init();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user