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

Beta backport various arielb1 patches #42053

Merged
merged 2 commits into from
May 23, 2017
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
41 changes: 7 additions & 34 deletions src/librustc_typeck/check/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@

use astconv::AstConv;

use super::FnCtxt;
use super::{FnCtxt, LvalueOp};

use check::coercion::AsCoercionSite;
use rustc::infer::InferOk;
use rustc::traits;
use rustc::ty::{self, Ty, TraitRef};
use rustc::ty::{ToPredicate, TypeFoldable};
use rustc::ty::{MethodCall, MethodCallee};
use rustc::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
use rustc::ty::{LvaluePreference, NoPreference};
use rustc::hir;

use syntax_pos::Span;
Expand Down Expand Up @@ -213,39 +213,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
span: Span,
base_expr: Option<&hir::Expr>,
base_ty: Ty<'tcx>,
lvalue_pref: LvaluePreference)
pref: LvaluePreference)
-> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
debug!("try_overloaded_deref({:?},{:?},{:?},{:?})",
span,
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.
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,
};
let rcvr = base_expr.map(|base_expr| super::AdjustedRcvr {
rcvr_expr: base_expr, autoderefs: 0, unsize: false
});

method
self.try_overloaded_lvalue_op(span, rcvr, base_ty, &[], pref, LvalueOp::Deref)
}
}
8 changes: 5 additions & 3 deletions src/librustc_typeck/check/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
};

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,
trait_def_id,
autoderefs,
false,
adjusted_ty,
None) {
None => continue,
Expand Down
196 changes: 77 additions & 119 deletions src/librustc_typeck/check/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

use super::probe;

use check::{FnCtxt, callee};
use check::{FnCtxt, LvalueOp, callee};
use hir::def_id::DefId;
use rustc::ty::subst::Substs;
use rustc::traits;
Expand Down Expand Up @@ -433,137 +433,95 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
for (i, &expr) in exprs.iter().rev().enumerate() {
debug!("convert_lvalue_derefs_to_mutable: i={} expr={:?}", i, expr);

// Count autoderefs. We don't need to fix up the autoref - the parent
// expression will fix them up for us.
let adjustment = self.tables.borrow().adjustments.get(&expr.id).cloned();
match adjustment {
Some(Adjustment { kind: Adjust::DerefRef { autoderefs, .. }, .. }) => {
if autoderefs > 0 {
let mut autoderef = self.autoderef(expr.span, self.node_ty(expr.id));
autoderef.nth(autoderefs).unwrap_or_else(|| {
span_bug!(expr.span,
"expr was deref-able {} times but now isn't?",
autoderefs);
});
autoderef.finalize(PreferMutLvalue, expr);
}
}
Some(_) | None => {}
// Fix up the autoderefs. Autorefs can only occur immediately preceding
// overloaded lvalue ops, and will be fixed by them in order to get
// the correct region.
let autoderefs = match self.tables.borrow().adjustments.get(&expr.id) {
Some(&Adjustment { kind: Adjust::DerefRef { autoderefs, .. }, .. }) => autoderefs,
Some(_) | None => 0
};

if autoderefs > 0 {
let mut autoderef = self.autoderef(expr.span, self.node_ty(expr.id));
autoderef.nth(autoderefs).unwrap_or_else(|| {
span_bug!(expr.span,
"expr was deref-able {} times but now isn't?",
autoderefs);
});
autoderef.finalize(PreferMutLvalue, expr);
}

// Don't retry the first one or we might infinite loop!
if i == 0 {
continue;
}
match expr.node {
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 adjusted_base_ty = self.resolve_type_vars_if_possible(&adjusted_base_ty);
let index_expr_ty = self.resolve_type_vars_if_possible(&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");
}
self.convert_lvalue_op_to_mutable(
LvalueOp::Index, expr, base_expr, &[index_expr_ty]);
}
hir::ExprUnary(hir::UnDeref, ref base_expr) => {
// if this is an overloaded deref, then re-evaluate with
// a preference for mut
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);
}
self.convert_lvalue_op_to_mutable(
LvalueOp::Deref, expr, base_expr, &[]);
}
_ => {}
}
}
}

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 = match method {
Some(method) => method,
None => return self.tcx.sess.delay_span_bug(expr.span, "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);

// Convert the autoref in the base expr to mutable with the correct
// region and mutability.
if let Some(&mut Adjustment {
ref mut target, kind: Adjust::DerefRef {
autoref: Some(AutoBorrow::Ref(ref mut r, ref mut mutbl)), ..
}
}) = self.tables.borrow_mut().adjustments.get_mut(&base_expr.id) {
debug!("convert_lvalue_op_to_mutable: converting autoref of {:?}", target);

// extract method return type, which will be &mut T;
// all LB regions should have been instantiated during method lookup
let method_sig = self.tcx.no_late_bound_regions(&method.ty.fn_sig()).unwrap();

*target = method_sig.inputs()[0];
if let ty::TyRef(r_, mt) = target.sty {
*r = r_;
*mutbl = mt.mutbl;
} else {
span_bug!(expr.span, "input to lvalue op is not a ref?");
}
}
}

///////////////////////////////////////////////////////////////////////////
// MISCELLANY

Expand Down
34 changes: 7 additions & 27 deletions src/librustc_typeck/check/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

//! Method lookup: the secret sauce of Rust. See `README.md`.

use check::FnCtxt;
use check::{FnCtxt, AdjustedRcvr};
use hir::def::Def;
use hir::def_id::DefId;
use rustc::ty::subst::Substs;
Expand Down Expand Up @@ -153,24 +153,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
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.
/// 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 @@ -184,18 +166,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
/// this method is basically the same as confirmation.
pub fn lookup_method_in_trait_adjusted(&self,
span: Span,
self_expr: Option<&hir::Expr>,
self_info: Option<AdjustedRcvr>,
m_name: ast::Name,
trait_def_id: DefId,
autoderefs: usize,
unsize: bool,
self_ty: ty::Ty<'tcx>,
opt_input_types: Option<Vec<ty::Ty<'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={:?})",
self_ty,
self_expr,
self_info,
m_name,
trait_def_id);

Expand Down Expand Up @@ -288,10 +268,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
obligations.push(traits::Obligation::new(cause, ty::Predicate::WellFormed(method_ty)));

// 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 \
(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 autoref = match (&original_sig.input(0).skip_binder().sty,
Expand All @@ -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 {
autoderefs: autoderefs,
autoref: autoref,
Expand Down
Loading